📦 email-template-builder — 电子邮件模板构建器
v1.0.0电子邮件模板构建器,用于生成完整的交易性电子邮件系统,支持React Email模板、多提供商集成、预览服务器、国际化、暗模式、垃圾邮件优化和分析跟踪。输出适用于Resend、Postmark、SendGrid和AWS SES的生产就绪代码。
详细分析 ▾
运行时依赖
版本
安装命令
点击复制技能文档
等级: 强大 类别: 工程团队 领域: 事务性邮件 / 通信基础设施
概述
构建完整的事务性邮件系统:React Email 模板、邮件服务商集成、预览服务器、i18n 支持、深色模式、垃圾邮件优化和分析追踪。输出可用于 Resend、Postmark、SendGrid 或 AWS SES 的生产级代码。
核心功能
- React Email 模板(欢迎邮件、验证邮件、密码重置、发票、通知、周报)
- MJML 模板以实现最大的邮件客户端兼容性
- 统一发送接口支持多服务商
- 带热重载的本地预览服务器
- 带类型化翻译键的 i18n/本地化
- 使用媒体查询的深色模式支持
- 垃圾邮件分数优化检查清单
- 带 UTM 参数的打开/点击追踪
使用场景
- 为新产品设置事务性邮件
- 从传统邮件系统迁移
- 添加新的邮件类型(发票、周报、通知)
- 调试邮件投递问题
- 为邮件模板实现 i18n
项目结构
emails/
├── components/
│ ├── layout/
│ │ ├── email-layout.tsx # 基础布局,包含品牌页眉/页脚
│ │ └── email-button.tsx # CTA 按钮组件
│ ├── partials/
│ │ ├── header.tsx
│ │ └── footer.tsx
├── templates/
│ ├── welcome.tsx
│ ├── verify-email.tsx
│ ├── password-reset.tsx
│ ├── invoice.tsx
│ ├── notification.tsx
│ └── weekly-digest.tsx
├── lib/
│ ├── send.ts # 统一发送函数
│ ├── providers/
│ │ ├── resend.ts
│ │ ├── postmark.ts
│ │ └── ses.ts
│ └── tracking.ts # UTM + 分析
├── i18n/
│ ├── en.ts
│ └── de.ts
└── preview/ # 开发预览服务器
└── server.ts
基础邮件布局
// 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", }, }
欢迎邮件
// 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: Confirm Email Address 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", }, }
发票邮件
// 发票邮件模板 // 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 ( 发票 ${invoiceNumber} - ${formatter.format(total / 100)}}> 发票 #{invoiceNumber} Hi {name}, 这是您来自 MyApp 的发票。感谢您一直以来的支持。{/ 发票元数据 /}
发票日期 {invoiceDate} 到期日期 {dueDate} 应付金额 {formatter.format(total / 100)}{/ 明细项目 /}
描述 金额 {items.map((item, i) => ( {item.description} {formatter.format(item.amount / 100)} ))}
合计 {formatter.format(total / 100)}下载 PDF 发票 ) }
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", }, }
统一发送函数
// 邮件发送库 // 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: "欢迎来到 MyApp — 请确认您的邮箱", }, invoice: { component: InvoiceEmail, subject:
来自 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 }
预览服务器设置
// package.json 脚本 { "scripts": { "email:dev": "email dev --dir emails/templates --port 3001", "email:build": "email export --dir emails/templates --outDir emails/out" } }
// 运行:npm run email:dev // 打开:http://localhost:3001 // 显示所有模板的实时预览和热重载
国际化支持
// 英文翻译 // 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: "确认邮箱地址", }, }// 德文翻译 // 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", }, }// 在模板中使用 import { en, de } from "../i18n"
const t = locale === "de" ? de : en
垃圾邮件分数优化检查清单
- [ ] 发件人域名已配置 SPF、DKIM 和 DMARC 记录
- [ ] 发件人地址使用您自己的域名(不是 gmail.com/hotmail.com)
- [ ] 主题行少于 50 个字符,不使用全大写,不使用 "FREE!!!"
- [ ] 文本与图片比例:至少 60% 为文本
- [ ] HTML 版本附带纯文本版本
- [ ] 每封营销邮件包含退订链接(CAN-SPAM、GDPR)
- [ ] 不使用 URL 缩短器 — 使用完整的品牌链接
- [ ] 主题行中避免敏感词:"guarantee"、"no risk"、"limited time offer"
- [ ] 每封邮件一个 CTA — 不使用 5 个不同的按钮
- [ ] 每张图片都有 alt 文本
- [ ] HTML 验证通过 — 无破损标签
- [ ] 首次发送前使用 Mail-Tester.com 测试(目标:9+/10)
分析跟踪
// 邮件跟踪库 // 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()
// 为邮件中的所有链接添加 UTM 参数 return html.replace(/href="(https?:\/\/[^"]+)"/g, (match, url) => { const separator = url.includes("?") ? "&" : "?" returnhref="${url}${separator}${utmString}"}) }
常见陷阱
- 必须使用内联样式 — 大多数邮件客户端会剥离
样式;React Email 会处理这个问题 - 最大宽度 600px — 超过这个宽度在 Gmail 移动端会显示异常
- 不支持 flexbox/grid — 使用 react-email 的
和,而不是 CSS grid - 深色模式媒体查询 — 必须使用
!important来覆盖内联样式 - 缺少纯文本版本 — 所有主流邮件提供商都有纯文本字段;务必填充它
- 事务性邮件 vs 营销邮件 — 使用单独的发件域名/IP 来保护送达率
{/ Content /}
{children}
{/ Footer /}