PAYATHON 2026

React/Next.js 中如何保存并渲染 Lexical 富文本内容?

数据小陈

可以继续将 Lexical EditorState 的 JSON 表示作为主要存储格式,展示时通过与节点结构匹配的渲染器转换成 React 元素。等有明确的 Markdown 或 HTML 消费需求时,再生成相应格式。

针对这个需求,可以参考 PayloadCMS 的 JSON → JSX 方案,不必为了避免使用 dangerouslySetInnerHTML 而额外增加 Markdown 转换。

为什么优先保存 Lexical JSON?

Lexical JSON 能保留编辑器的文档结构,方便重新加载和继续编辑。Lexical 提供 editorState.toJSON() 序列化状态,也提供 editor.parseEditorState() 读取序列化状态;具体节点仍需与编辑器配置兼容。Lexical 序列化文档

保存时,可以先调用下面的函数,再将返回值交给项目已有的保存接口:

import type { EditorState } from 'lexical';

export function serializeContent(editorState: EditorState) {
  return editorState.toJSON();
}

如果数据库支持 JSON 字段,可以直接保存这个对象;如果使用文本字段,则通过 JSON.stringify() 将它转成字符串。

不建议将 JSON 和 Markdown 都作为可独立修改的正文来源,以免两份内容不一致。需要导出 Markdown 或缓存 HTML 时,应明确它们由哪份源数据生成,并在源数据变化后更新。

JSON 如何变成 React 页面?

渲染器遍历文档树,把支持的节点映射到固定的 React 元素或组件,例如:

  • 段落节点映射为 <p>。
  • 文本节点作为 JSX 文本输出,并根据格式信息处理加粗、斜体等样式。
  • 链接节点经过 URL 检查后映射为 <a>。
  • 图片和自定义内容块映射到项目自己的组件。

这里的 JSX 指应用代码创建的 React 元素,用户内容无需作为 JavaScript 编译或执行。

如果项目已经使用 Payload 的 Lexical 富文本数据,并且所安装版本提供相应接口,可以使用其 RichText 组件:

import { RichText } from '@payloadcms/richtext-lexical/react';
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical';

type Props = {
  data: SerializedEditorState;
};

export function ArticleBody({ data }: Props) {
  return <RichText data={data} />;
}

Payload 的组件提供常见节点的转换器。自定义节点和内容块需要补充 converters;内部链接也可能需要提供从文档到 URL 的映射。Payload JSX 转换文档

如果独立使用 Lexical,可以用同样的节点映射方式实现展示层,也可以选择覆盖项目所用节点类型的现有渲染器。任意 Lexical JSON 不一定与 Payload 的节点结构兼容,也不必仅为展示内容引入整套 CMS。

Markdown 和 HTML 分别适合什么情况?

内容本身以 Markdown 为目标格式

如果编辑功能限定在目标 Markdown 方言及转换器能够表达的范围内,可以使用 Markdown 配合 react-markdown:

import Markdown from 'react-markdown';

export function MarkdownBody({ content }: { content: string }) {
  return <Markdown skipHtml>{content}</Markdown>;
}

这个示例会忽略 Markdown 中的原始 HTML。react-markdown 默认具有安全保护,但自定义 components、插件或不安全的 urlTransform 都可能改变其安全边界。如果需要解析原始 HTML,应结合清洗策略,例如在 rehype-raw 之后使用 rehype-sanitize,并检查清洗后的处理是否会重新引入危险内容。react-markdown 安全说明

Markdown 的表达能力也有限。自定义内容块、复杂布局或某些格式,如果没有对应的语法和转换规则,转换时就可能丢失。是否采用 Markdown,应根据实际节点集合判断。

下游明确需要 HTML

邮件、传统模板或其他系统需要 HTML 时,可以从 Lexical 内容生成 HTML。经过可信清洗流程处理的 HTML 可以使用 dangerouslySetInnerHTML 展示;直接将不可信内容作为 HTML 插入页面会带来风险。React 官方说明

从编辑器导出 HTML 并不能自动证明输出安全。需要按业务允许的标签、属性和 URL 协议处理内容,尤其要检查自定义节点的导出逻辑。

实施时需要注意什么?

  1. TypeScript 类型不能验证数据库或接口中的实际 JSON。保存或渲染前,应检查节点类型、字段、文档大小和嵌套深度。
  2. 不要根据输入动态选择任意组件,也不要将节点的所有字段直接展开为 JSX 属性。链接、图片和嵌入内容需要各自的校验规则。
  3. 编辑器启用的节点应有对应的展示逻辑。升级编辑器或修改自定义节点时,需要考虑历史内容的兼容与迁移。
  4. 在 Next.js 中,不依赖浏览器 API 的展示逻辑可以考虑放在服务端;编辑器和需要交互的组件则按项目路由模式划分边界。只读页面通常无需初始化完整编辑器。
  5. 用项目实际支持的列表、链接、格式化文本和自定义节点,检查保存、重新编辑和展示过程,确认没有丢失信息。

备注:内容仅供参考。