掘金文章归档 · 第 2 篇 react-markdown 系列 1/4 React / JavaScript

基于 react-markdown 实现对大模型输出展示(一):初步展示及插件应用

react-markdown + github-markdown-css + remark-gfm + rehype-raw:让大模型的 Markdown 流式输出"长得像 GitHub"

原文作者:随意_(掘金) | juejin.cn/post/7478893378034581540 | 发布于 2025-03-07 | 阅读 4,394 · 约 7 分钟

0背景:大模型输出展示的五个痛点

我们做大模型应用的时候,往往需要处理大模型流式输出,一般是 md 格式的数据。不论是流式输出还是整体输出,我们都需要把它呈现成"md 格式文档"。这中间有些许的难点及痛点需要解决:

  1. 如何展示 markdown 的格式;
  2. 如何美化 markdown 的格式;
  3. 如何在 markdown 中展示 html 结构;
  4. 如何在 markdown 中展示 html 结构,且触发对应的事件;
  5. 如何展示类似于 echarts 类似的图表。

下面我们一一展示,并给出实际的应用 demo。本系列共 4 篇:

篇目内容解决痛点
第 1 篇(本文)react-markdown 初步展示 + 插件应用①②③(基础格式 / 美化 / html)
第 2 篇自定义标签及事件触发④(html + 事件)
第 3 篇输出自定义的 echarts 报表⑤(图表)
第 4 篇输出代码及高亮展示代码块能力
📌 本文(第 1 篇)先打好地基:初始化项目 + 用 react-markdown 渲染 + 用三个插件补齐"样式、表格、原生 HTML"。后面所有复杂功能都基于此实现。

1初始化 React 项目并引入 Tailwind CSS

1.1 vite 搭建 react 项目

npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install
npm run dev

1.2 引入 tailwindcss

安装依赖(本示例使用版本 tailwindcss: "^3.4.17"):

npm install tailwindcss@3 postcss autoprefixer -S

初始化 Tailwind CSS:在项目根目录运行以下命令,创建 tailwind.config.js(同时根目录会多出 postcss.config.js):

npx tailwindcss init -p

1.3 配置 Tailwind CSS

tailwind.config.js 配置如下:

/** @type {import('tailwindcss').Config} */
export default {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
}

postcss.config.js 配置:

export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

src/index.css(或相应 CSS 文件)中引入 Tailwind CSS:

@tailwind base;
@tailwind components;
@tailwind utilities;

1.4 使用 Tailwind CSS 类名并运行

app.jsx 中使用 Tailwind CSS 的工具类:

function App() {
  return (
    <div className="bg-blue-500 text-white p-4 rounded">Hello, World!</div>
  );
}

运行项目:

npm run dev
截图位置请对照原文:浏览器中能看到蓝色圆角 Hello, World! 卡片即引入成功。项目搭建完成,也可以直接 clone 原文项目(master 分支)。

2引入 react-markdown 并初步展示 md 文档

2.1 安装

npm i react-markdown -S

2.2 基础展示代码

定义一段包含丰富 Markdown 元素的内容(标题、文本样式、列表、超链接、表格),用 <ReactMarkdown> 渲染:

import React from 'react';
import ReactMarkdown from 'react-markdown';

// 定义包含丰富 Markdown 元素的内容
const richMarkdownContent = `
# 一级标题:Markdown 丰富示例

## 二级标题:文本样式

这里展示了 **加粗**、*斜体* 和 ***加粗斜体*** 的文本样式。

## 二级标题:列表

### 无序列表
- 无序列表项 1
- 无序列表项 2
  - 子列表项 2.1
  - 子列表项 2.2
- 无序列表项 3

### 有序列表
1. 有序列表项 1
2. 有序列表项 2
   1. 子有序列表项 2.1
   2. 子有序列表项 2.2
3. 有序列表项 3

## 二级标题:超链接
这是一个 [指向百度的超链接](https://www.baidu.com)。

## 二级标题:表格
| 表头 1 | 表头 2 | 表头 3 |
| ---- | ---- | ---- |
| 单元格 1 | 单元格 2 | 单元格 3 |
| 单元格 4 | 单元格 5 | 单元格 6 |

### 三级标题:嵌套结构示例
可以在表格里嵌套列表,例如:

| 列表嵌套 | 详情 |
| ---- | ---- |
| 无序列表 | - 子项 1<br>- 子项 2 |
| 有序列表 | 1. 子项 A<br>2. 子项 B |
`;

const App = () => {
  return (
    <div>
      <h1>使用 react - markdown 渲染丰富 Markdown 内容</h1>
      <ReactMarkdown>
        {richMarkdownContent}
      </ReactMarkdown>
    </div>
  );
};

