NEW Browse AI tools across categories — updated daily. See what's new →
★ Featured Development

Email Template Builder

Build complete transactional email systems: React Email templates, provider integration (Resend, Postmark, SendGrid, AWS SES), preview server, i18n support, dark mode, spam optimization, analytics ...

Version1.0.0
LicenseMIT
Token count~3,829
UpdatedJun 4, 2026

Build complete transactional email systems: React Email templates, provider integration (Resend, Postmark, SendGrid, AWS SES), preview server, i18n support, dark mode, spam optimization, analytics tracking. Use when adding transactional email to a new product, migrating between email providers, refactoring legacy email templates for accessibility, or adding internationalization to existing templates.

Install

Quick install

via npx skills · works with 57+ agents
npx skills add https://github.com/alirezarezvani/claude-skills/tree/main/engineering-team/skills/email-template-builder
Or pick agent:
npx skills add alirezarezvani/claude-skills --skill email-template-builder --agent claude-code
npx skills add alirezarezvani/claude-skills --skill email-template-builder --agent cursor
npx skills add alirezarezvani/claude-skills --skill email-template-builder --agent codex
npx skills add alirezarezvani/claude-skills --skill email-template-builder --agent opencode
npx skills add alirezarezvani/claude-skills --skill email-template-builder --agent github-copilot
npx skills add alirezarezvani/claude-skills --skill email-template-builder --agent windsurf
More install options

Shorthand — useful for multi-skill repos:

npx skills add alirezarezvani/claude-skills --skill email-template-builder

Manual — clone the repo and drop the folder into your agent's skills directory:

git clone https://github.com/alirezarezvani/claude-skills.git
cp -r claude-skills/engineering-team/skills/email-template-builder ~/.claude/skills/
How to use: Once installed, ask your agent to "use the email-template-builder skill" or describe what you want (e.g. "Build complete transactional email systems: React Email templates, provider inte"). Requires Node.js 18+.

Email Template Builder

Tier: POWERFUL
Category: Engineering Team
Domain: Transactional Email / Communications Infrastructure

---

Overview

Build complete transactional email systems: React Email templates, provider integration, preview server, i18n support, dark mode, spam optimization, and analytics tracking. Output production-ready code for Resend, Postmark, SendGrid, or AWS SES.

---

Core Capabilities

  • React Email templates (welcome, verification, password reset, invoice, notification, digest)
  • MJML templates for maximum email client compatibility
  • Multi-provider support with unified sending interface
  • Local preview server with hot reload
  • i18n/localization with typed translation keys
  • Dark mode support using media queries
  • Spam score optimization checklist
  • Open/click tracking with UTM parameters

---

When to Use

  • Setting up transactional email for a new product
  • Migrating from a legacy email system
  • Adding new email types (invoice, digest, notification)
  • Debugging email deliverability issues
  • Implementing i18n for email templates

---

Project Structure

emails/
├── components/
│   ├── layout/
│   │   ├── email-layout.tsx       # Base layout with brand header/footer
│   │   └── email-button.tsx       # CTA button component
│   ├── partials/
│   │   ├── header.tsx
│   │   └── footer.tsx
├── templates/
│   ├── welcome.tsx
│   ├── verify-email.tsx
│   ├── password-reset.tsx
│   ├── invoice.tsx
│   ├── notification.tsx
│   └── weekly-digest.tsx
├── lib/
│   ├── send.ts                    # Unified send function
│   ├── providers/
│   │   ├── resend.ts
│   │   ├── postmark.ts
│   │   └── ses.ts
│   └── tracking.ts                # UTM + analytics
├── i18n/
│   ├── en.ts
│   └── de.ts
└── preview/                       # Dev preview server
    └── server.ts

---

Base Email Layout

// emails/components/layout/email-layout.tsx
import {
  Body, Container, Head, Html, Img, Preview, Section, Text, Hr, Font
} from "@react-email/components"

interface EmailLayoutProps {
  preview: string
  children: React.ReactNode
}

