如何用 EmailNode 的 renderToReactEmail 为 React Email Editor 创建自定义邮件节点

发布时间:2026/9/14 2:43:33

如何用 EmailNode 的 renderToReactEmail 为 React Email Editor 创建自定义邮件节点 如何用 EmailNode 的 renderToReactEmail 为 React Email Editor 创建自定义邮件节点【免费下载链接】react-email Build and send emails using React项目地址: https://gitcode.com/GitHub_Trending/re/react-email这篇文章解决的问题是在 react-email 的react-email/editor基于 TipTap 的邮件编辑器中内置的StarterKit只覆盖了一组邮件场景需要的节点当你需要一种内置节点之外的结构——比如带高亮底色的 Callout 提示块——并且希望它在编辑器里能输入、能粘贴识别、还能在导出时序列化成邮件 HTML就需要用EmailNode创建一个自定义扩展并实现其中唯一的新增必选方法renderToReactEmail()。完成后的结果是编辑器里可以插入自定义节点调用composeReactEmail导出时该节点会走你的renderToReactEmail渲染逻辑出现在最终html输出中。适用前提来自编辑器文档的 Prerequisites 说明React 18使用支持 package exports 的打包器Vite、Next.js、Webpack 5 等react-email/editor包要求 Node 20见 packages/editor/package.json 的engines字段。安装编辑器包npm install react-email/editor文档同时给出 yarn / pnpm / bun 的等价命令yarn add/pnpm add/bun add react-email/editor。本文后续都基于低层级路径EditorProvider 扩展数组因为自定义节点必须显式加入 extensions 数组如果你使用独立的EmailEditor组件文档建议改走低层级路径来获得对扩展列表的完全控制见 getting-started.mdx 的 Lower-level setup。EmailNode 与 renderToReactEmail 的工作机制EmailNode扩展了 TipTap 的Node类额外要求一个renderToReactEmail()方法它决定节点在通过composeReactEmail导出为邮件 HTML 时如何被序列化见 email-node.mdx。创建扩展的配置对象接受所有标准 TipTap Node 选项加上renderToReactEmail。文档中三个方法的分工parseHTML()识别粘贴或导入的 HTML让已有内容能被解析回该节点renderHTML()控制节点在编辑器内的外观renderToReactEmail()控制导出时该节点渲染成什么 React Email 结构。renderToReactEmail收到的 props来自 email-node.mdx 的 props 表PropType说明childrenReact.ReactNode该节点序列化后的子内容styleReact.CSSProperties该节点解析后的主题样式无主题时为空对象nodeNodeProseMirror 节点实例extensionEmailNode扩展实例可用于访问options导出的样式解析优先级高者胜出来自 compose-react-email.mdx节点属性上的内联样式当前主题通过getNodeStyles()提供的主题样式各扩展renderToReactEmail()中写死的默认样式。因此在渲染器中通常是{...style}展开主题样式再叠加固定样式。一个关键限制composeReactEmail遍历文档时未注册、或不是EmailNode的节点类型会渲染为null。所以自定义节点必须用EmailNode.create或EmailNode.from创建并注册否则导出时会直接消失。创建自定义 Callout 节点下面是文档给出的完整示例一个渲染为高亮块的 Callout 节点示例代码来自 custom-extensions.mdximport { EmailNode } from react-email/editor/core; import { mergeAttributes } from tiptap/core; const Callout EmailNode.create({ name: callout, group: block, content: inline*, parseHTML() { return [{ tag: div[data-callout] }]; }, renderHTML({ HTMLAttributes }) { return [ div, mergeAttributes(HTMLAttributes, { data-callout: , style: padding: 12px 16px; background: #f4f4f5; border-left: 3px solid #1c1c1c; border-radius: 4px; margin: 8px 0;, }), 0, ]; }, renderToReactEmail({ children, style }) { return ( div style{{ ...style, padding: 12px 16px, backgroundColor: #f4f4f5, borderLeft: 3px solid #1c1c1c, borderRadius: 4px, margin: 8px 0, }} {children} /div ); }, });三个方法要分别保持一致parseHTML用div[data-callout]作为导入识别特征renderHTML输出相同的data-callout属性和编辑器内样式renderToReactEmail则把同样的视觉效果用 React 样式对象写出供render()生成邮件 HTML。注册扩展并插入自定义节点把自定义扩展加入 extensions 数组与StarterKit并列const extensions [StarterKit, Callout];程序化插入使用编辑器的insertContent命令。文档示例是一个工具栏按钮import { useCurrentEditor } from tiptap/react; function Toolbar() { const { editor } useCurrentEditor(); if (!editor) return null; return ( button onClick{() editor .chain() .focus() .insertContent({ type: callout, content: [{ type: text, text: New callout }], }) .run() } Insert Callout /button ); }完整编辑器组合把节点、工具栏和 BubbleMenu 组合进EditorProvider同样来自 custom-extensions.mdximport { EmailNode } from react-email/editor/core; import { StarterKit } from react-email/editor/extensions; import { BubbleMenu } from react-email/editor/ui; import { mergeAttributes } from tiptap/core; import { EditorProvider, useCurrentEditor } from tiptap/react; import { Info } from lucide-react; const Callout EmailNode.create({ name: callout, group: block, content: inline*, parseHTML() { return [{ tag: div[data-callout] }]; }, renderHTML({ HTMLAttributes }) { return [ div, mergeAttributes(HTMLAttributes, { data-callout: , style: padding: 12px 16px; background: #f4f4f5; border-left: 3px solid #1c1c1c; border-radius: 4px; margin: 8px 0;, }), 0, ]; }, renderToReactEmail({ children, style }) { return ( div style{{ ...style, padding: 12px 16px, backgroundColor: #f4f4f5, borderLeft: 3px solid #1c1c1c, borderRadius: 4px, margin: 8px 0, }} {children} /div ); }, }); const extensions [StarterKit, Callout]; const content { type: doc, content: [ { type: paragraph, content: [ { type: text, text: This editor includes a custom Callout node. Use the toolbar to insert one., }, ], }, { type: callout, content: [ { type: text, text: This is a callout block — a custom extension! }, ], }, ], }; function Toolbar() { const { editor } useCurrentEditor(); if (!editor) return null; return ( button onClick{() editor .chain() .focus() .insertContent({ type: callout, content: [{ type: text, text: New callout }], }) .run() } Info size{16} / Insert Callout /button ); } export function MyEditor() { return ( EditorProvider extensions{extensions} content{content} slotBefore{Toolbar /} BubbleMenu / /EditorProvider ); }注意content用的是 TipTap JSON 格式编辑器同时支持 HTML 字符串与 TipTap JSON 两种初始内容格式见 getting-started.mdx 的 Content format。使用低层级 UI 组件时记得导入主题 CSS例如import react-email/editor/themes/default.css;否则编辑器无样式。验证导出结果自定义节点是否真正参与邮件序列化以导出输出为准。composeReactEmail的签名与返回值来自 compose-react-email.mdximport { composeReactEmail } from react-email/editor/core; async function composeReactEmail(params: { editor: Editor; preview: string | null; }): Promise{ html: string; text: string };它读取editor.getJSON()遍历每个节点和 mark调用各扩展的renderToReactEmail()应用主题样式包上邮件模板BaseTemplate最后用 react-email 的render()同时产出html与纯文本text。文档给出的带导出面板的验证方式示例结果见 email-export.mdximport { composeReactEmail } from react-email/editor/core; import { useCurrentEditor } from tiptap/react; import { useState } from react; function ExportPanel() { const { editor } useCurrentEditor(); const [html, setHtml] useState(); const [exporting, setExporting] useState(false); const handleExport async () { if (!editor) return; setExporting(true); const result await composeReactEmail({ editor, preview: null }); setHtml(result.html); setExporting(false); }; return ( div button onClick{handleExport} disabled{exporting} {exporting ? Exporting... : Export HTML} /button {html ( textarea readOnly value{html} rows{16} style{{ width: 100%, fontFamily: monospace }} / )} /div ); }把ExportPanel作为EditorProvider的子组件与Toolbar一样需要在 provider 内使用useCurrentEditor。导出后的检查点html中应出现 Callout 节点renderToReactEmail写出的结构与内联样式如padding: 12px 16px、border-left: 3px solid #1c1c1c并且内容被BaseTemplate包裹默认模板包含 viewport meta 与Preview占位若文档中存在未注册的节点类型它们在html中不会有任何输出渲染为null这是排查导出后内容缺失的依据如果 extensions 数组里配置了EmailTheming主题样式会自动注入到每个节点没有SerializerPlugin时style默认为空对象、使用内置DefaultBaseTemplate。复用已有 TipTap 扩展可选分支不想从零写节点时EmailNode.from()可以把现有 TipTap 节点包一层邮件序列化能力第二个参数就是renderToReactEmail渲染器示例同样来自 custom-extensions.mdximport { EmailNode } from react-email/editor/core; import { Node } from tiptap/core; const MyTipTapNode Node.create({ /* ... */ }); const MyEmailNode EmailNode.from(MyTipTapNode, ({ children, style }) { return div style{style}{children}/div; });EmailMark.from是 mark行内样式如高亮的等价方法。EmailNode与EmailMark同时支持 TipTap 标准的.configure()和.extend()而且.extend时也可以直接覆盖renderToReactEmail例如文档示例中给Paragraph加键盘快捷键的同时重写其导出渲染const CustomParagraph Paragraph.extend({ renderToReactEmail({ children, style }) { return p style{{ ...style, lineHeight: 1.8 }}{children}/p; }, });边界与注意事项自定义节点必须走EditorProvider 显式 extensions 数组这条路renderToReactEmail只在composeReactEmail及EmailEditorref 的getEmailHTML/getEmailText/getEmail三者底层都是它导出时被调用。styleprop 在无主题时是空对象主题样式、内联样式与扩展默认样式的合并优先级以第 3 节列表为准渲染器里先展开...style再叠加固定样式是文档给出的合并方式。depth只在列表节点bulletList、orderedList内递增用于区分嵌套段落listParagraph与顶层段落paragraph的主题键如果你的自定义节点只在列表外使用这一点不影响其渲染。文档中的 EmailNode 参考 与 composeReactEmail 参考 给出了create/from/configure/extend的完整 API 细节可作为扩展开发时的查阅入口。【免费下载链接】react-email Build and send emails using React项目地址: https://gitcode.com/GitHub_Trending/re/react-email创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/14 2:43:33

