React Bootstrap logoReact Bootstrap

RichTextEditor

基础表单编辑器

基于 Bootstrap 5 与内置无框架富文本内核的编辑器组件,提供文本格式、段落与对齐、缩进与行高、引用与代码块、链接与图片、媒体与表格、表情与符号、日期与自定义卡片等 40 余项功能,支持撤销重做、剪切复制粘贴、粘贴为纯文本、全选、查找替换与格式刷,并提供完整快捷键、受控用法、命令式句柄与扩展机制

Component Demo

Basic usage and examples of the RichTextEditor component

基础用法

展示用示例隐藏了工具栏以便阅读;实际使用时工具栏默认渲染在正文上方,包含文本格式、 段落对齐、缩进、行高、引用、代码块、分割线、插入与编辑操作共 40 余项功能

文本格式与段落

区块类型下拉可在正文与 H1–H6 之间切换,行高与段间距面板同时提供代码块语言选择; 工具栏的每一项都是图标,名称与快捷键显示在悬浮提示中,选中面板里的选项后面板随即收起

插入内容

图片支持 URL、本地上传与图库三种来源;表情、符号、日期以原子节点写入文档, 保留名称与时间戳以便检索与再次格式化。媒体面板支持 iframe 地址或嵌入代码;平台视频使用 iframe,视频文件使用原生播放器。图片与媒体均带常驻焦点工具栏

编辑操作

查找替换支持区分大小写、逐个替换与全部替换;格式刷先取样当前选区格式,再刷到目标选区

受控与命令式调用

组件内部持有唯一的编辑器实例,重复渲染不会丢失文档、撤销历史与光标位置

只读与无工具栏

JSON 自定义视图与帖子详情

编辑内容

社区精选 · 作者小林 · 5 分钟阅读

把写作内容变成帖子详情

正文来自编辑器导出的 JSON,展示方式由业务决定。

推荐帖子延伸阅读:如何设计内容展示页
12 人觉得有帮助 · 3 条讨论

使用 editor.getJSON() 导出内容,交给 RichTextRenderer 展示。 默认支持正文、列表、表格、媒体与文字格式,无需创建编辑器实例。nodeRenderers 与 markRenderers 可接入业务组件; 回调提供 children、path 与 defaultRender(), 返回 undefined 继续回退,返回 null 隐藏内容。未知节点与格式默认保留子内容。 存储的 HTML 不会直接执行,嵌入代码和业务卡片可通过自定义回调展示。 需要完全自定义外层布局时,可直接调用 renderRichTextContent(content, options)。 自定义回调中的 URL 与 HTML 处理由业务负责。

自定义与内核直用

组件内部按 StarterKit + MediaKit + TableKit + extendedKit 组合扩展; 传入 extensions 即可完全接管节点、命令与快捷键。需要自定义视图规则、 节点视图或嵌入非 React 宿主页面时,可以直接使用无框架的内核。

内置扩展节点包含 emoji、symbol、date、embed 与 custom_block,并导出对应的命令、DOM 规则与节点名。

快捷键

功能快捷键
加粗Ctrl + B
斜体Ctrl + I
下划线Ctrl + U
删除线Ctrl + Shift + X
行内代码Ctrl + E
撤销 / 重做Ctrl + Z / Ctrl + Shift + Z
全选Ctrl + A
插入链接Ctrl + K
查找替换Ctrl + F
粘贴为纯文本Ctrl + Shift + V
格式刷Ctrl + Shift + C
换行不分段Shift + Enter

工具栏控件在按下时会取消 pointerdown 默认行为,因此命令始终作用于用户选中的 区间;弹窗打开时记录选区,确认后恢复选区再执行命令,一次操作对应一步撤销

API Documentation

Complete API reference for the RichTextEditor component

Props

RichTextRenderer