export function EmailLayout({ preview, children }: EmailLayoutProps) {
  return (
    <Html lang="en">
      <Head>
        <Font
          fontFamily="Inter"
          fallbackFontFamily="Arial"
          webFont={{ url: "https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZ9hiJ-Ek-_EeA.woff2", format: "woff2" }}
          fontWeight={400}
          fontStyle="normal"
        />
        {/* Dark mode styles */}
        <style>{`
          @media (prefers-color-scheme: dark) {
            .email-body { background-color: #0f0f0f !important; }
            .email-container { background-color: #1a1a1a !important; }
            .email-text { color: #e5e5e5 !important; }
            .email-heading { color: #ffffff !important; }
            .email-divider { border-color: #333333 !important; }
          }
        `}</style>
      </Head>
      <Preview>{preview}</Preview>
      <Body className="email-body" style={styles.body}>
        <Container className="email-container" style={styles.container}>
          {/* Header */}
          <Section style={styles.header}>
            <Img src="https://yourapp.com/logo.png" width={120} height={40} alt="MyApp" />
          </Section>
          
          {/* Content */}
          <Section style={styles.content}>
            {children}
          </Section>
          
          {/* Footer */}
          <Hr style={styles.divider} />
          <Section style={styles.footer}>
            <Text style={styles.footerText}>
              MyApp Inc. · 123 Main St · San Francisco, CA 94105
            </Text>
            <Text style={styles.footerText}>
              <a href="{{unsubscribe_url}}" style={styles.link}>Unsubscribe</a>
              {" · "}
              <a href="https://yourapp.com/privacy" style={styles.link}>Privacy Policy</a>
            </Text>
          </Section>
        </Container>
      </Body>
    </Html>
  )
}

const styles = {
  body: { backgroundColor: "#f5f5f5", fontFamily: "Inter, Arial, sans-serif" },
  container: { maxWidth: "600px", margin: "0 auto", backgroundColor: "#ffffff", borderRadius: "8px", overflow: "hidden" },
  header: { padding: "24px 32px", borderBottom: "1px solid #e5e5e5" },
  content: { padding: "32px" },
  divider: { borderColor: "#e5e5e5", margin: "0 32px" },
  footer: { padding: "24px 32px" },
  footerText: { fontSize: "12px", color: "#6b7280", textAlign: "center" as const, margin: "4px 0" },
  link: { color: "#6b7280", textDecoration: "underline" },
}

---

Welcome Email

// emails/templates/welcome.tsx
import { Button, Heading, Text } from "@react-email/components"
import { EmailLayout } from "../components/layout/email-layout"

interface WelcomeEmailProps {
  name: "string"
  confirmUrl: string
  trialDays?: number
}

export function WelcomeEmail({ name, confirmUrl, trialDays = 14 }: WelcomeEmailProps) {
  return (
    <EmailLayout preview={`Welcome to MyApp, ${name}! Confirm your email to get started.`}>
      <Heading style={styles.h1}>Welcome to MyApp, {name}!</Heading>
      <Text style={styles.text}>
        We're excited to have you on board. You've got {trialDays} days to explore everything MyApp has to offer — no credit card required.
      </Text>
      <Text style={styles.text}>
        First, confirm your email address to activate your account:
      </Text>
      <Button href={confirmUrl} style={styles.button}>
        Confirm Email Address
      </Button>
      <Text style={styles.hint}>
        Button not working? Copy and paste this link into your browser:
        <br />
        <a href={confirmUrl} style={styles.link}>{confirmUrl}</a>
      </Text>
      <Text style={styles.text}>
        Once confirmed, you can:
      </Text>
      <ul style={styles.list}>
        <li>Connect your first project in 2 minutes</li>
        <li>Invite your team (free for up to 3 members)</li>
        <li>Set up Slack notifications</li>
      </ul>
    </EmailLayout>
  )
}

export default WelcomeEmail

const styles = {
  h1: { fontSize: "28px", fontWeight: "700", color: "#111827", margin: "0 0 16px" },
  text: { fontSize: "16px", lineHeight: "1.6", color: "#374151", margin: "0 0 16px" },
  button: { backgroundColor: "#4f46e5", color: "#ffffff", borderRadius: "6px", fontSize: "16px", fontWeight: "600", padding: "12px 24px", textDecoration: "none", display: "inline-block", margin: "8px 0 24px" },
  hint: { fontSize: "13px", color: "#6b7280" },
  link: { color: "#4f46e5" },
  list: { fontSize: "16px", lineHeight: "1.8", color: "#374151", paddingLeft: "20px" },
}

---

Invoice Email

// emails/templates/invoice.tsx
import { Row, Column, Section, Heading, Text, Hr, Button } from "@react-email/components"
import { EmailLayout } from "../components/layout/email-layout"

