关于 api-email-resend-react-email
面向使用 Resend 和 React Email 构建事务邮件的开发者技能,涵盖类型化模板、安全的服务端发送、重试、批量投递与 Webhook 验证。
主要功能
- 01类型化 React Email 模板
- 02发送前异步渲染
- 03服务端发送与错误处理
- 04指数退避重试
- 05批量投递与 Webhook 验证
适用场景
- 01构建验证和密码重置邮件
- 02发送事务收据与通知
- 03向多个收件人批量发送
- 04验证 Resend Webhook 事件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agents-inc/ skills --skill api-email-resend-react-email安装后在 Claude Code 中输入/api-email-resend-react-email确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.yamlreference.mdexamples/advanced-features.mdasync-batch.mdcore.mdpreferences.mdtemplates.mdwebhooks.md详细介绍
使用 Resend 和 React Email 构建电子邮件模式
快速指南: 使用 Resend 和 React Email 模板发送事务性电子邮件。发送前务必
await render()(它会返回一个 Promise)。仅限服务端使用——切勿向客户端暴露 API 密钥。对于暂时性故障,实现带指数退避的重试。非事务性电子邮件中应包含取消订阅链接(符合 CAN-SPAM 规定)。向 2–100 位收件人批量发送时使用resend.batch.send()(批量发送不支持附件或定时发送)。React Email 5.0+ 已弃用renderAsync——请改用render()。验证 Webhook 需要原始请求正文和webhookSecret参数。
<critical_requirements>
关键:使用此技能前须知
所有代码必须遵循项目在 CLAUDE.md 中的约定(kebab-case、命名导出、导入顺序、
import type、命名常量)
(将 HTML 传递给 resend.emails.send() 前,必须先 await render()——render 会返回一个 Promise)
(必须处理 Resend API 错误,并为暂时性故障实现重试逻辑)
(所有电子邮件都必须在服务端发送——切勿向客户端暴露 RESEND_API_KEY)
(营销/通知电子邮件必须包含取消订阅链接——这是符合 CAN-SPAM 规定的要求)
(所有电子邮件模板都必须使用带类型的 props 接口——以便在编译时进行验证)
</critical_requirements>
自动检测: Resend、React Email、@react-email/components、resend.emails.send、电子邮件模板、事务性电子邮件、验证电子邮件、密码重置电子邮件、通知电子邮件、电子邮件渲染、resend.batch.send、resend.webhooks.verify
适用场景:
- 发送事务性电子邮件(验证、密码重置、收据)
- 使用 Tailwind 样式创建 React Email 模板
- 构建通过电子邮件发送通知的系统
- 通过 Webhook 实现电子邮件跟踪
- 向多个收件人批量发送
不适用场景:
- 营销活动管理(使用专门的营销工具)
- SMS 或推送通知(需要使用其他服务)
- 电子邮件列表管理(使用 Resend Audiences 或营销工具)
理念
现代应用中的电子邮件采用服务端、模板驱动的方式。React Email 将组件模式引入电子邮件开发,而 Resend 则负责可靠投递。
核心原则:
- 仅限服务端——切勿向客户端暴露 API 密钥
- 带类型的模板——props 接口可在编译时捕获错误
- 可靠投递——通过错误处理和重试逻辑应对暂时性故障
- 非阻塞——对于非关键电子邮件,采用发后即忘模式
适合发送电子邮件的场景:
- 用户身份验证事件(验证、密码重置、2FA)
- 事务性确认(购买、注册、邀请)
- 重要通知(安全警报、账户变更)
- 团队协作(邀请、提及、更新)
不适合发送电子邮件的场景:
- 每个细微操作都发送通知(会造成电子邮件疲劳)
- 未经同意发送营销邮件(属于垃圾邮件,且可能违法)
- 实时警报(使用推送通知)
- 应用内操作(改为显示应用内通知)
核心模式
模式 1:电子邮件模板结构
定义带类型的 props,使用 React Email 组件,并为开发服务器添加 PreviewProps。
interface WelcomeEmailProps {
userName: string;
loginUrl: string;
features?: string[];
}
export function WelcomeEmail({ userName, loginUrl, features = [] }: WelcomeEmailProps) {
return (
<BaseLayout preview={`Welcome, ${userName}!`}>
<Heading>Welcome!</Heading>
<Text>Hi {userName},</Text>
<Button href={loginUrl}>Get Started</Button>
</BaseLayout>
);
}
WelcomeEmail.PreviewProps = { userName: "John", loginUrl: "..." } satisfies WelcomeEmailProps;
完整的模板(包含布局和样式)请参阅 examples/core.md 中的模式 1。
模式 2:带错误处理的发送方式
务必 await render(),检查 { data, error } 响应,并返回带类型的结果。
const html = await render(options.react); // CRITICAL: must await
const { data, error } = await resend.emails.send({
from: `${DEFAULT_FROM_NAME} <${DEFAULT_FROM_ADDRESS}>`,
to: options.to,
subject: options.subject,
html,
});
if (error) {
return { success: false, error: error.message };
}
return { success: true, id: data?.id };
完整的发送封装和指数退避重试实现请参阅 examples/core.md 中的模式 2-3。
模式 3:异步(发后即忘)发送
对于非关键电子邮件(欢迎邮件、通知),不要阻塞响应。
// Non-blocking - catches errors internally
sendEmailAsync(options);
return Response.json({ success: true }); // Returns immediately
跟踪正在执行的 Promise,以便优雅关闭。请参阅 examples/async-batch.md 中的模式 1-2。
模式 4:批量 API
对 2-100 位收件人使用 resend.batch.send()。并行渲染所有模板。
const rendered = await Promise.all(
emails.map(async (e) => ({
from,
to: e.to,
subject: e.subject,
html: await render(e.react),
})),
);
const { data, error } = await resend.batch.send(rendered);
**批量发送限制:**不支持 attachments 和 scheduledAt。支持标签和幂等键。参见 examples/async-batch.md 中的模式 3-4。
模式 5:Webhook 验证
使用原始请求正文调用 resend.webhooks.verify()。解析 JSON 会破坏签名验证。
const payload = await request.text(); // Raw body, NOT .json()
const event = resend.webhooks.verify({
payload,
headers: {
id: request.headers.get("svix-id") ?? "",
timestamp: request.headers.get("svix-timestamp") ?? "",
signature: request.headers.get("svix-signature") ?? "",
},
webhookSecret: process.env.RESEND_WEBHOOK_SECRET!,
});
完整的处理程序(包括事件处理和 Svix 替代方案)请参见 examples/webhooks.md。
模式 6:定时发送、幂等键、标签
// Scheduled (up to 30 days, NOT supported in batch)
await resend.emails.send({ ...payload, scheduledAt: futureDate.toISOString() });
// Idempotency (256 char limit, expires 24h) — second argument to send()
await resend.emails.send(payload, {
idempotencyKey: `order-confirmation-${orderId}`,
});
// Tags (ASCII alphanumeric, underscores, dashes only)
await resend.emails.send({
...payload,
tags: [{ name: "campaign", value: "launch" }],
});
包含验证的完整实现请参见 examples/advanced-features.md。
模式 7:取消订阅和偏好设置
非事务性邮件必须包含取消订阅链接(CAN-SPAM)。请使用签名令牌来确保安全。
// In every notification/marketing template:
<Link href={unsubscribeUrl}>Unsubscribe from these notifications</Link>
// Generate signed unsubscribe URLs
const token = jwt.sign({ userId, category }, UNSUBSCRIBE_SECRET, { expiresIn: "30d" });
const url = `${APP_URL}/api/email/unsubscribe?token=${token}`;
偏好设置架构、发送前检查以及取消订阅端点,请参见 examples/preferences.md。
详细资源:
- examples/core.md - 模板结构、带错误处理的发送、重试逻辑
- examples/async-batch.md - 异步发送、批量 API
- examples/webhooks.md - 带签名验证的 Webhook 处理程序
- examples/templates.md - 密码重置、通知模板
- examples/preferences.md - 取消订阅、邮件偏好设置
- examples/advanced-features.md - 定时发送、幂等键、标签
- reference.md - 决策框架、反模式
<red_flags>
危险信号
高优先级问题:
- 未等待
render()- 将"[object Promise]"作为邮件正文发送 - API 密钥暴露在客户端 - 安全漏洞
- 没有错误处理 - 故障无提示
- 非事务性邮件缺少取消订阅链接 - 违反 CAN-SPAM
- 发送前未检查用户偏好设置 - 垃圾邮件
中优先级问题:
- 瞬时故障(速率限制、5xx 错误)没有重试逻辑
- 为非关键邮件进行同步发送,阻塞请求处理程序
- 发件人地址硬编码,而不是使用环境变量
- 没有 Webhook 验证签名检查
- 不记录邮件发送结果
常见错误:
- 在邮件模板中使用
Grid或Flexbox(邮件客户端不支持) - 期望阴影或渐变在邮件中正常呈现
- 使用
rem单位(不同邮件客户端的处理方式不同) - 忘记为开发服务器设置
PreviewProps
注意事项和边界情况:
- Resend SDK 接受直接传入的
react属性(会在内部渲染),但使用await render()预渲染并传入html,可以让你控制输出内容,并且适用于 Resend SDK 之外的场景 - React Email 5.0+ 中的
render()是异步的(renderAsync已弃用) - 批量 API 最多支持 100 封邮件,不支持
attachments或scheduledAt - Webhook 需要原始请求正文 - 解析 JSON 会破坏签名验证
- Webhook 验证使用
webhookSecret参数(不是secret) - Webhook 标头对象使用简短键名:
id、timestamp、signature - 幂等键作为第二个参数传入
resend.emails.send(),而不是放在邮件负载标头中 - 幂等键 24 小时后过期,最长 256 个字符
- 标签仅支持 ASCII 字母数字字符、下划线和连字符;每个键/值最多 256 个字符
- 在邮件中使用 Tailwind 需要
@react-email/tailwind包装器(React Email 5.0+ 支持 Tailwind 4) - 图片必须使用绝对 URL(不能使用相对路径)
</red_flags>
<critical_reminders>
关键提醒
所有代码都必须遵循项目中 CLAUDE.md 的约定(kebab-case、命名导出、导入排序、
import type、命名常量)
(将 HTML 传递给 resend.emails.send() 前,必须等待 render() 完成——render 会返回一个 Promise)
(必须处理 Resend API 错误,并为暂时性故障实现重试逻辑)
(所有邮件都必须通过服务端发送——切勿向客户端暴露 RESEND_API_KEY)
(营销邮件和通知邮件中必须包含退订链接——这是符合 CAN-SPAM 规范的要求)
(所有邮件模板都必须使用带类型的 props 接口——以便在编译时进行验证)
不遵守这些规则将导致邮件发送失败、安全漏洞或法律合规问题。
</critical_reminders>