属性名类型默认值描述
contentNodeJSON | null-editor.getJSON() 或 ref.getJSON() 导出的文档;为空时不渲染正文
nodeRenderersRecord<string, RichTextNodeRenderer>-按节点类型替换视图,如 heading、image、custom_block;优先于 renderNode
markRenderersRecord<string, RichTextMarkRenderer>-按文字格式类型替换视图,如 bold、link、highlight;优先于 renderMark
renderNodeRichTextNodeRenderer-全局节点渲染回调;返回 undefined 使用默认视图,返回 null 隐藏节点
renderMarkRichTextMarkRenderer-全局格式渲染回调;返回 undefined 使用默认视图,返回 null 隐藏格式及其内容
...restHTMLAttributes<HTMLDivElement>-容器原生属性,支持 className、style、aria-* 和事件;children 和 dangerouslySetInnerHTML 由 JSON 渲染管理

RichTextEditor

属性名类型默认值描述
classNamestring-自定义类名,作用于编辑器根元素
contentNodeJSON | string-初始文档,支持 HTML 字符串或 JSON 文档;挂载后修改该属性会替换整篇文档,未修改时不会干扰用户输入
editablebooleantrue是否可编辑;设为 `false` 时正文只读,工具栏按钮全部禁用
extensionsExtension[]StarterKit + MediaKit + TableKit + extendedKit编辑器扩展列表,传入后完全覆盖默认组合;节点、命令与快捷键都由该列表决定
galleryRichTextEditorGalleryGroup[][]图片弹窗「图库」页签展示的图片分组
focusToolbarfalse | ((context: RichTextEditorFocusToolbarContext) => ReactNode)图片、媒体与自定义卡片工具栏常驻于叶子节点上方的焦点工具栏,独立占位、不覆盖内容。接收节点、位置及 update/remove 操作;返回 null 忽略该节点,设为 false 禁用
hideToolbarbooleanfalse是否隐藏工具栏,仅保留可编辑正文
localeEditorLocale-写入 DOM 的文案(编辑区无障碍名称、`translate` 等)
onChange(editor: Editor) => void-文档变化后触发,入参为编辑器实例
onHTMLChange(html: string) => void-文档变化后触发,入参为 HTML 字符串,便于受控表单直接保存
placeholderstring-空文档时展示的占位文案
toolbarExtraReactNode-追加到工具栏末尾的额外控件
upload{ file?: FileUploader; image?: FileUploader }-上传适配器,`image` 用于图片、`file` 用于附件;未提供时图片以本地 data URL 嵌入。上传状态显示在正文下方,成功后隐藏
...restHTMLAttributes-根元素的所有原生属性(如 `style`、`aria-*` 等)

ref

属性名类型默认值描述
refRef<RichTextEditorHandle>-通过 `useRef<RichTextEditorHandle>` 读取文档、替换文档、撤销重做与聚焦

ToolbarButton

属性名类型默认值描述
titlestring-无障碍名称,同时作为 tooltip 文本
activebooleanfalse是否为激活态,对应 `aria-pressed` 与 Bootstrap 的 `active` 类
keepFocusbooleantrue按下时是否保持编辑器选区;默认 `true`,控件会取消 `pointerdown` 默认行为以免光标丢失
shortcutstring-tooltip 中追加的快捷键提示

ToolbarDropdown

属性名类型默认值描述
openboolean-面板是否展开,组件为受控模式
onOpenChange(open: boolean) => void-面板开合回调
placement'bottom-start' | 'bottom-end' | 'top-start''bottom-start'面板优先出现的位置;超出视口时自动翻转
widthnumber | string-面板宽度,数字按像素处理

ToolbarMenuItem

属性名类型默认值描述
labelReactNode-行标题内容
activebooleanfalse是否为当前值
onSelect() => void-点击回调
iconReactNode-行首图标
hintReactNode-行尾提示,例如快捷键或当前值

ColorPicker

属性名类型默认值描述
groupsRichTextEditorColorGroup[]-色板分组,来自 `TEXT_COLOR_GROUPS` / `HIGHLIGHT_COLOR_GROUPS` 或自定义
onChange(color: string) => void-选择颜色回调;传入空字符串表示清除该格式
valuenull | string-当前颜色,用于高亮选中的色块

Type Definitions

RichTextRendererProps / RichTextRenderOptions

JSON 展示组件、渲染函数与自定义节点/格式回调接口