interface InvoiceItem { description: string; amount: number }

interface InvoiceEmailProps {
  name: "string"
  invoiceNumber: string
  invoiceDate: string
  dueDate: string
  items: InvoiceItem[]
  total: number
  currency: string
  downloadUrl: string
}

export function InvoiceEmail({ name, invoiceNumber, invoiceDate, dueDate, items, total, currency = "USD", downloadUrl }: InvoiceEmailProps) {
  const formatter = new Intl.NumberFormat("en-US", { style: "currency", currency })

  return (
    <EmailLayout preview={`Invoice ${invoiceNumber} - ${formatter.format(total / 100)}`}>
      <Heading style={styles.h1}>Invoice #{invoiceNumber}</Heading>
      <Text style={styles.text}>Hi {name},</Text>
      <Text style={styles.text}>Here's your invoice from MyApp. Thank you for your continued support.</Text>

      {/* Invoice Meta */}
      <Section style={styles.metaBox}>
        <Row>
          <Column><Text style={styles.metaLabel}>Invoice Date</Text><Text style={styles.metaValue}>{invoiceDate}</Text></Column>
          <Column><Text style={styles.metaLabel}>Due Date</Text><Text style={styles.metaValue}>{dueDate}</Text></Column>
          <Column><Text style={styles.metaLabel}>Amount Due</Text><Text style={styles.metaValueLarge}>{formatter.format(total / 100)}</Text></Column>
        </Row>
      </Section>

      {/* Line Items */}
      <Section style={styles.table}>
        <Row style={styles.tableHeader}>
          <Column><Text style={styles.tableHeaderText}>Description</Text></Column>
          <Column><Text style={{ ...styles.tableHeaderText, textAlign: "right" }}>Amount</Text></Column>
        </Row>
        {items.map((item, i) => (
          <Row key={i} style={i % 2 === 0 ? styles.tableRowEven : styles.tableRowOdd}>
            <Column><Text style={styles.tableCell}>{item.description}</Text></Column>
            <Column><Text style={{ ...styles.tableCell, textAlign: "right" }}>{formatter.format(item.amount / 100)}</Text></Column>
          </Row>
        ))}
        <Hr style={styles.divider} />
        <Row>
          <Column><Text style={styles.totalLabel}>Total</Text></Column>
          <Column><Text style={styles.totalValue}>{formatter.format(total / 100)}</Text></Column>
        </Row>
      </Section>

      <Button href={downloadUrl} style={styles.button}>Download PDF Invoice</Button>
    </EmailLayout>
  )
}

export default InvoiceEmail

const styles = {
  h1: { fontSize: "24px", fontWeight: "700", color: "#111827", margin: "0 0 16px" },
  text: { fontSize: "15px", lineHeight: "1.6", color: "#374151", margin: "0 0 12px" },
  metaBox: { backgroundColor: "#f9fafb", borderRadius: "8px", padding: "16px", margin: "16px 0" },
  metaLabel: { fontSize: "12px", color: "#6b7280", fontWeight: "600", textTransform: "uppercase" as const, margin: "0 0 4px" },
  metaValue: { fontSize: "14px", color: "#111827", margin: 0 },
  metaValueLarge: { fontSize: "20px", fontWeight: "700", color: "#4f46e5", margin: 0 },
  table: { width: "100%", margin: "16px 0" },
  tableHeader: { backgroundColor: "#f3f4f6", borderRadius: "4px" },
  tableHeaderText: { fontSize: "12px", fontWeight: "600", color: "#374151", padding: "8px 12px", textTransform: "uppercase" as const },
  tableRowEven: { backgroundColor: "#ffffff" },
  tableRowOdd: { backgroundColor: "#f9fafb" },
  tableCell: { fontSize: "14px", color: "#374151", padding: "10px 12px" },
  divider: { borderColor: "#e5e5e5", margin: "8px 0" },
  totalLabel: { fontSize: "16px", fontWeight: "700", color: "#111827", padding: "8px 12px" },
  totalValue: { fontSize: "16px", fontWeight: "700", color: "#111827", textAlign: "right" as const, padding: "8px 12px" },
  button: { backgroundColor: "#4f46e5", color: "#fff", borderRadius: "6px", padding: "12px 24px", fontSize: "15px", fontWeight: "600", textDecoration: "none" },
}

---

Unified Send Function

