Rechercher

PythonReactSEOPDF
GitHub Skill

resend

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.

nathanielcrowell12-spec☆ 0 Étoiles du dépôt0 Forks du dépôt

Installation

Suivez les instructions du dépôt pour votre agent. Consultez les fichiers et dépendances avant l’installation.

Voir la source ↗Guides

Instructions

⚠️ 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
Template Trigger Recipient
gallery-ready Photographer marks ready Client
payment-success Checkout completed Client
payment-failed Invoice failed Client
invitation Photographer invites client Client
commission-earned Client pays Photographer
Branding
Element Value
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

Exemples

Consultez les exemples du dépôt source.

Voir la source ↗

Historique source

Date d’indexation
2026-01-20
Mise à jour de l’index
2026-01-26
Branch
main

Les dates concernent ce répertoire. Consultez le dépôt pour l’historique du code.

GitHub ↗

chat Comments (0)

chat_bubble_outline

No comments yet. Be the first to share your thoughts!