export default App;
⚠️ 只引入 react-markdown 时,md 的基础格式能展示,但表格、Ul、ol、超链接等并不能很好地回显(后面逐个补插件)。
截图位置请对照原文:页面能渲染标题、加粗斜体、列表、链接,但表格区域显示异常。

3样式美化:github-markdown-css

3.1 安装并在包裹元素上设置类名

npm install github-markdown-css -S

关键点:必须在包裹元素上设置 className="markdown-body",样式才会生效:

import React from 'react';
import ReactMarkdown from 'react-markdown';
import "github-markdown-css"

// 定义包含丰富 Markdown 元素的内容
const richMarkdownContent = `...`;   // 同上一节的丰富示例

const App = () => {
  return (
    <div className="markdown-body">
      <h1>使用 react - markdown 渲染丰富 Markdown 内容</h1>
      <ReactMarkdown>
        {richMarkdownContent}
      </ReactMarkdown>
    </div>
  );
};

export default App;
📌 markdown-body 是 github-markdown-css 的作用域类名,所有 GitHub 风格样式(标题、代码块、引用等)都挂在它下面。
截图位置请对照原文:此时 md 格式样式(标题层级、代码块底色等)已出现,但表格等部分元素仍无法展示。

4表格支持:引入 remark-gfm

4.1 安装并接入 remarkPlugins

npm i remark-gfm -S
import React from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from "remark-gfm";
import "github-markdown-css"

// 定义包含丰富 Markdown 元素的内容
const richMarkdownContent = `...`;   // 同前

const App = () => {
  return (
    <div className="markdown-body">
      <h1>使用 react - markdown 渲染丰富 Markdown 内容</h1>
      <ReactMarkdown remarkPlugins={[remarkGfm]}>
        {richMarkdownContent}
      </ReactMarkdown>
    </div>
  );
};

export default App;
✅ GFM(GitHub Flavored Markdown)补齐了表格、任务列表、自动链接等 GitHub 扩展语法,表格此时已经能正常展示。
截图位置请对照原文:表格区域已能完整展示表头与单元格。

5展示 HTML 结构:引入 rehype-raw

5.1 问题:Markdown 里嵌 HTML 默认不展示

大模型的输出里可能直接包含 HTML 标签(比如高亮某个词)。把代码改成下面这样,markdown 内容中有一个 <span style="color: red;">

import React from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from "remark-gfm";
import "github-markdown-css"

// 定义包含丰富 Markdown 元素的内容
const markdownContent = `
# 这是一个标题

这是一段包含 <span style="color: red;">HTML 标签</span> 的文本。
`;

const App = () => {
  return (
    <div className="markdown-body">
      <h1>使用 react - markdown 渲染丰富 Markdown 内容</h1>
      <ReactMarkdown remarkPlugins={[remarkGfm]}>
        {markdownContent}
      </ReactMarkdown>
    </div>
  );
};

export default App;
⚠️ 默认情况下 react-markdown 出于安全考虑会过滤掉原始 HTML,所以这段 HTML 结构不会被展示(安全优先是刻意的设计)。
截图位置请对照原文:红色"HTML 标签"四个字没有效果,span 标签被当作纯文本或丢弃。

5.2 解法:接入 rehypePlugins={[rehypeRaw]}

npm i rehype-raw -S
import React from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from "remark-gfm";
import rehypeRaw from "rehype-raw";
import "github-markdown-css"

// 定义包含丰富 Markdown 元素的内容
const markdownContent = `
# 这是一个标题

这是一段包含 <span style="color: red;">HTML 标签</span> 的文本。
`;

const App = () => {
  return (
    <div className="markdown-body">
      <h1>使用 react - markdown 渲染丰富 Markdown 内容</h1>
      <ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
        {markdownContent}
      </ReactMarkdown>
    </div>
  );
};

export default App;
📌 rehype-raw 让 markdown 中嵌入的原始 HTML 标签能被解析并渲染,红色"HTML 标签"出现了。注意:放开原生 HTML 意味着引入了 XSS 面,生产环境要配合白名单 / DOMPurify 使用。
截图位置请对照原文:"HTML 标签"四个字已渲染为红色。

6总结:基础能力的正确组合

至此,我们基于 react-markdown 并引用 remark-gfm、rehype-raw、github-markdown-css 来处理复杂 md 格式、处理 html 标签和美化样式。以上是基础功能展示(原文对应分支是 dev1,可以自行下载)。后期的所有复杂功能都基于此来实现。

能力缺什么插件接入方式
基础 md 渲染<ReactMarkdown>{content}</ReactMarkdown>
GitHub 风格美化github-markdown-css包裹元素加 className="markdown-body"
表格 / 任务列表remark-gfmremarkPlugins={[remarkGfm]}
原生 HTML 标签rehype-rawrehypePlugins={[rehypeRaw]}