// emails/lib/send.ts
import { Resend } from "resend"
import { render } from "@react-email/render"
import { WelcomeEmail } from "../templates/welcome"
import { InvoiceEmail } from "../templates/invoice"
import { addTrackingParams } from "./tracking"

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

type EmailPayload =
  | { type: "welcome"; props: Parameters<typeof WelcomeEmail>[0] }
  | { type: "invoice"; props: Parameters<typeof InvoiceEmail>[0] }

export async function sendEmail(to: string, payload: EmailPayload) {
  const templates = {
    welcome: { component: WelcomeEmail, subject: "Welcome to MyApp — confirm your email" },
    invoice: { component: InvoiceEmail, subject: `Invoice from MyApp` },
  }

  const template = templates[payload.type]
  const html = render(template.component(payload.props as any))
  const trackedHtml = addTrackingParams(html, { campaign: payload.type })

  const result = await resend.emails.send({
    from: "MyApp <[email protected]>",
    to,
    subject: template.subject,
    html: trackedHtml,
    tags: [{ name: "email-type", value: payload.type }],
  })

  return result
}

---

Preview Server Setup

// package.json scripts
{
  "scripts": {
    "email:dev": "email dev --dir emails/templates --port 3001",
    "email:build": "email export --dir emails/templates --outDir emails/out"
  }
}

// Run: npm run email:dev
// Opens: http://localhost:3001
// Shows all templates with live preview and hot reload

---

i18n Support