type RichTextMarkJSON = NonNullable<NodeJSON['marks']>[number];

interface RichTextNodeRenderContext {
  node: NodeJSON;
  parent: NodeJSON | null;
  path: readonly number[];
  children: ReactNode;
  defaultRender: () => ReactNode;
}

interface RichTextMarkRenderContext {
  mark: RichTextMarkJSON;
  node: NodeJSON;
  path: readonly number[];
  children: ReactNode;
  defaultRender: () => ReactNode;
}

type RichTextNodeRenderer = (context: RichTextNodeRenderContext) => ReactNode;
type RichTextMarkRenderer = (context: RichTextMarkRenderContext) => ReactNode;

interface RichTextRenderOptions {
  nodeRenderers?: Readonly<Record<string, RichTextNodeRenderer>>;
  markRenderers?: Readonly<Record<string, RichTextMarkRenderer>>;
  renderNode?: RichTextNodeRenderer;
  renderMark?: RichTextMarkRenderer;
}

interface RichTextRendererProps
  extends
    Omit<HTMLAttributes<HTMLDivElement>, 'children' | 'content' | 'dangerouslySetInnerHTML'>,
    RichTextRenderOptions {
  content?: NodeJSON | null;
}

function renderRichTextContent(
  content: NodeJSON | null | undefined,
  options?: RichTextRenderOptions,
): ReactNode;

RichTextEditorProps

富文本编辑器组件属性接口

export interface RichTextEditorProps extends Omit<
  HTMLAttributes<HTMLDivElement>,
  'content' | 'defaultValue' | 'onChange'
> {
  className?: string;
  content?: NodeJSON | string;
  editable?: boolean;
  extensions?: Extension[];
  gallery?: RichTextEditorGalleryGroup[];
  hideToolbar?: boolean;
  locale?: EditorLocale;
  onChange?: (editor: Editor) => void;
  onHTMLChange?: (html: string) => void;
  placeholder?: string;
  toolbarExtra?: ReactNode;
  focusToolbar?: false | ((context: RichTextEditorFocusToolbarContext) => ReactNode);
  upload?: { file?: FileUploader; image?: FileUploader };
}

RichTextEditorHandle

通过 `ref` 暴露的命令式句柄

export interface RichTextEditorHandle {
  focus: () => void;
  getEditor: () => Editor;
  getHTML: () => string;
  getJSON: () => NodeJSON;
  getMarkdown: () => string;
  getText: () => string;
  getView: () => EditorView | null;
  redo: () => void;
  setHTML: (html: string) => void;
  setJSON: (content: NodeJSON) => void;
  undo: () => void;
}

RichTextEditorContextValue

上下文值,可通过 `useRichTextEditor` 读取,自定义工具条按钮依赖它

export interface RichTextEditorContextValue {
  can: (name: string, options?: Attrs) => boolean;
  editable: boolean;
  editor: Editor;
  getAttributes: (name: string) => Attrs;
  isActive: (name: string, attrs?: Attrs) => boolean;
  run: (name: string, options?: Attrs) => boolean;
  state: EditorState;
}

RichTextEditorFocusToolbarContext

通用节点焦点工具栏上下文,图片默认接入,可扩展其他节点

export interface RichTextEditorFocusToolbarContext {
  editor: Editor;
  node: NodeJSON;
  position: number;
  remove: () => boolean;
  update: (attrs: Attrs) => boolean;
}

RichTextEditorGalleryGroup

图库分组与图片描述

export interface RichTextEditorGalleryGroup {
  images: RichTextEditorImage[];
  label: string;
}

export interface RichTextEditorImage {
  alt?: string;
  height?: number;
  id: string;
  src: string;
  title?: string;
  width?: number;
}

RichTextEditorPanel

工具栏同时只会展开一个面板,面板标识即为此类型

export type RichTextEditorPanel =
  | 'code-language'
  | 'block-type'
  | 'color'
  | 'datetime'
  | 'emoji'
  | 'font-family'
  | 'font-size'
  | 'highlight'
  | 'insert'
  | 'line-height'
  | 'spacing'
  | 'symbol'
  | null;