Novu 项目 React Email 组件参考:从@react-email/components到可上线的邮件模板
【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu
本篇技术指南围绕 Novu 仓库.agents中为 Agent 与开发者准备的 React Email 技能组件参考 展开,系统梳理@react-email/components提供的全部组件、Props 与推荐写法。读完本文,你将能够基于Tailwind+pixelBasedPreset的组合独立搭建欢迎邮件、密码重置、通知确认等事务性邮件模板,并掌握在客户端兼容性约束下(不支持rem、flexbox、媒体查询等)写出可直接用于 Novu 邮件工作流的高质量模板代码。
这份文档在仓库中的定位
在 Novu 仓库中,.agents/skills/react-email目录是一份供编码 Agent 使用的完整技能包,其中SKILL.md定义了技能主流程(安装、开发服务器、模板骨架、样式约束),而 COMPONENTS.md 是该技能包的组件级权威参考,与 STYLING.md、PATTERNS.md、I18N.md、SENDING.md 互为补充。
React Email 是一套"高质量、无预设样式"的 React 组件集合,用组件化方式构建在主流邮件客户端均能正常渲染的 HTML 邮件。这一点与 Novu 本身高度契合:Novu 的 Framework Email 步骤文档 明确推荐用 React Email 组件编写邮件模板、再通过render()输出 HTML 交由邮件工作流发送,从而保证模板与品牌一致且易于维护。
需要特别记住两条硬性约定:
- 只导入真正用到的组件。未使用却出现在代码中的组件不应被导入,这既影响包体积也容易让模板校验误判依赖关系。
- 所有组件统一从
@react-email/components导入(含render与pixelBasedPreset)。
组件全景速览
全部组件均从@react-email/components导入,按用途可分为四类:
| 类别 | 组件 | 一句话作用 |
|---|---|---|
| 结构 | Html | 邮件根包裹,始终作为最外层组件 |
| 结构 | Head | 承载title、style、meta等文档级头部内容 |
| 结构 | Body | 邮件正文的主包裹容器 |
| 结构 | Container | 内容在断点处水平居中,自带最大宽度约束 |
| 结构 | Section | 可通过行/列进一步排版的分区 |
| 结构 | Row/Column | 行内水平分隔内容区 / 列内垂直分隔内容区(Column 必须配合 Row) |
| 内容 | Preview | 收件箱中显示的预览文本 |
| 内容 | Heading | h1–h6 标题块 |
| 内容 | Text | 以空白间隔的文本块 |
| 内容 | Button | 外观像按钮的链接(含 Outlook 内边距修复) |
| 内容 | Link | 指向网页、邮件地址等 URL 的超链接 |
| 内容 | Img | 展示图片 |
| 内容 | Hr | 分隔不同内容区域的分割线 |
| 专用 | Tailwind | 用 Tailwind CSS 包裹并统一样式 |
| 专用 | CodeBlock | 基于 Prism.js 的主题化正则高亮代码块 |
| 专用 | CodeInline | 在所有客户端表现一致的行内代码元素 |
| 专用 | Markdown | 将 Markdown 转换为合法邮件模板代码 |
| 专用 | Font | 声明自定义字体 |
下文按"Tailwind 主题 → 结构组件 → 内容组件 → 专用组件"的顺序逐一讲解,保证每个组件的代码示例、Props、最佳实践不遗漏。
Tailwind:推荐的样式方案
组件参考文档明确将Tailwind定位为 React Email 组件推荐的样式方式——用组件包裹邮件内容,再通过工具类(utility classes)完成排版与配色。
import { Tailwind, pixelBasedPreset, Html, Body, Container, Heading, Text, Button } from '@react-email/components'; export default function Email() { return ( <Html lang="en"> <Tailwind config={{ presets: [pixelBasedPreset], theme: { extend: { colors: { brand: '#007bff', accent: '#28a745' }, }, }, }} > <Body className="bg-gray-100 font-sans"> <Container className="max-w-xl mx-auto p-5"> <Heading className="text-2xl font-bold text-brand mb-4"> Welcome! </Heading> <Text className="text-base text-gray-700 mb-4"> Your content here. </Text> <Button href="https://example.com" className="bg-brand text-white px-6 py-3 rounded-lg block text-center" > Get Started </Button> </Container> </Body> </Tailwind> </Html> ); }Props
config:Tailwind 配置对象,透传给底层 Tailwind 编译过程。
底层工作方式
- Tailwind 工具类在构建阶段被自动转换为内联样式;
- 媒体查询被抽取出来放到
<head>的<style>标签内; - CSS 变量(如
--brand)会被解析成最终值; - RGB 颜色语法会被规范化,以保证不同邮件客户端的兼容性。
必须遵守的注意事项
- 始终使用
pixelBasedPreset——主流邮件客户端不支持rem单位,只有像素级预设才能让间距、字号在所有客户端一致(.agents/skills/react-email/SKILL.md的样式章节也重复强调了这一点); - 自定义 config 是可选的,默认配置在多数场景已足够,扩展配色时优先使用
theme.extend; sm:、md:、lg:等响应式断点工具类虽然能通过媒体查询工作,但由于邮件客户端对媒体查询支持有限,应谨慎使用;技能行为准则更是建议在用户主动要求时才考虑,否则一律采用堆叠布局。
结构组件:搭好邮件的骨架
Html
邮件的根组件,必须始终作为最外层,为内容提供lang、dir语义。
import { Html, Tailwind, pixelBasedPreset } from '@react-email/components'; <Html lang="en" dir="ltr"> <Tailwind config={{ presets: [pixelBasedPreset] }}> {/* email content */} </Tailwind> </Html>Props
lang:语言代码,如"en"、"es"、"fr";dir:文本方向,"ltr"或"rtl"(本地化邮件可借此支持阿拉伯语、希伯来语等从右向左语言)。
Head
存放与文档相关的头部元素(title、style、meta等)。使用 Tailwind 时必须放在<Tailwind>内部,以便抽取出的媒体查询与字体声明能正确落到此处。
import { Head } from '@react-email/components'; <Head> <title>Email Title</title> </Head>Head也是自定义字体(Font)与国际化场景的标准挂载点。
Body
包裹邮件正文内容的组件,通常在这里设置全局背景色与字体。
import { Body } from '@react-email/components'; <Body className="bg-gray-100 font-sans"> {/* email content */} </Body>按技能默认结构,Body 推荐使用font-sans py-10 bg-gray-100,为内容留出垂直呼吸空间。
Container
一个在断点处将内容水平居中的布局组件,内置37.5em的最大宽度约束(换算为像素基准约 600px,正是行业公认的邮件安全宽度)。
import { Container } from '@react-email/components'; <Container className="max-w-xl mx-auto p-5"> {/* centered content */} </Container>技能文档的"Best Practices"建议邮件整体最大宽度保持在 600px 左右,并确保移动端表现良好;Container 即承担这一职责。默认结构下 Container 通常为白色底、内容左对齐。
Section
表示一个内容分区,分区内部可再用Row/Column编排,是邮件表格布局语义化的关键组件。
import { Section } from '@react-email/components'; <Section className="p-5 bg-white"> {/* section content */} </Section>Row 与 Column
Row在水平方向分隔内容区;Column在垂直方向分隔内容区;Column必须与Row组合使用(即Row内部放多个Column)。
两者正是邮件客户端不支持 flexbox/grid 时替代多栏布局的官方方案——其底层输出为表格结构,保证 Outlook、Gmail 渲染一致。
import { Section, Row, Column } from '@react-email/components'; <Section> <Row> <Column className="w-1/2 p-2 align-top"> Left column content </Column> <Column className="w-1/2 p-2 align-top"> Right column content </Column> </Row> </Section>Column 宽度建议
- 优先使用百分比宽度类(如
w-1/2、w-1/3); - 或使用 Tailwind 宽度工具类;
- 各列宽度之和应达到 100% 或等于容器宽度,避免出现溢出或塌陷。
内容组件:填充邮件的血肉
Preview
出现在收件人收件箱列表中的预览文本,在决定打开率上非常关键。
import { Preview } from '@react-email/components'; <Preview>Welcome to our platform - Get started today!</Preview>最佳实践
- 控制在 140 字符以内;
- 文案要有吸引力、导向行动(action-oriented);
- 始终作为
<Body>内的第一个元素,确保其在 DOM 中出现位置正确、行为可预期。
Heading
标题块,支持 h1–h6 六级。
import { Heading } from '@react-email/components'; <Heading as="h1" className="text-2xl font-bold text-gray-800 mb-4"> Welcome to Acme </Heading> <Heading as="h2" className="text-xl font-semibold text-gray-600 mb-3"> Getting Started </Heading>Props
as:HTML 标题层级,取值"h1"至"h6"。
结合技能排版规范:标题使用粗体、更大字号、更大外边距;正文使用常规字重、更小字号与更小外边距,形成清晰的内容层级。
Text
以空白间隔区分的文本块,是邮件正文段落的标准载体。
import { Text } from '@react-email/components'; <Text className="text-base leading-6 text-gray-800 my-4"> Your paragraph content here. </Text>注意Text与普通<p>的差异:Text被设计为跨客户端稳定渲染的块级文本,段落间距建议通过外边距类显式控制。
Button
外观为按钮的链接,针对 Outlook 的 padding 问题内置了 workaround,是全模板最高频的 CTA 组件。
import { Button } from '@react-email/components'; <Button href="https://example.com/verify" target="_blank" className="bg-blue-600 text-white px-5 py-3 rounded block text-center no-underline font-medium" > Verify Email Address </Button>Props
href(必填):链接目标 URL;target:打开方式,默认"_blank"。
样式技巧
- 使用
block让按钮占满容器宽度成为"通栏按钮"; - 用
text-center居中按钮文字; - 加
no-underline去掉按钮文字下划线; - 按技能要求还应加上
box-border,避免内边距导致内容溢出 padding 区域。
Link
可以指向网页、mailto:邮箱地址等任何 URL 的超链接,是正文内嵌链接的常规组件。
import { Link } from '@react-email/components'; <Link href="https://example.com" target="_blank" className="text-blue-600 underline"> Visit our website </Link>Props
href(必填):链接目标;target:默认"_blank"。
Img
展示图片的组件。邮件环境比 Web 严苛得多,图片处理有一组硬性规范。
import { Img } from '@react-email/components'; <Img src="https://example.com/logo.png" alt="Company Logo" width="150" height="50" className="block mx-auto" />Props
src(必填):图片 URL,必须是绝对地址;alt(必填):无障碍替代文本;width/height:以像素为单位的宽高。
最佳实践
- 始终使用托管在 CDN 上的绝对 URL(技能要求先向用户确认生产环境静态资源地址,禁止硬编码
localhost:3000); - 始终提供 alt 文本;
- 显式指定宽高防止布局位移;
- 用
block类规避部分客户端对图片下方空隙的间距问题; - 图片文件仅支持 PNG/JPG,SVG 与 WEBP 在邮件客户端渲染不可靠,应明确提醒用户。
Hr
分隔内容区域的分割线。
import { Hr } from '@react-email/components'; <Hr className="border-gray-200 my-5" />由于邮件客户端对缩写边框(如仅写border)支持不一致,技能规范要求始终明确边框类型(border-solid、border-dashed等);当只定义单侧边框时,记得先用border-none重置其余三侧。
专用组件:代码、Markdown 与字体
CodeBlock
基于 Prism.js 渲染带主题与正则高亮的代码块,适合发送给开发者用户的"验证码/接入指引"类邮件。
import { CodeBlock, dracula } from '@react-email/components'; const Email = () => { const code = `export default async (req, res) => { try { const html = await renderAsync( EmailTemplate({ firstName: 'John' }) ); return NextResponse.json({ html }); } catch (error) { return NextResponse.json({ error }); } }`; return ( <div className="overflow-auto"> <CodeBlock fontFamily="monospace" theme={dracula} language="javascript" code={code} /> </div> ); };Props
code(必填):要渲染的实际代码,纯字符串,需自带正确的缩进;language(必填):PrismLanguage中支持的语言,如"javascript"、"python"、"typescript";theme(必填):代码块主题,从@react-email/components导入:dracula、github、nord等;fontFamily(可选):代码块字体族,如"monospace";lineNumbers(可选):是否自动显示行号,布尔值,默认false。
两条硬性要求
- 除非用户明确要求,否则不要开启
lineNumbers; - 始终用带
overflow-auto的<div>包裹CodeBlock,避免横向溢出把邮件撑破。
CodeInline
提供在所有邮件客户端表现一致的行内代码 HTML 元素。普通<code>在各客户端的默认样式千差万别,CodeInline用于消除这些差异。
import { Text, CodeInline } from '@react-email/components'; <Text className="text-base text-gray-800"> Run <CodeInline className="bg-gray-100 px-1 rounded">npm install</CodeInline> to get started. </Text>Markdown
将 Markdown 字符串转换为合法的 React Email 模板代码,适合内容以文案为主的邮件(如月刊、公告)。
import { Html, Markdown } from '@react-email/components'; const Email = () => { return ( <Html lang="en" dir="ltr"> <Markdown markdownCustomStyles={{ h1: { color: "red" }, h2: { color: "blue" }, codeInline: { background: "grey" }, }} markdownContainerStyles={{ padding: "12px", border: "solid 1px black", }} >{`# Hello, World!`}</Markdown> {/* OR */} <Markdown children={`# This is a ~~strikethrough~~`} /> </Html> ); };Props
children(必填):Markdown 字符串;markdownCustomStyles:对转换后 HTML 元素(h1、h2、p、a、codeInline等)的样式覆写;markdownContainerStyles:容器div的样式。
以上三种调用形态(children 模板字符串、childrenprop 传入)均等价,可按可读性选择。
Font
声明邮件中使用的自定义网络字体。邮件客户端对自定义字体支持有限,因此必须提供可靠的fallbackFontFamily。
import { Head, Font } from '@react-email/components'; <Head> <Font fontFamily="Roboto" fallbackFontFamily="Arial, sans-serif" webFont={{ url: "https://fonts.gstatic.com/s/roboto/v27/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2", format: "woff2" }} /> </Head>Props
fontFamily(必填):字体族名称;fallbackFontFamily:加载失败时的回退字体栈;webFont:含url与format的对象。
支持的字体格式
woff2(推荐,体积最小、支持最广)wofftruetypeopentype
模板骨架与 Novu 工作流的衔接
把组件串起来,就得到技能文档中的标准邮件骨架——这一结构与 Novu 官方推荐的用法完全一致。在 Novu Framework 的 React Email 集成文档 中,模板的编写与发送链路是:
- 安装依赖:
npm install @react-email/components react-email; - 编写邮件组件,并通过
render()导出 HTML 渲染函数,例如文档中的render(<TestEmailTemplate name={name} />); - 在 workflow 的
step.email()中把渲染结果赋给body,通过controlSchema/payloadSchema声明主题、用户姓名等变量,交给 Novu 完成实际投递。
因此本文所有组件知识都可直接迁移到 Novu 邮件工作流模板中:模板内的{{}}占位符类变量不应硬编码进 JSX(技能规范要求直接引用 props 字段,把占位符值放入PreviewProps便于本地预览测试),真正需要动态化的数据则通过 workflow 的 payload/controls 注入。
一个可复制的组合骨架(同时体现组件顺序与 Tailwind 用法):
import { Html, Head, Preview, Body, Container, Heading, Text, Button, Tailwind, pixelBasedPreset } from '@react-email/components'; interface WelcomeEmailProps { name: string; verificationUrl: string; } export default function WelcomeEmail({ name, verificationUrl }: WelcomeEmailProps) { return ( <Html lang="en"> <Tailwind config={{ presets: [pixelBasedPreset] }}> <Head /> <Preview>Welcome - Verify your email</Preview> <Body className="bg-gray-100 font-sans"> <Container className="max-w-xl mx-auto p-5"> <Heading as="h1" className="text-2xl text-gray-800">Welcome!</Heading> <Text className="text-base text-gray-800">Hi {name}, thanks for signing up!</Text> <Button href={verificationUrl} className="bg-blue-600 text-white px-5 py-3 rounded block text-center no-underline box-border" > Verify Email </Button> </Container> </Body> </Tailwind> </Html> ); }组件选型与邮件最佳实践速查
选型建议:
- 需要页面级骨架:
Html→Tailwind→Head→Preview→Body; - 需要单栏居中内容:
Container; - 需要分块分区:
Section;需要两/三栏:Section+Row+Column,宽度用百分比; - 需要 CTA:
Button;正文链接:Link;正文段落:Text;标题层级:Heading; - 需要呈现代码:
CodeBlock(配overflow-auto容器)/CodeInline; - 需要自定义字体:
Head内的Font; - 需要大段文案驱动的邮件:
Markdown。
发送前的通用检查(综合自技能参考与本文组件行为):
- 跨客户端测试(Gmail、Outlook、Apple Mail、Yahoo Mail),用 Litmus / Email on Acid 等做精确验证;
- 保持响应式,主内容宽度 ≤ 600px,并测试移动端;
- 图片一律绝对 URL + CDN + alt 文本;
- 提供纯文本版本(
render(..., { plainText: true })),兼顾无障碍与部分客户端; - 单封邮件体积控制在 102KB 以内,超出会被 Gmail 截断;
- 为所有组件 props 定义 TypeScript 接口,并补
.PreviewProps便于开发期预览; - 生产环境
from地址使用已验证域名,发送时检查返回的error。
如需更贴近真实业务的组合模板(密码重置、订单确认、多栏布局、自定义字体邮件等),可继续阅读仓库内的 PATTERNS.md;涉及多语言时参照 I18N.md;要接入实际发送链路则参照 SENDING.md。三者与本文共同构成一套从"组件认知"到"工程落地"的完整邮件开发体系。
【免费下载链接】novuThe open-source communication infrastructure for agents and products项目地址: https://gitcode.com/GitHub_Trending/no/novu
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考