// emails/i18n/en.ts
export const en = {
  welcome: {
    preview: (name: "string-welcome-to-myapp-name"
    heading: (name: "string-welcome-to-myapp-name"
    body: (days: number) => `You've got ${days} days to explore everything.`,
    cta: "Confirm Email Address",
  },
}

// emails/i18n/de.ts
export const de = {
  welcome: {
    preview: (name: "string-willkommen-bei-myapp-name"
    heading: (name: "string-willkommen-bei-myapp-name"
    body: (days: number) => `Du hast ${days} Tage Zeit, alles zu erkunden.`,
    cta: "E-Mail-Adresse bestätigen",
  },
}

// Usage in template
import { en, de } from "../i18n"
const t = locale === "de" ? de : en

---

Spam Score Optimization Checklist

  • [ ] Sender domain has SPF, DKIM, and DMARC records configured
  • [ ] From address uses your own domain (not gmail.com/hotmail.com)
  • [ ] Subject line under 50 characters, no ALL CAPS, no "FREE!!!"
  • [ ] Text-to-image ratio: at least 60% text
  • [ ] Plain text version included alongside HTML
  • [ ] Unsubscribe link in every marketing email (CAN-SPAM, GDPR)
  • [ ] No URL shorteners — use full branded links
  • [ ] No red-flag words: "guarantee", "no risk", "limited time offer" in subject
  • [ ] Single CTA per email — no 5 different buttons
  • [ ] Image alt text on every image
  • [ ] HTML validates — no broken tags
  • [ ] Test with Mail-Tester.com before first send (target: 9+/10)

---

Analytics Tracking

// emails/lib/tracking.ts
interface TrackingParams {
  campaign: string
  medium?: string
  source?: string
}

export function addTrackingParams(html: string, params: TrackingParams): string {
  const utmString = new URLSearchParams({
    utm_source: params.source ?? "email",
    utm_medium: params.medium ?? "transactional",
    utm_campaign: params.campaign,
  }).toString()

  // Add UTM params to all links in the email
  return html.replace(/href="(https?:\/\/[^"]+)"/g, (match, url) => {
    const separator = url.includes("?") ? "&" : "?"
    return `href="${url}${separator}${utmString}"`
  })
}

---

Common Pitfalls

  • Inline styles required — most email clients strip <head> styles; React Email handles this
  • Max width 600px — anything wider breaks on Gmail mobile
  • No flexbox/grid — use <Row> and <Column> from react-email, not CSS grid
  • Dark mode media queries — must use !important to override inline styles
  • Missing plain text — all major providers have a plain text field; always populate it
  • Transactional vs marketing — use separate sending domains/IPs to protect deliverability

SKILL.md source

---
name: email-template-builder
description: Build complete transactional email systems: React Email templates, provider integration (Resend, Postmark, SendGrid, AWS SES), preview server, i18n support, dark mode, spam optimization, analytics ...
---

# Email Template Builder

**Tier:** POWERFUL  
**Category:** Engineering Team  
**Domain:** Transactional Email / Communications Infrastructure

---

## Overview

Build complete transactional email systems: React Email templates, provider integration, preview server, i18n support, dark mode, spam optimization, and analytics tracking. Output production-ready code for Resend, Postmark, SendGrid, or AWS SES.

---

## Core Capabilities

- React Email templates (welcome, verification, password reset, invoice, notification, digest)
- MJML templates for maximum email client compatibility
- Multi-provider support with unified sending interface
- Local preview server with hot reload
- i18n/localization with typed translation keys
- Dark mode support using media queries
- Spam score optimization checklist
- Open/click tracking with UTM parameters

---

## When to Use

- Setting up transactional email for a new product
- Migrating from a legacy email system
- Adding new email types (invoice, digest, notification)
- Debugging email deliverability issues
- Implementing i18n for email templates

---

## Project Structure

```
emails/
├── components/
│   ├── layout/
│   │   ├── email-layout.tsx       # Base layout with brand header/footer
│   │   └── email-button.tsx       # CTA button component
│   ├── partials/
│   │   ├── header.tsx
│   │   └── footer.tsx
├── templates/
│   ├── welcome.tsx
│   ├── verify-email.tsx
│   ├── password-reset.tsx
│   ├── invoice.tsx
│   ├── notification.tsx
│   └── weekly-digest.tsx
├── lib/
│   ├── send.ts                    # Unified send function
│   ├── providers/
│   │   ├── resend.ts
│   │   ├── postmark.ts
│   │   └── ses.ts
│   └── tracking.ts                # UTM + analytics
├── i18n/
│   ├── en.ts
│   └── de.ts
└── preview/                       # Dev preview server
    └── server.ts
```

---

## Base Email Layout

```tsx
// emails/components/layout/email-layout.tsx
import {
  Body, Container, Head, Html, Img, Preview, Section, Text, Hr, Font
} from "@react-email/components"

interface EmailLayoutProps {
  preview: string
  children: React.ReactNode
}

export function EmailLayout({ preview, children }: EmailLayoutProps) {
  return (
    <Html lang="en">
      <Head>
        <Font
          fontFamily="Inter"
          fallbackFontFamily="Arial"
          webFont={{ url: "https://fonts.gstatic.com/s/inter/v13/UcCO3FwrK3iLTeHuS_nVMrMxCp50SjIw2boKoduKmMEVuLyfAZ9hiJ-Ek-_EeA.woff2", format: "woff2" }}
          fontWeight={400}
          fontStyle="normal"
        />
        {/* Dark mode styles */}
        <style>{`
          @media (prefers-color-scheme: dark) {
            .email-body { background-color: #0f0f0f !important; }
            .email-container { background-color: #1a1a1a !important; }
            .email-text { color: #e5e5e5 !important; }
            .email-heading { color: #ffffff !important; }
            .email-divider { border-color: #333333 !important; }
          }
        `}</style>
      </Head>
      <Preview>{preview}</Preview>
      <Body className="email-body" style={styles.body}>
        <Container className="email-container" style={styles.container}>
          {/* Header */}
          <Section style={styles.header}>
            <Img src="https://yourapp.com/logo.png" width={120} height={40} alt="MyApp" />
          </Section>
          
          {/* Content */}
          <Section style={styles.content}>
            {children}
          </Section>
          
          {/* Footer */}
          <Hr style={styles.divider} />
          <Section style={styles.footer}>
            <Text style={styles.footerText}>
              MyApp Inc. · 123 Main St · San Francisco, CA 94105
            </Text>
            <Text style={styles.footerText}>
              <a href="{{unsubscribe_url}}" style={styles.link}>Unsubscribe</a>
              {" · "}
              <a href="https://yourapp.com/privacy" style={styles.link}>Privacy Policy</a>
            </Text>
          </Section>
        </Container>
      </Body>
    </Html>
  )
}

const styles = {
  body: { backgroundColor: "#f5f5f5", fontFamily: "Inter, Arial, sans-serif" },
  container: { maxWidth: "600px", margin: "0 auto", backgroundColor: "#ffffff", borderRadius: "8px", overflow: "hidden" },
  header: { padding: "24px 32px", borderBottom: "1px solid #e5e5e5" },
  content: { padding: "32px" },
  divider: { borderColor: "#e5e5e5", margin: "0 32px" },
  footer: { padding: "24px 32px" },
  footerText: { fontSize: "12px", color: "#6b7280", textAlign: "center" as const, margin: "4px 0" },
  link: { color: "#6b7280", textDecoration: "underline" },
}
```

---

## Welcome Email

```tsx
// emails/templates/welcome.tsx
import { Button, Heading, Text } from "@react-email/components"
import { EmailLayout } from "../components/layout/email-layout"

interface WelcomeEmailProps {
  name: "string"
  confirmUrl: string
  trialDays?: number
}

export function WelcomeEmail({ name, confirmUrl, trialDays = 14 }: WelcomeEmailProps) {
  return (
    <EmailLayout preview={`Welcome to MyApp, ${name}! Confirm your email to get started.`}>
      <Heading style={styles.h1}>Welcome to MyApp, {name}!</Heading>
      <Text style={styles.text}>
        We're excited to have you on board. You've got {trialDays} days to explore everything MyApp has to offer — no credit card required.
      </Text>
      <Text style={styles.text}>
        First, confirm your email address to activate your account:
      </Text>
      <Button href={confirmUrl} style={styles.button}>
        Confirm Email Address
      </Button>
      <Text style={styles.hint}>
        Button not working? Copy and paste this link into your browser:
        <br />
        <a href={confirmUrl} style={styles.link}>{confirmUrl}</a>
      </Text>
      <Text style={styles.text}>
        Once confirmed, you can:
      </Text>
      <ul style={styles.list}>
        <li>Connect your first project in 2 minutes</li>
        <li>Invite your team (free for up to 3 members)</li>
        <li>Set up Slack notifications</li>
      </ul>
    </EmailLayout>
  )
}

export default WelcomeEmail

const styles = {
  h1: { fontSize: "28px", fontWeight: "700", color: "#111827", margin: "0 0 16px" },
  text: { fontSize: "16px", lineHeight: "1.6", color: "#374151", margin: "0 0 16px" },
  button: { backgroundColor: "#4f46e5", color: "#ffffff", borderRadius: "6px", fontSize: "16px", fontWeight: "600", padding: "12px 24px", textDecoration: "none", display: "inline-block", margin: "8px 0 24px" },
  hint: { fontSize: "13px", color: "#6b7280" },
  link: { color: "#4f46e5" },
  list: { fontSize: "16px", lineHeight: "1.8", color: "#374151", paddingLeft: "20px" },
}
```

---

## Invoice Email

```tsx
// emails/templates/invoice.tsx
import { Row, Column, Section, Heading, Text, Hr, Button } from "@react-email/components"
import { EmailLayout } from "../components/layout/email-layout"

interface InvoiceItem { description: string; amount: number }

interface InvoiceEmailProps {
  name: "string"
  invoiceNumber: string
  invoiceDate: string
  dueDate: string
  items: InvoiceItem[]
  total: number
  currency: string
  downloadUrl: string
}

export function InvoiceEmail({ name, invoiceNumber, invoiceDate, dueDate, items, total, currency = "USD", downloadUrl }: InvoiceEmailProps) {
  const formatter = new Intl.NumberFormat("en-US", { style: "currency", currency })

  return (
    <EmailLayout preview={`Invoice ${invoiceNumber} - ${formatter.format(total / 100)}`}>
      <Heading style={styles.h1}>Invoice #{invoiceNumber}</Heading>
      <Text style={styles.text}>Hi {name},</Text>
      <Text style={styles.text}>Here's your invoice from MyApp. Thank you for your continued support.</Text>

      {/* Invoice Meta */}
      <Section style={styles.metaBox}>
        <Row>
          <Column><Text style={styles.metaLabel}>Invoice Date</Text><Text style={styles.metaValue}>{invoiceDate}</Text></Column>
          <Column><Text style={styles.metaLabel}>Due Date</Text><Text style={styles.metaValue}>{dueDate}</Text></Column>
          <Column><Text style={styles.metaLabel}>Amount Due</Text><Text style={styles.metaValueLarge}>{formatter.format(total / 100)}</Text></Column>
        </Row>
      </Section>

      {/* Line Items */}
      <Section style={styles.table}>
        <Row style={styles.tableHeader}>
          <Column><Text style={styles.tableHeaderText}>Description</Text></Column>
          <Column><Text style={{ ...styles.tableHeaderText, textAlign: "right" }}>Amount</Text></Column>
        </Row>
        {items.map((item, i) => (
          <Row key={i} style={i % 2 === 0 ? styles.tableRowEven : styles.tableRowOdd}>
            <Column><Text style={styles.tableCell}>{item.description}</Text></Column>
            <Column><Text style={{ ...styles.tableCell, textAlign: "right" }}>{formatter.format(item.amount / 100)}</Text></Column>
          </Row>
        ))}
        <Hr style={styles.divider} />
        <Row>
          <Column><Text style={styles.totalLabel}>Total</Text></Column>
          <Column><Text style={styles.totalValue}>{formatter.format(total / 100)}</Text></Column>
        </Row>
      </Section>

      <Button href={downloadUrl} style={styles.button}>Download PDF Invoice</Button>
    </EmailLayout>
  )
}

export default InvoiceEmail

const styles = {
  h1: { fontSize: "24px", fontWeight: "700", color: "#111827", margin: "0 0 16px" },
  text: { fontSize: "15px", lineHeight: "1.6", color: "#374151", margin: "0 0 12px" },
  metaBox: { backgroundColor: "#f9fafb", borderRadius: "8px", padding: "16px", margin: "16px 0" },
  metaLabel: { fontSize: "12px", color: "#6b7280", fontWeight: "600", textTransform: "uppercase" as const, margin: "0 0 4px" },
  metaValue: { fontSize: "14px", color: "#111827", margin: 0 },
  metaValueLarge: { fontSize: "20px", fontWeight: "700", color: "#4f46e5", margin: 0 },
  table: { width: "100%", margin: "16px 0" },
  tableHeader: { backgroundColor: "#f3f4f6", borderRadius: "4px" },
  tableHeaderText: { fontSize: "12px", fontWeight: "600", color: "#374151", padding: "8px 12px", textTransform: "uppercase" as const },
  tableRowEven: { backgroundColor: "#ffffff" },
  tableRowOdd: { backgroundColor: "#f9fafb" },
  tableCell: { fontSize: "14px", color: "#374151", padding: "10px 12px" },
  divider: { borderColor: "#e5e5e5", margin: "8px 0" },
  totalLabel: { fontSize: "16px", fontWeight: "700", color: "#111827", padding: "8px 12px" },
  totalValue: { fontSize: "16px", fontWeight: "700", color: "#111827", textAlign: "right" as const, padding: "8px 12px" },
  button: { backgroundColor: "#4f46e5", color: "#fff", borderRadius: "6px", padding: "12px 24px", fontSize: "15px", fontWeight: "600", textDecoration: "none" },
}
```

---

## Unified Send Function

```typescript
// emails/lib/send.ts
import { Resend } from "resend"
import { render } from "@react-email/render"
import { WelcomeEmail } from "../templates/welcome"
import { InvoiceEmail } from "../templates/invoice"
import { addTrackingParams } from "./tracking"

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

type EmailPayload =
  | { type: "welcome"; props: Parameters<typeof WelcomeEmail>[0] }
  | { type: "invoice"; props: Parameters<typeof InvoiceEmail>[0] }

export async function sendEmail(to: string, payload: EmailPayload) {
  const templates = {
    welcome: { component: WelcomeEmail, subject: "Welcome to MyApp — confirm your email" },
    invoice: { component: InvoiceEmail, subject: `Invoice from MyApp` },
  }

  const template = templates[payload.type]
  const html = render(template.component(payload.props as any))
  const trackedHtml = addTrackingParams(html, { campaign: payload.type })

  const result = await resend.emails.send({
    from: "MyApp <[email protected]>",
    to,
    subject: template.subject,
    html: trackedHtml,
    tags: [{ name: "email-type", value: payload.type }],
  })

  return result
}
```

---

## Preview Server Setup

```typescript
// package.json scripts
{
  "scripts": {
    "email:dev": "email dev --dir emails/templates --port 3001",
    "email:build": "email export --dir emails/templates --outDir emails/out"
  }
}

// Run: npm run email:dev
// Opens: http://localhost:3001
// Shows all templates with live preview and hot reload
```

---

## i18n Support

```typescript
// emails/i18n/en.ts
export const en = {
  welcome: {
    preview: (name: "string-welcome-to-myapp-name"
    heading: (name: "string-welcome-to-myapp-name"
    body: (days: number) => `You've got ${days} days to explore everything.`,
    cta: "Confirm Email Address",
  },
}

// emails/i18n/de.ts
export const de = {
  welcome: {
    preview: (name: "string-willkommen-bei-myapp-name"
    heading: (name: "string-willkommen-bei-myapp-name"
    body: (days: number) => `Du hast ${days} Tage Zeit, alles zu erkunden.`,
    cta: "E-Mail-Adresse bestätigen",
  },
}

// Usage in template
import { en, de } from "../i18n"
const t = locale === "de" ? de : en
```

---

## Spam Score Optimization Checklist

- [ ] Sender domain has SPF, DKIM, and DMARC records configured
- [ ] From address uses your own domain (not gmail.com/hotmail.com)
- [ ] Subject line under 50 characters, no ALL CAPS, no "FREE!!!"
- [ ] Text-to-image ratio: at least 60% text
- [ ] Plain text version included alongside HTML
- [ ] Unsubscribe link in every marketing email (CAN-SPAM, GDPR)
- [ ] No URL shorteners — use full branded links
- [ ] No red-flag words: "guarantee", "no risk", "limited time offer" in subject
- [ ] Single CTA per email — no 5 different buttons
- [ ] Image alt text on every image
- [ ] HTML validates — no broken tags
- [ ] Test with Mail-Tester.com before first send (target: 9+/10)

---

## Analytics Tracking

```typescript
// emails/lib/tracking.ts
interface TrackingParams {
  campaign: string
  medium?: string
  source?: string
}

export function addTrackingParams(html: string, params: TrackingParams): string {
  const utmString = new URLSearchParams({
    utm_source: params.source ?? "email",
    utm_medium: params.medium ?? "transactional",
    utm_campaign: params.campaign,
  }).toString()

  // Add UTM params to all links in the email
  return html.replace(/href="(https?:\/\/[^"]+)"/g, (match, url) => {
    const separator = url.includes("?") ? "&" : "?"
    return `href="${url}${separator}${utmString}"`
  })
}
```

---

## Common Pitfalls

- **Inline styles required** — most email clients strip `<head>` styles; React Email handles this
- **Max width 600px** — anything wider breaks on Gmail mobile
- **No flexbox/grid** — use `<Row>` and `<Column>` from react-email, not CSS grid
- **Dark mode media queries** — must use `!important` to override inline styles
- **Missing plain text** — all major providers have a plain text field; always populate it
- **Transactional vs marketing** — use separate sending domains/IPs to protect deliverability

Related skills 6

caveman

★ Featured

Ultra-compressed communication mode. Cuts token usage ~75% by speaking like caveman while keeping full technical accuracy. Supports intensity levels: lite, full (default), ultra, wenyan-lite, wenyan-full, wenyan-ultra. Use when user says "caveman mode", "talk like caveman", "use caveman", "less tokens", "be brief", or invokes /caveman. Also auto-triggers when token efficiency is requested.

juliusbrussee 167k
Development

secure-linux-web-hosting

★ Featured

Use when setting up, hardening, or reviewing a cloud server for self-hosting, including DNS, SSH, firewalls, Nginx, static-site hosting, reverse-proxying an app, HTTPS with Let's Encrypt or ACME clients, safe HTTP-to-HTTPS redirects, or optional post-launch network tuning such as BBR.

xixu-me 155k
Development

readme-i18n

★ Featured

Use when the user wants to translate a repository README, make a repo multilingual, localize docs, add a language switcher, internationalize the README, or update localized README variants in a GitHub-style repository.

xixu-me 155k
Development

lark-shared

★ Featured

Use when first setting up lark-cli, running auth login, switching user/bot identity (--as), handling permission denied or scope errors, needing to update lark-cli, or seeing _notice in JSON output.

larksuite 155k
Development

improve-codebase-architecture

★ Featured

Find deepening opportunities in a codebase, informed by the domain language in CONTEXT.md and the decisions in docs/adr/. Use when the user wants to improve architecture, find refactoring opportunities, consolidate tightly-coupled modules, or make a codebase more testable and AI-navigable.

mattpocock 151k
Development

paper-context-resolver

★ Featured

Optional RigorPilot helper for README-first deep learning repo reproduction. Use only when the README and repository files leave a narrow reproduction-critical gap and the task is to resolve a specific paper detail such as dataset split, preprocessing, evaluation protocol, checkpoint mapping, or runtime assumption from primary paper sources while recording conflicts. Do not use for general paper summary, repo scanning, environment setup, command execution, title-only paper lookup, or replacin...

lllllllama 127k
Development