clawhub install email-template-builderπ About This Skill
name: "email-template-builder" description: "Email Template Builder"
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
When to Use
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 (
{/* Dark mode styles */}
{preview}
{/* Header */}
{/* Content */}
{children}
{/* Footer */}
MyApp Inc. Β· 123 Main St Β· San Francisco, CA 94105
Unsubscribe
{" Β· "}
Privacy Policy
)
}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 (
Welcome to MyApp, ${name}! Confirm your email to get started. Welcome to MyApp, {name}! We're excited to have you on board. You've got {trialDays} days to explore everything MyApp has to offer β no credit card required. First, confirm your email address to activate your account: Button not working? Copy and paste this link into your browser:
{confirmUrl}Once confirmed, you can:
- Connect your first project in 2 minutes
- Invite your team (free for up to 3 members)
- Set up Slack notifications
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 (
Invoice ${invoiceNumber} - ${formatter.format(total / 100)} Invoice #{invoiceNumber} Hi {name}, Here's your invoice from MyApp. Thank you for your continued support. {/* Invoice Meta */}
|
Invoice Date {invoiceDate} Due Date {dueDate} Amount Due {formatter.format(total / 100)} {/* Line Items */}
{items.map((item, i) => (
Description Amount ))}
{item.description} {formatter.format(item.amount / 100)}
|
Total {formatter.format(total / 100)} ) }
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[0] }
| { type: "invoice"; props: Parameters[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 ",
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
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
styles; React Email handles this|
and from react-email, not CSS grid!important to override inline styles