一个基于 React 的 Markdown 渲染组件,支持 GFM(GitHub Flavored Markdown)Tabs 标签、代码高亮、Mermaid 图表等功能。
| 类别 | 技术选型 | 说明 |
|---|---|---|
| 框架 | React 18 | 组件库基础框架 |
| 构建工具 | Vite | 基于 Rollup 的极速构建器 |
| 语言 | TypeScript | 提供类型定义 |
| 样式方案 | CSS Modules / 内联样式 | 轻量无外部依赖 |
| 代码规范 | ESLint + Prettier | 统一代码风格 |
| 发布工具 | changesets | 语义化版本管理与 changelog 生成 |
- GFM 支持: 完整的 GitHub Flavored Markdown 渲染
- 增量渲染: 基于 morphdom 的 DOM 差异化更新,适用于流式输出场景
- 代码高亮: 基于 highlight.js 的代码语法高亮
- Mermaid 图表: 支持 Mermaid 语法渲染流程图、时序图等
- Tab 标签页: 支持 Tab 标签页语法
- Alert 提示框: 支持警告、提示等信息框
- 目录锚点: 自动生成文档目录和锚点链接(自行实现目录渲染)
- 代码复制: 一键复制代码块
- 代码折叠: 支持代码块折叠展开
- 深色模式: 自动适配深色/浅色主题
npm install remons-render-markdownimport RenderMarkdown, { markdownFormat, languagesCommon, initHighlighter, useIncrementalRender } from 'remons-render-markdown';- RenderMarkdown: React 组件,用于渲染 Markdown 内容
- markdownFormat: Markdown 解析函数,返回
{ anchor: AnchorItem[], info: string } - languagesCommon: 默认支持的语言包配置(javascript, typescript, css, json, bash, xml, plaintext)
- initHighlighter: 初始化高亮语言包的函数
- useIncrementalRender: 增量渲染 hook,使用 morphdom 进行 DOM 差异化更新,适用于流式输出场景
- renderMermaid: 独立的 Mermaid 图表渲染组件
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| content | string | ✅ | - | Markdown 内容 |
| createTime | string | ❌ | - | 创建时间,用于显示文档更新时间 |
| isSlotMermaid | boolean | ❌ | true | 是否使用 Mermaid 插件渲染图表 |
| isShowCollapsed | boolean | ❌ | true | 是否显示代码折叠按钮 |
| codeType | string | ❌ | - | 指定代码类型(如 'javascript' 等),不传则按 Markdown 渲染 |
| editButton | React.ReactNode | ❌ | - | 自定义编辑按钮 |
| backTopTarget | HTMLElement | ❌ | body | 返回顶部按钮监听的容器 |
| showDriverGuide | boolean | ❌ | false | 是否显示新手引导 |
| showToc | boolean | ❌ | false | 是否显示目录(TOC)按钮和侧边栏 |
解析 Markdown 内容并返回结构化数据。
参数:
content: string- Markdown 文本内容
返回值:
{
anchor: Array<{
title: string; // 标题文本
href: string; // 锚点链接
nodeName: string; // 节点名称,如 "H1", "H2"
nodeTitle: string; // 完整节点 HTML
children: Array<...>; // 子标题
}>;
info: string; // 渲染后的 HTML 字符串
}注册自定义的代码高亮语言。
参数:
languages: Record<string, any>- 语言包对象,格式为{ languageName: languageModule }
示例:
import javascript from 'highlight.js/lib/languages/javascript';
import python from 'highlight.js/lib/languages/python';
initHighlighter({
javascript,
python,
});import RenderMarkdown from 'remons-render-markdown';
import 'render-markdown/dist/index.css';
function App() {
const markdownContent = `
# Hello World
This is a **Markdown** renderer.
\`\`\`javascript
console.log('Hello');
\`\`\`
`;
return <RenderMarkdown content={markdownContent} />;
}import RenderMarkdown from 'remons-render-markdown';
import 'render-markdown/dist/index.css';
function ArticlePage() {
const content = `
# 文章标题
这是一篇示例文章。
## 代码示例
\`\`\`javascript
const greeting = 'Hello World';
console.log(greeting);
\`\`\`
## Mermaid 图表
\`\`\`mermaid
graph TD
A[开始] --> B[处理]
B --> C[结束]
\`\`\`
`;
return (
<RenderMarkdown
content={content}
createTime="2026-07-23T12:00:00Z"
isSlotMermaid={true}
isShowCollapsed={true}
editButton={<button>编辑</button>}
/>
);
}import { markdownFormat } from 'remons-render-markdown';
async function TableOfContents() {
const content = '# 标题1\n## 标题1.1\n## 标题1.2\n# 标题2';
const { anchor, info } = await markdownFormat(content);
console.log(anchor);
// [
// { title: '标题1', href: '标题1', nodeName: 'H1', children: [...] },
// { title: '标题2', href: '标题2', nodeName: 'H1', children: [] }
// ]
return <div dangerouslySetInnerHTML={{ __html: info }} />;
}import RenderMarkdown, { initHighlighter, languagesCommon } from 'remons-render-markdown';
import 'render-markdown/dist/index.css';
import python from 'highlight.js/lib/languages/python';
import go from 'highlight.js/lib/languages/go';
// 注册额外的语言
initHighlighter({
...languagesCommon, // 包含默认语言
python,
go,
});
function App() {
return (
<RenderMarkdown
content={`
\`\`\`python
def hello():
print('Hello')
\`\`\`
\`\`\`go
package main
import "fmt"
func main() {
fmt.Println("Hello")
}
\`\`\`
`}
/>
);
}import RenderMarkdown from 'remons-render-markdown';
import 'render-markdown/dist/index.css';
function CodeViewer() {
const code = `
const x = 1;
const y = 2;
console.log(x + y);
`;
return (
<RenderMarkdown
content={code}
codeType="javascript"
isSlotMermaid={false}
/>
);
}useIncrementalRender 适用于流式输出场景(如 AI 对话),通过 morphdom 实现高效的 DOM 差异化更新。
import { useIncrementalRender } from 'remons-render-markdown';
function StreamingContent({ content }) {
const { anchors, hasContent, setInnerRef } = useIncrementalRender({
content,
// 可选:节流时间,默认 16ms
throttleMs: 16,
});
return (
<div>
{hasContent && <div ref={setInnerRef} />}
{anchors.length > 0 && (
<ul>
{anchors.map((item) => (
<li key={item.href}>
<a href={`#${item.href}`}>{item.title}</a>
</li>
))}
</ul>
)}
</div>
);
}参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| content | string | ✅ | - | Markdown 内容 |
| codeType | string | ❌ | - | 代码类型,不传则按 Markdown 渲染 |
| customRenderers | MarkdownPlugin[] | ❌ | - | 自定义 markdown-it 插件 |
| throttleMs | number | ❌ | 16 | 节流时间(ms) |
| onNodeDiscarded | (el: HTMLElement) => void | ❌ | - | 节点被 diff 移除后触发(含被移除子树的子节点),用于清理 React Root 等挂载状态 |
返回值:
| 属性 | 类型 | 说明 |
|---|---|---|
| anchors | AnchorItem[] | 文档锚点列表 |
| hasContent | boolean | 是否有内容 |
| setInnerRef | (node: HTMLDivElement) => void | 内容容器的 ref 回调 |
增量渲染与自定义插件(customRenderers)的配合约定:
- 插件产出的容器节点(如
:::badge/:::linkCard/<plugin-container>)按"标签 + className"语义匹配,内容位移时不同类型的块不会被错配到旧节点上。 - 元素上 JS 后加的
data-*属性(如初始化守卫位data-xxx-inited)在 diff 后会被保留,"只初始化一次"的守卫逻辑不会在增量渲染中被重复触发。 - JS 动态插入的额外子节点(如 React 挂载点、收起按钮)默认会被 diff 移除;若需跨增量渲染保留,请给节点设置
data-md-persist属性(见导出常量MD_PERSIST_ATTR)。 - 被 diff 移除的节点会触发
onNodeDiscarded,请在此卸载挂载在其上的 React Root,避免内存泄漏。
---
title: 图表标题
---
......... 图表内容
依赖于 @mdit/plugin-tab 插件,参考 mdit-plugin-tab 的文档。
暂不支持 tabs 嵌套
语法示例:
:::markdown-tabs
@tab:active tab1
```javascript
console.log('Hello');
```
@tab tab2
```typescript
console.log('World');
```
:::语法示例:
> [!warning]
> 我是一个警告信息支持渲染独立的 Mermaid 图表渲染组件,支持缩放、全屏、下载、源码查看等功能。
import { renderMermaid } from 'remons-render-markdown';
import 'remons-render-markdown/dist/index.css';
function MermaidComponent() {
const mermaidCode = `
---
title: 流程图示例
---
graph TD
A[开始] --> B[处理]
B --> C[结束]
`;
return (
<div>
{
renderMermaid({ source: mermaidCode })
}
</div>
);
}| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| source | string | ✅ | - | Mermaid 代码字符串,支持设置标题 |
| debounceMs | number | ❌ | 300 | 图表渲染时的 debounce 时间(毫秒) |
| enablePanzoom | boolean | ❌ | true | 是否启用缩放和平移功能 |
| showDownload | boolean | ❌ | true | 是否显示下载按钮(支持 SVG/PNG 格式) |
| showSourceView | boolean | ❌ | false | 是否显示源码查看按钮和工具栏 |
| showCollapse | boolean | ❌ | false | 是否显示折叠/展开按钮 |
| defaultCollapsed | boolean | ❌ | true | 默认折叠状态 |
| className | string | ❌ | "" | 自定义样式类名 |
| minHeight | number | ❌ | 200 | 最小高度(像素) |
功能特性:
- 缩放:支持放大、缩小、重置视图
- 全屏:支持浏览器原生全屏模式
- 下载:支持下载 SVG 和 PNG 格式
- 源码查看:点击按钮可查看 Mermaid 源码
- 折叠/展开:支持图表的折叠和展开
- 缩略图模式:最小化时显示缩略图
- 深色模式:自动适配主题
- 样式引入:务必引入
remons-render-markdown/dist/index.css以确保正确的样式渲染 - Mermaid 支持:当
isSlotMermaid为true时,会自动渲染 mermaid 代码块为图表 - 代码高亮:请使用
initHighlighter注册(必须) - 安全性:外部链接会自动添加
target="_blank"和rel="noopener"属性 - 性能优化:Mermaid 渲染会延迟执行,以优化首屏加载速度




