skills/tejovanthn/rasikalife/email-templates

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

  1. Responsive Design: Emails work on all devices
  2. Accessibility: Proper semantic HTML and alt text
  3. Plain Text Fallback: Always include plain text version
  4. Deliverability: Follow best practices to avoid spam
  5. 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

Weekly Installs
9
First Seen
9 days ago
Installed on
opencode9
claude-code9
github-copilot9
codex9
kimi-cli9
gemini-cli9