email-templates
SKILL.md
Email Templates Skill
Transactional email design using React Email and integration with email providers like Resend or AWS SES.
Core Principles
- Responsive Design: Emails work on all devices
- Accessibility: Proper semantic HTML and alt text
- Plain Text Fallback: Always include plain text version
- Deliverability: Follow best practices to avoid spam
- Testing: Preview emails before sending
Installation
# React Email
npm install react-email @react-email/components
# Email provider (choose one)
npm install resend # Recommended
npm install @aws-sdk/client-ses
React Email Setup
1. Create Email Templates Directory
emails/
├── welcome.tsx
├── reset-password.tsx
├── verify-email.tsx
├── receipt.tsx
└── components/
├── layout.tsx
└── button.tsx
2. Base Layout Component
// emails/components/layout.tsx
import {
Html,
Head,
Preview,
Body,
Container,
Section,
Text,
} from "@react-email/components"
interface LayoutProps {
preview: string
children: React.ReactNode
}
export function EmailLayout({ preview, children }: LayoutProps) {
return (
<Html>
<Head />
<Preview>{preview}</Preview>
<Body style={main}>
<Container style={container}>
{children}
<Section style={footer}>
<Text style={footerText}>
© 2026 Your Company. All rights reserved.
</Text>
</Section>
</Container>
</Body>
</Html>
)
}
const main = {
backgroundColor: "#f6f9fc",
fontFamily: '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
}
const container = {
backgroundColor: "#ffffff",
margin: "0 auto",
padding: "20px 0 48px",
marginBottom: "64px",
}
const footer = {
color: "#8898aa",
fontSize: "12px",
lineHeight: "16px",
marginTop: "32px",
}
const footerText = {
margin: "0",
textAlign: "center" as const,
}
3. Reusable Button Component
// emails/components/button.tsx
import { Button as ReactEmailButton } from "@react-email/components"
interface ButtonProps {
href: string
children: React.ReactNode
}
export function Button({ href, children }: ButtonProps) {
return (
<ReactEmailButton href={href} style={button}>
{children}
</ReactEmailButton>
)
}
const button = {
backgroundColor: "#5469d4",
borderRadius: "4px",
color: "#fff",
fontSize: "16px",
fontWeight: "bold",
textDecoration: "none",
textAlign: "center" as const,
display: "block",
padding: "12px 24px",
}
Email Templates
Welcome Email
// emails/welcome.tsx
import {
Section,
Heading,
Text,
} from "@react-email/components"
import { EmailLayout } from "./components/layout"
import { Button } from "./components/button"
interface WelcomeEmailProps {
name: string
dashboardUrl: string
}
export default function WelcomeEmail({ name, dashboardUrl }: WelcomeEmailProps) {
return (
<EmailLayout preview={`Welcome to our platform, ${name}!`}>
<Section style={content}>
<Heading style={heading}>Welcome aboard, {name}!</Heading>
<Text style={paragraph}>
We're excited to have you on board. Your account has been successfully created.
</Text>
<Text style={paragraph}>
Here's what you can do next:
</Text>
<ul style={list}>
<li>Complete your profile</li>
<li>Explore the dashboard</li>
<li>Invite your team members</li>
</ul>
<Button href={dashboardUrl}>
Go to Dashboard
</Button>
<Text style={paragraph}>
If you have any questions, feel free to reply to this email.
</Text>
</Section>
</EmailLayout>
)
}
const content = {
padding: "24px",
}
const heading = {
fontSize: "24px",
lineHeight: "32px",
fontWeight: "700",
margin: "0 0 16px",
}
const paragraph = {
fontSize: "16px",
lineHeight: "24px",
margin: "0 0 16px",
}
const list = {
fontSize: "16px",
lineHeight: "24px",
marginBottom: "16px",
}
Password Reset Email
// emails/reset-password.tsx
import {
Section,
Heading,
Text,
Hr,
} from "@react-email/components"
import { EmailLayout } from "./components/layout"
import { Button } from "./components/button"
interface ResetPasswordEmailProps {
name: string
resetUrl: string
expiresIn: string
}
export default function ResetPasswordEmail({
name,
resetUrl,
expiresIn,
}: ResetPasswordEmailProps) {
return (
<EmailLayout preview="Reset your password">
<Section style={content}>
<Heading style={heading}>Reset your password</Heading>
<Text style={paragraph}>Hi {name},</Text>
<Text style={paragraph}>
We received a request to reset your password. Click the button below to choose a new password.
</Text>
<Button href={resetUrl}>
Reset Password
</Button>
<Text style={paragraph}>
This link will expire in {expiresIn}.
</Text>
<Hr style={hr} />
<Text style={small}>
If you didn't request a password reset, you can safely ignore this email.
</Text>
</Section>
</EmailLayout>
)
}
const content = {
padding: "24px",
}
const heading = {
fontSize: "24px",
lineHeight: "32px",
fontWeight: "700",
margin: "0 0 16px",
}
const paragraph = {
fontSize: "16px",
lineHeight: "24px",
margin: "0 0 16px",
}
const hr = {
borderColor: "#e6ebf1",
margin: "20px 0",
}
const small = {
fontSize: "14px",
lineHeight: "20px",
color: "#6b7280",
}
Receipt Email
// emails/receipt.tsx
import {
Section,
Heading,
Text,
Hr,
Row,
Column,
} from "@react-email/components"
import { EmailLayout } from "./components/layout"
interface ReceiptEmailProps {
name: string
orderId: string
date: string
items: Array<{
name: string
quantity: number
price: number
}>
total: number
}
export default function ReceiptEmail({
name,
orderId,
date,
items,
total,
}: ReceiptEmailProps) {
return (
<EmailLayout preview={`Receipt for order ${orderId}`}>
<Section style={content}>
<Heading style={heading}>Thanks for your order!</Heading>
<Text style={paragraph}>Hi {name},</Text>
<Text style={paragraph}>
Your order has been confirmed. Here's a summary:
</Text>
<Section style={orderInfo}>
<Text style={label}>Order ID: <strong>{orderId}</strong></Text>
<Text style={label}>Date: {date}</Text>
</Section>
<Hr style={hr} />
{items.map((item, index) => (
<Row key={index} style={itemRow}>
<Column>
<Text style={itemName}>{item.name}</Text>
<Text style={itemQuantity}>Qty: {item.quantity}</Text>
</Column>
<Column style={itemPriceColumn}>
<Text style={itemPrice}>${item.price.toFixed(2)}</Text>
</Column>
</Row>
))}
<Hr style={hr} />
<Row style={totalRow}>
<Column>
<Text style={totalLabel}>Total</Text>
</Column>
<Column style={totalPriceColumn}>
<Text style={totalPrice}>${total.toFixed(2)}</Text>
</Column>
</Row>
</Section>
</EmailLayout>
)
}
const content = { padding: "24px" }
const heading = { fontSize: "24px", fontWeight: "700", margin: "0 0 16px" }
const paragraph = { fontSize: "16px", margin: "0 0 16px" }
const orderInfo = { backgroundColor: "#f6f9fc", padding: "16px", borderRadius: "4px", marginBottom: "16px" }
const label = { fontSize: "14px", margin: "4px 0" }
const hr = { borderColor: "#e6ebf1", margin: "20px 0" }
const itemRow = { marginBottom: "12px" }
const itemName = { fontSize: "16px", margin: "0" }
const itemQuantity = { fontSize: "14px", color: "#6b7280", margin: "4px 0 0" }
const itemPriceColumn = { textAlign: "right" as const }
const itemPrice = { fontSize: "16px", margin: "0" }
const totalRow = { marginTop: "8px" }
const totalLabel = { fontSize: "18px", fontWeight: "700", margin: "0" }
const totalPriceColumn = { textAlign: "right" as const }
const totalPrice = { fontSize: "18px", fontWeight: "700", margin: "0" }
Sending Emails
Using Resend (Recommended)
// lib/email.server.ts
import { Resend } from "resend"
import { render } from "@react-email/render"
import WelcomeEmail from "~/emails/welcome"
const resend = new Resend(process.env.RESEND_API_KEY)
export async function sendWelcomeEmail(to: string, name: string, dashboardUrl: string) {
const html = render(<WelcomeEmail name={name} dashboardUrl={dashboardUrl} />)
await resend.emails.send({
from: "noreply@yourapp.com",
to,
subject: "Welcome to Your App!",
html,
})
}
// Alternative: Using Resend with React component directly
export async function sendWelcomeEmailDirect(to: string, name: string, dashboardUrl: string) {
await resend.emails.send({
from: "noreply@yourapp.com",
to,
subject: "Welcome to Your App!",
react: <WelcomeEmail name={name} dashboardUrl={dashboardUrl} />,
})
}
Using AWS SES
// lib/email.server.ts
import { SESClient, SendEmailCommand } from "@aws-sdk/client-ses"
import { render } from "@react-email/render"
import WelcomeEmail from "~/emails/welcome"
const ses = new SESClient({ region: "us-east-1" })
export async function sendWelcomeEmail(to: string, name: string, dashboardUrl: string) {
const html = render(<WelcomeEmail name={name} dashboardUrl={dashboardUrl} />)
const text = render(<WelcomeEmail name={name} dashboardUrl={dashboardUrl} />, {
plainText: true,
})
const command = new SendEmailCommand({
Source: "noreply@yourapp.com",
Destination: { ToAddresses: [to] },
Message: {
Subject: { Data: "Welcome to Your App!" },
Body: {
Html: { Data: html },
Text: { Data: text },
},
},
})
await ses.send(command)
}
SST Integration
// sst.config.ts
import { Config } from "sst/constructs"
export default {
stacks(app) {
app.stack(function Email({ stack }) {
// Option 1: Resend API key
const RESEND_API_KEY = new Config.Secret(stack, "RESEND_API_KEY")
// Option 2: SES (no additional config needed)
// Lambda functions automatically get SES permissions
new RemixSite(stack, "site", {
bind: [RESEND_API_KEY],
})
})
},
}
Email Best Practices
Deliverability
// 1. Verify sender domain
// Add SPF, DKIM, DMARC records to your DNS
// 2. Use proper from address
from: "Your Name <noreply@yourapp.com>"
// 3. Include unsubscribe link
<Link href={unsubscribeUrl} style={unsubscribeLink}>
Unsubscribe
</Link>
// 4. Authenticate email domain
// With Resend: verify domain in dashboard
// With SES: verify domain and configure DKIM
Content Guidelines
// DO: Use plain, professional language
subject: "Your order #12345 has shipped"
// DON'T: Use spammy language
subject: "🔥 AMAZING DEAL!!! BUY NOW!!!"
// DO: Include clear CTAs
<Button href={url}>View Order</Button>
// DON'T: Use too many links
// Limit to 1-3 primary CTAs
// DO: Provide context
"You're receiving this because you signed up for updates on [date]"
// DON'T: Send without context
// Always explain why they're receiving the email
Accessibility
import { Img } from "@react-email/components"
// Always include alt text
<Img
src="https://yourapp.com/logo.png"
alt="Your App Logo"
width="150"
height="50"
/>
// Use semantic HTML
<Heading>Main Title</Heading>
<Text>Paragraph content</Text>
// Ensure good color contrast
const linkStyle = {
color: "#0066cc", // Good contrast with white background
textDecoration: "underline",
}
// Use readable font sizes
const text = {
fontSize: "16px", // Minimum 14px
lineHeight: "24px", // 1.5x font size
}
Testing Emails
Development Preview
# Start React Email dev server
npm run email:dev
# package.json
{
"scripts": {
"email:dev": "email dev"
}
}
# Visit http://localhost:3000 to preview emails
Send Test Emails
// scripts/test-email.ts
import { sendWelcomeEmail } from "./lib/email.server"
await sendWelcomeEmail(
"test@example.com",
"Test User",
"https://app.yourapp.com/dashboard"
)
console.log("Test email sent!")
Email Testing Tools
// Use Mailtrap for testing in development
const resend = new Resend(
process.env.NODE_ENV === "production"
? process.env.RESEND_API_KEY
: process.env.MAILTRAP_API_KEY
)
// Use Litmus or Email on Acid for rendering tests across clients
Advanced Patterns
Email with Attachments
await resend.emails.send({
from: "noreply@yourapp.com",
to: email,
subject: "Your Invoice",
react: <InvoiceEmail />,
attachments: [
{
filename: "invoice.pdf",
content: pdfBuffer,
},
],
})
Scheduled Emails
// Using EventBridge with SST
import { Cron } from "sst/constructs"
new Cron(stack, "DailySummary", {
schedule: "cron(0 9 * * ? *)", // 9 AM daily
job: "functions/send-daily-summary.handler",
})
// functions/send-daily-summary.ts
export async function handler() {
const users = await getActiveUsers()
for (const user of users) {
await sendDailySummaryEmail(user.email, user.name, user.stats)
}
}
Email Analytics
// Track opens and clicks with Resend
await resend.emails.send({
from: "noreply@yourapp.com",
to: email,
subject: "Your Report",
react: <ReportEmail />,
tags: [
{ name: "category", value: "report" },
{ name: "user_id", value: userId },
],
})
// Query analytics
const analytics = await resend.emails.get(emailId)
console.log(analytics.opens, analytics.clicks)
Common Email Types
// Welcome series
export const welcomeEmails = [
{ delay: 0, template: "welcome", subject: "Welcome!" },
{ delay: 1, template: "getting-started", subject: "Getting Started Guide" },
{ delay: 3, template: "tips", subject: "Pro Tips for Success" },
]
// Transactional
- Welcome email
- Email verification
- Password reset
- Receipt/invoice
- Shipping notification
- Account changes
// Engagement
- Weekly summary
- Achievement unlocked
- Abandoned cart
- Re-engagement
- Product updates
Resources
- React Email Documentation
- Resend Documentation
- AWS SES Best Practices
- Email on Acid
- Can I email - Email client support tables
Weekly Installs
9
Repository
tejovanthn/rasikalifeFirst Seen
9 days ago
Security Audits
Installed on
opencode9
claude-code9
github-copilot9
codex9
kimi-cli9
gemini-cli9