STM32 AI开发流程重构:从代码生成到全链路闭环

1. 这不是“用AI写代码”,而是重构嵌入式开发的底层逻辑“嵌入式软件AI编程”这个标题,很多人第一反应是:让ChatGPT生成几行GPIO初始化代码,再复制粘贴进Keil里编译——这根本不是AI编程,这只是AI代打。我带过27个嵌入…

2026/9/14 2:43:33

Linux嵌入式驱动系统路径:设备树、I2C/CAN与内核模块协同原理

1. 这不是“写个驱动就完事”的事——它是一条贯穿Linux嵌入式系统的完整技术链路你手头有一块RK3568开发板,芯片手册里写着I2C0接了温湿度传感器,CAN总线挂了电机控制器,但ls /dev里啥也没有;你照着《Linux设备驱动开发详解》第3…

2026/9/14 3:28:35

2026年AI学术写作工具五大旗舰解析与应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/14 3:28:35

Python+OpenCV轻量级车道线检测系统开发实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/14 3:28:35

ThinkPHP与Laravel开发流浪动物救助平台实战

1. 项目概述与背景分析这个基于ThinkPHP和Laravel框架开发的流浪动物救助网站,本质上是一个连接爱心人士与流浪动物的数字化平台。作为从业十余年的全栈开发者,我见过太多类似的公益项目因为技术选型不当而中途夭折。这次我们选择PHP生态中这两个最成熟的…

2026/9/14 3:28:35

Llama 3大模型架构解析与本地部署实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/14 3:23:34

微信小程序+Python校园自动点餐与跑腿系统开发实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/14 2:17:50

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/14 0:03:22

KCF目标跟踪算法与OTB工程实现:毕业设计实战解析

简介:这是一份基于KCF核相关滤波算法、融合尺度池与抗遮挡处理的目标检测跟踪MATLAB完整源码,主要面向计算机相关专业准备毕业设计、课程设计或期末大作业的学生,也适合需要项目实战练习的初学者。源码在OTB数据集上完成验证,能够…

2026/9/14 0:03:22

语音情感识别实战:Keras实现LSTM、CNN、SVM与MLP多模型对比

简介:面向语音情感识别入门与进阶开发者,这份基于Keras的项目源码完整实现了LSTM、CNN、SVM、MLP四种模型,兼容Python3.8与Keras/TensorFlow2环境。压缩包内含49个文件,大小约70.31MB,主体包括Python脚本、yaml/json配…

2026/9/12 6:29:36

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/12 14:32:17

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/13 11:18:28

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码