7扩展:四个库分别解决什么问题

7.1 react-markdown

用于在 React 应用中渲染 Markdown 内容的库。Markdown 是一种轻量级标记语言,使用简单的文本格式来创建富文本内容,例如标题、列表、链接等。react-markdown 可以将 Markdown 字符串转换为 React 组件,使得在 React 应用中显示 Markdown 内容变得非常方便。

7.2 remark-gfm

一个 remark 插件,用于支持 GitHub Flavored Markdown(GFM)。GFM 是 GitHub 对标准 Markdown 的扩展,增加了一些额外的功能,如表格、任务列表、自动链接等。

作用:使用 react-markdown 渲染 Markdown 内容时,默认情况下可能不支持这些 GFM 特性。通过引入 remark-gfm 插件,可以让 react-markdown 能够正确解析和渲染这些扩展的 Markdown 语法。

7.3 rehype-raw

一个 rehype 插件,用于处理 Markdown 中的原始 HTML 内容。在 Markdown 中,有时会嵌入一些 HTML 标签,例如 <div><span> 等。默认情况下,react-markdown 可能会过滤掉这些原始 HTML 内容,以确保安全性。

作用:使用 rehype-raw 插件可以让 react-markdown 解析并渲染这些原始 HTML 标签,使得 Markdown 中嵌入的 HTML 内容能够正常显示。

7.4 github-markdown-css

一个 CSS 文件,它提供了与 GitHub 上 Markdown 内容相同的样式。当你在自己的应用中渲染 Markdown 内容时,使用这个 CSS 文件可以让渲染结果看起来与 GitHub 上的 Markdown 样式一致,包括标题、列表、代码块等的样式。

作用:通过引入 github-markdown-css,可以让你的 Markdown 内容在视觉上更加美观和专业,同时保持与 GitHub 风格的一致性。

📌 一句话总结这四者的分工:react-markdown 负责"把 md 变成组件",remark-gfm 负责"支持 GitHub 扩展语法",rehype-raw 负责"放行原生 HTML",github-markdown-css 负责"长得像 GitHub"

8术语表 & 附录:完整可运行代码

术语表

术语定义
react-markdown把 Markdown 字符串转换为 React 组件的渲染库
remark / rehypeMarkdown 的解析插件体系:remark 管 md→mdast 语法树,rehype 管 hast 语法树→HTML
remark-gfm支持 GitHub Flavored Markdown:表格、任务列表、自动链接等扩展语法
rehype-raw允许 markdown 中的原始 HTML 被解析渲染(注意 XSS 风险)
github-markdown-cssGitHub 风格的 Markdown 样式表,需配合 markdown-body 作用域类名
GFMGitHub Flavored Markdown,GitHub 对标准 Markdown 的扩展
Tailwind CSS原子化 CSS 框架,本系列用它做页面基础样式

附录 A:完整可运行代码(App.jsx 全量)

// ========== 安装依赖 ==========
// npm create vite@latest my-react-app -- --template react
// cd my-react-app && npm install
// npm install tailwindcss@3 postcss autoprefixer -S
// npx tailwindcss init -p
// npm i react-markdown -S
// npm install github-markdown-css -S
// npm i remark-gfm -S
// npm i rehype-raw -S

import React from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from "remark-gfm";
import rehypeRaw from "rehype-raw";
import "github-markdown-css";

const richMarkdownContent = `
# 一级标题:Markdown 丰富示例

## 二级标题:文本样式

这里展示了 **加粗**、*斜体* 和 ***加粗斜体*** 的文本样式。

## 二级标题:列表

### 无序列表
- 无序列表项 1
- 无序列表项 2
  - 子列表项 2.1
  - 子列表项 2.2
- 无序列表项 3

### 有序列表
1. 有序列表项 1
2. 有序列表项 2
3. 有序列表项 3

## 二级标题:超链接
这是一个 [指向百度的超链接](https://www.baidu.com)。

## 二级标题:表格
| 表头 1 | 表头 2 | 表头 3 |
| ---- | ---- | ---- |
| 单元格 1 | 单元格 2 | 单元格 3 |
| 单元格 4 | 单元格 5 | 单元格 6 |
`;

const App = () => {
  return (
    <div className="markdown-body">
      <h1>使用 react-markdown 渲染丰富 Markdown 内容</h1>
      <ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
        {richMarkdownContent}
      </ReactMarkdown>
    </div>
  );
};

export default App;

系列预告

未完待续……下一篇(系列 2)我们学习如何借助自定义 components 实现自定义标签和事件触发;第 3 篇更进一步渲染 echarts 等报表;第 4 篇输出代码及高亮展示。所有复杂功能都基于本文这个组合。