Back to list
nathanielcrowell12-spec

resend

by nathanielcrowell12-spec

A collection point for various photography website

0🍴 0📅 Jan 21, 2026

SKILL.md


name: resend description: > Implement email notifications for PhotoVault using Resend and React Email. Use when working with email templates, transactional emails, notification triggers, deliverability issues, or styling email content. Includes PhotoVault branding and template patterns.

⚠️ MANDATORY WORKFLOW - DO NOT SKIP

When this skill activates, you MUST follow the expert workflow before writing any code:

  1. Spawn Domain Expert using the Task tool with this prompt:

    Read the expert prompt at: C:\Users\natha\Stone-Fence-Brain\VENTURES\PhotoVault\claude\experts\resend-expert.md
    
    Then research the codebase and write an implementation plan to: docs/claude/plans/email-[task-name]-plan.md
    
    Task: [describe the user's request]
    
  2. Spawn QA Critic after expert returns, using Task tool:

    Read the QA critic prompt at: C:\Users\natha\Stone-Fence-Brain\VENTURES\PhotoVault\claude\experts\qa-critic-expert.md
    
    Review the plan at: docs/claude/plans/email-[task-name]-plan.md
    Write critique to: docs/claude/plans/email-[task-name]-critique.md
    
  3. Present BOTH plan and critique to user - wait for approval before implementing

DO NOT read files and start coding. DO NOT rationalize that "this is simple." Follow the workflow.


Resend Email Integration

Core Principles

Email HTML is NOT Web HTML

Email clients strip <style> tags, ignore CSS classes, and render tables differently. Everything must be inline.

// ❌ BAD: CSS classes don't work
<div className="button">Click me</div>

// ✅ GOOD: Inline styles
<a href={url} style={{
  backgroundColor: '#f59e0b',
  color: '#000000',
  padding: '12px 24px',
  borderRadius: '8px',
  textDecoration: 'none',
  display: 'inline-block',
}}>
  Click me
</a>

Mobile First (60%+ opens)

Most emails are read on phones. Design for 320px width first.

const container = {
  maxWidth: '600px',
  padding: '20px',
  margin: '0 auto',
}

Every Email Needs a Preview

The preview text appears in the inbox next to the subject.

import { Preview } from '@react-email/components'
<Preview>Your gallery "Smith Wedding" is ready with 247 photos</Preview>

Anti-Patterns

Using CSS classes or external stylesheets

// WRONG: Won't render
<style>{`.button { background: blue; }`}</style>
<a className="button">Click</a>

// RIGHT: Inline everything
<a style={{ backgroundColor: 'blue', padding: '12px 24px' }}>Click</a>

Using flexbox or grid

// WRONG: Not supported in most email clients
<div style={{ display: 'flex' }}>

// RIGHT: Use tables for layout
import { Row, Column } from '@react-email/components'
<Row>
  <Column>Left content</Column>
  <Column>Right content</Column>
</Row>

Forgetting alt text on images

// WRONG: Images often blocked
<Img src={url} />

// RIGHT: Always include meaningful alt
<Img src={url} alt="Preview of your wedding photos" />

Generic subject lines

// WRONG: Low open rate
subject: 'Update from PhotoVault'

// RIGHT: Specific and actionable
subject: 'Your "Smith Wedding" gallery is ready - 247 photos inside'

Not handling send failures

// WRONG: Silent failure
await resend.emails.send({ ... })

// RIGHT: Handle errors
const { data, error } = await resend.emails.send({ ... })
if (error) {
  console.error('Email failed:', error)
}

Email Template Pattern

// src/lib/email/templates/gallery-ready.tsx
import {
  Body, Container, Head, Heading, Html,
  Img, Link, Preview, Section, Text,
} from '@react-email/components'

interface GalleryReadyEmailProps {
  clientName: string
  galleryName: string
  photoCount: number
  previewImageUrl: string
  galleryUrl: string
}

export function GalleryReadyEmail({
  clientName, galleryName, photoCount, previewImageUrl, galleryUrl,
}: GalleryReadyEmailProps) {
  return (
    <Html>
      <Head />
      <Preview>Your "{galleryName}" gallery is ready - {photoCount} photos inside</Preview>
      <Body style={main}>
        <Container style={container}>
          <Img src="https://photovault.photo/logo.png" alt="PhotoVault" width={150} />
          <Heading style={heading}>Hi {clientName}!</Heading>
          <Text style={text}>
            Your photos from <strong>{galleryName}</strong> are ready.
            Your photographer has uploaded {photoCount} photos.
          </Text>
          {previewImageUrl && (
            <Img src={previewImageUrl} alt={`Preview from ${galleryName}`} width={560} />
          )}
          <Section style={{ textAlign: 'center', marginTop: '30px' }}>
            <Link href={galleryUrl} style={button}>View Your Photos</Link>
          </Section>
        </Container>
      </Body>
    </Html>
  )
}

const main = {
  backgroundColor: '#0a0a0a',
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
}

const container = { maxWidth: '600px', margin: '0 auto', padding: '40px 20px' }
const heading = { color: '#ffffff', fontSize: '28px', fontWeight: 'bold' }
const text = { color: '#a3a3a3', fontSize: '16px', lineHeight: '26px' }
const button = {
  backgroundColor: '#f59e0b',
  color: '#000000',
  padding: '14px 28px',
  borderRadius: '8px',
  textDecoration: 'none',
  fontWeight: 'bold',
  display: 'inline-block',
}

Email Service

// src/lib/email/email-service.ts
import { Resend } from 'resend'

const resend = new Resend(process.env.RESEND_API_KEY)

interface SendEmailParams {
  to: string | string[]
  subject: string
  react: React.ReactElement
  replyTo?: string
}

export async function sendEmail({ to, subject, react, replyTo }: SendEmailParams) {
  try {
    const { data, error } = await resend.emails.send({
      from: 'PhotoVault <noreply@photovault.photo>',
      to: Array.isArray(to) ? to : [to],
      subject,
      react,
      replyTo: replyTo || 'support@photovault.photo',
    })

    if (error) {
      console.error('[Email] Send failed:', error)
      return { success: false, error }
    }

    console.log('[Email] Sent successfully:', data?.id)
    return { success: true, id: data?.id }
  } catch (error) {
    console.error('[Email] Unexpected error:', error)
    return { success: false, error }
  }
}

PhotoVault Configuration

Templates Needed

TemplateTriggerRecipient
gallery-readyPhotographer marks readyClient
payment-successCheckout completedClient
payment-failedInvoice failedClient
invitationPhotographer invites clientClient
commission-earnedClient paysPhotographer

Branding

ElementValue
Primary color#f59e0b (amber)
Background#0a0a0a (near black)
Text#a3a3a3 (gray)
Headings#ffffff (white)

Environment Variables

RESEND_API_KEY=re_...
FROM_EMAIL=PhotoVault <noreply@photovault.photo>

Testing Emails

# Preview locally
npx react-email dev

Deliverability Checklist

  1. ✅ Domain verified in Resend (DKIM, SPF)
  2. ✅ FROM address uses verified domain
  3. ✅ Subject line is specific, not spammy
  4. ✅ Alt text on all images
  5. ✅ No URL shorteners
  6. ✅ Reply-to address is valid

Score

Total Score

50/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon