🚀 高性能 React Markdown 打字动画组件,完美复刻 DeepSeek 聊天界面效果
🇨🇳 中文 | 🇺🇸 English
一个专为现代 AI 应用设计的 React 组件,提供流畅的实时打字动画和完整的 Markdown 渲染能力。
如果你想要一个纯净的react markdown 打字组件,可以使用react-markdown-typer
- 
流式数据打字卡顿问题
传统打字机组件在处理 AI 后端流式数据时,由于每个 chunk 包含多个字符,会出现卡顿、跳字等问题。ds-markdown 智能拆分每个 chunk,确保每个字符都流畅打字。 - 
Markdown 渲染与打字动画割裂
大多数打字机组件只支持纯文本,无法在打字过程中实时渲染 Markdown 语法、数学公式、图表等富媒体内容。 
- 
提升用户沉浸感
提供专业级 AI 聊天体验,让用户感受到真实的 AI 交互过程,极大提升产品专业度和用户满意度。 - 
开箱即用,降低开发复杂度
完整的解决方案,无需额外配置即可支持流式数据、Markdown 渲染、数学公式、图表等复杂功能。 - 
适配多种应用场景
从 AI 聊天机器人到教育内容展示,从技术文档到产品演示,一个组件满足多种需求。 
- 专业级 AI 聊天响应效果,媲美主流 AI 平台体验
 - 支持思考过程 (
thinking) 和回答内容 (answer) 双模式 - 流式数据完美适配,零延迟响应用户输入
 
- 完整 Markdown 语法支持,包括代码高亮、表格、列表等
 - 数学公式渲染 (KaTeX),支持 
$...$和\[...\]语法 - Mermaid 图表支持,包括流程图、序列图、甘特图、类图等 🆕
 - 支持亮色/暗色主题,适配不同产品风格
 - 插件化架构,支持 remark/rehype 插件扩展
 
- 代码块、图表增强功能:复制、下载、语言等
 - 内置丰富的UI组件:Button、IconButton、ToolTip、Segmented等
 - 完整的交互体验和无障碍支持
 
- 丰富的命令式API:
start、stop、resume、restart等 - 支持打字中断与继续,灵活控制动画状态
 - 支持打字关闭与开启,满足不同场景需求
 - 完整的TypeScript类型支持
 
- 双模式定时器优化,支持
requestAnimationFrame和setTimeout模式 - 高频打字支持(
requestAnimationFrame模式下打字间隔最低可接近于0ms) - 帧同步渲染,与浏览器刷新完美配合
 - 智能字符批量处理,视觉效果更自然
 - 动态速度控制 🆕:根据剩余字符数量自动调整打字速度,流式数据场景下提供更自然的阅读体验
 
- 轻量级设计,体积小、性能优
 - 核心依赖 react-markdown,无重量级依赖
 
# npm
npm install ds-markdown
# yarn
yarn add ds-markdown
# pnpm
pnpm add ds-markdown无需安装,直接在浏览器中使用:
<!-- 导入样式, 必须 -->
<link rel="stylesheet" href="https://esm.sh/ds-markdown/dist/style.css" />
<!-- 导入katex数学公式样式, 非不要不引入 -->
<link rel="stylesheet" href="https://esm.sh/ds-markdown/dist/katex.css" />
<!-- 导入组件 -->
<script type="module">
  import Markdown from 'https://esm.sh/ds-markdown';
</script>import DsMarkdown from 'ds-markdown';
import 'ds-markdown/style.css';
function App() {
  return (
    <DsMarkdown interval={20} answerType="answer">
      # Hello ds-markdown 这是一个**高性能**的打字动画组件! ## 特性 - ⚡ 零延迟流式处理 - 🎬 流畅打字动画 - 🎯 完美语法支持
    </DsMarkdown>
  );
}import DsMarkdown from 'ds-markdown';
import 'ds-markdown/style.css';
function StaticDemo() {
  const [disableTyping, setDisableTyping] = useState(false);
  return (
    <div>
      <button onClick={() => setDisableTyping(!disableTyping)}>{disableTyping ? '开启' : '关闭'}打字机效果</button>
      <DsMarkdown interval={20} answerType="answer" disableTyping={disableTyping}>
        # 静态展示模式 当 `disableTyping` 为 `true` 时,内容会立即全部显示,无打字动画效果。 这在某些场景下非常有用: - 📄 静态文档展示 - 🔄 切换显示模式 - ⚡ 快速预览内容
      </DsMarkdown>
    </div>
  );
}import DsMarkdown from 'ds-markdown';
// 如果需要展示公式,则需要引入公式转换插件
import { katexPlugin } from 'ds-markdown/plugins';
import 'ds-markdown/style.css';
// 如果需要展示公式,则需要引入数学公式样式
import 'ds-markdown/katex.css';
function MathDemo() {
  return (
    <DsMarkdown interval={20} answerType="answer" plugins={[katexPlugin]} math={{ splitSymbol: 'dollar' }}>
      # 勾股定理 在直角三角形中,斜边的平方等于两条直角边的平方和: $a^2 + b^2 = c^2$ 其中: - $a$ 和 $b$ 是直角边 - $c$ 是斜边 对于经典的"勾三股四弦五": $c = \sqrt{3 ^ (2 + 4) ^ 2} = \sqrt{25} = 5$
    </DsMarkdown>
  );
}function ChatDemo() {
  const [thinking, setThinking] = useState('');
  const [answer, setAnswer] = useState('');
  const handleAsk = () => {
    setThinking('🤔 正在思考您的问题...');
    setTimeout(() => {
      setAnswer(`# 关于 React 19
React 19 带来了许多激动人心的新特性:
## 🚀 主要更新
1. **React Compiler** - 自动优化性能
2. **Actions** - 简化表单处理
3. **Document Metadata** - 内置 SEO 支持
让我们一起探索这些新功能!`);
    }, 2000);
  };
  return (
    <div>
      <button onClick={handleAsk}>询问 AI</button>
      {thinking && (
        <DsMarkdown answerType="thinking" interval={30}>
          {thinking}
        </DsMarkdown>
      )}
      {answer && (
        <DsMarkdown answerType="answer" interval={15}>
          {answer}
        </DsMarkdown>
      )}
    </div>
  );
}import DsMarkdown from 'ds-markdown';
import 'ds-markdown/style.css';
function CodeBlockDemo() {
  const codeContent = `# Hello World
\`\`\`javascript
function greet(name) {
  console.log(\`Hello, \${name}!\`);
}
greet('ds-markdown');
\`\`\`
支持代码高亮、复制和下载功能!`;
  return (
    <DsMarkdown
      interval={20}
      answerType="answer"
      codeBlock={{
        headerActions: true, // 启用代码块头部操作按钮
      }}
    >
      {codeContent}
    </DsMarkdown>
  );
}import DsMarkdown from 'ds-markdown';
import { ConfigProvider } from 'ds-markdown';
import mermaidPlugin from 'ds-markdown-mermaid-plugin';
import 'ds-markdown/style.css';
function MermaidDemo() {
  const chartContent = `以下是简化版的学习开车流程图,仅保留 **最核心步骤**,适合快速掌握关键节点:
\`\`\`mermaid
graph TD
    A[开始] --> B[科目一: 理论考试]
    B --> C[科目二: 场地五项]
    C --> D[科目三: 路考]
    D --> E[科目四: 安全笔试]
    E --> F[拿驾照]
    F --> G[实际驾驶练习]
\`\`\`
### 极简说明:
1. **理论先行**:先通过交通规则笔试(科目一)。
2. **场地基础**:练习倒车、坡起等(科目二)。
3. **上路实战**:实际道路驾驶考试(科目三)。
4. **安全收尾**:通过科目四即可领证。
5. **持续熟练**:拿证后继续练习适应真实路况。
### 可视化建议:
- 用手机备忘录或白纸手绘时,按 **箭头顺序** 写步骤即可。
- 想更直观?用圆形便签贴出每个科目,连线成流程。`;
  return (
    <ConfigProvider>
      <DsMarkdown interval={20} answerType="answer" plugins={[mermaidPlugin]}>
        {chartContent}
      </DsMarkdown>
    </ConfigProvider>
  );
}import DsMarkdown, { MarkdownCMD } from 'ds-markdown';| 属性 | 类型 | 说明 | 默认值 | 
|---|---|---|---|
interval | 
number | IntervalConfig | 
打字间隔配置,支持固定间隔或动态速度控制 | 30 | 
timerType | 
'setTimeout' | 'requestAnimationFrame' | 
定时器类型,不支持动态修改 | 当前默认值是setTimeout,后期会改为requestAnimationFrame | 
answerType | 
'thinking' | 'answer' | 
内容类型 (影响样式主题),不支持动态修改 | 'answer' | 
theme | 
'light' | 'dark' | 
主题类型 | 'light' | 
plugins | 
IMarkdownPlugin[] | 
插件配置 | [] | 
math | 
IMarkdownMath | 数学公式配置 | { splitSymbol: 'dollar' } | 
onEnd | 
(data: EndData) => void | 
打字结束回调 | - | 
onStart | 
(data: StartData) => void | 
打字开始回调 | - | 
onBeforeTypedChar | 
(data: IBeforeTypedChar) => Promise<void> | 
字符打字前的回调,支持异步操作,会阻塞之后的打字 | - | 
onTypedChar | 
(data: ITypedChar) => void | 
每字符打字后的回调 | - | 
disableTyping | 
boolean | 
禁用打字动画效果 | false | 
autoStartTyping | 
boolean | 
是否自动开始打字动画,设为 false 时需手动触发,不支持动态修改 | true | 
codeBlock | 
IMarkdownCode | 
代码块配置 | {headerActions: true} | 
注意: 如果当在打字中
disableTyping从true变为false
| 属性 | 类型 | 说明 | 默认值 | 
|---|---|---|---|
currentIndex | 
number | 
当前字符在整个字符串中的索引 | 0 | 
currentChar | 
string | 
当前即将打字的字符 | - | 
answerType | 
AnswerType | 
内容类型 (thinking/answer) | - | 
prevStr | 
string | 
当前类型内容的前缀字符串 | - | 
percent | 
number | 
打字进度百分比 (0-100) | 0 | 
| 属性 | 类型 | 说明 | 默认值 | 
|---|---|---|---|
currentIndex | 
number | 
当前字符在整个字符串中的索引 | 0 | 
currentChar | 
string | 
当前已打字的字符 | - | 
answerType | 
AnswerType | 
内容类型 (thinking/answer) | - | 
prevStr | 
string | 
当前类型内容的前缀字符串 | - | 
currentStr | 
string | 
当前类型内容的完整字符串 | - | 
percent | 
number | 
打字进度百分比 (0-100) | 0 | 
| 属性 | 类型 | 说明 | 默认值 | 
|---|---|---|---|
max | 
number | 
最大间隔时间 (毫秒),剩余字符多时使用 | - | 
min | 
number | 
最小间隔时间 (毫秒),剩余字符少时使用 | - | 
curveFn | 
(x: number) => number | 
自定义曲线函数,x 为剩余字符占比 [0,1] | - | 
curve | 
'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'linear' | 'step-start' | 'step-end' | 
预设曲线函数,curveFn 存在时无效 | 'ease' | 
动态速度控制说明:
- 剩余字符越多,打字越快:当后端流式推送大量字符时,组件会自动加快打字速度
 - 剩余字符越少,打字越慢:接近完成时,打字速度会逐渐放缓,提供更好的阅读体验
 - 流式数据适配:自动适应流式场景中字符数量的动态变化
 - 曲线函数:通过 
curve或curveFn控制速度变化曲线 
使用示例:
// 固定间隔
<DsMarkdown interval={20}>内容</DsMarkdown>
// 动态速度控制
<DsMarkdown
  interval={{
    min: 10,    // 最快 10ms
    max: 50,    // 最慢 50ms
    curve: 'ease-out'  // 减速曲线
  }}
>
  内容
</DsMarkdown>
// 自定义曲线
<DsMarkdown
  interval={{
    min: 5,
    max: 100,
    curveFn: (x) => x * x  // 二次函数曲线
  }}
>
  内容
</DsMarkdown>| 属性 | 类型 | 说明 | 默认值 | 
|---|---|---|---|
splitSymbol | 
'dollar' | 'bracket' | 
数学公式分隔符类型 | 'dollar' | 
分隔符说明:
'dollar':使用$...$和$$...$$语法'bracket':使用\(...\)和\[...\]语法
| 属性 | 类型 | 说明 | 默认值 | 
|---|---|---|---|
headerActions | 
boolean | 
是否显示头部操作按钮 | true | 
| 属性 | 类型 | 说明 | 默认值 | 
|---|---|---|---|
remarkPlugin | 
Pluggable | 
remark 插件 | - | 
rehypePlugin | 
Pluggable | 
rehype 插件 | - | 
type | 
'buildIn' | 'custom' | 
插件类型 | - | 
id | 
any | 
插件唯一标识 | - | 
components | 
Record<string, React.ComponentType<unknown>> | 
自定义组件映射 🆕 | - | 
| 方法 | 参数 | 说明 | 
|---|---|---|
start | 
- | 开始打字动画 | 
stop | 
- | 暂停打字动画 | 
resume | 
- | 恢复打字动画 | 
restart | 
- | 重新开始打字动画,从头开始播放当前内容 | 
| 方法 | 参数 | 说明 | 
|---|---|---|
push | 
(content: string, answerType: AnswerType) | 
添加内容并开始打字 | 
clear | 
- | 清空所有内容和状态 | 
triggerWholeEnd | 
- | 手动触发完成回调 | 
start | 
- | 开始打字动画 | 
stop | 
- | 暂停打字动画 | 
resume | 
- | 恢复打字动画 | 
restart | 
- | 重新开始打字动画,从头开始播放当前内容 | 
用法示例:
markdownRef.current?.start(); // 开始动画
markdownRef.current?.stop(); // 暂停动画
markdownRef.current?.resume(); // 恢复动画
markdownRef.current?.restart(); // 重新开始动画import { katexPlugin } from 'ds-markdown/plugins';
// 启用数学公式支持
<DsMarkdown plugins={[katexPlugin]}>数学公式:$E = mc^2$</DsMarkdown>;安装 Mermaid 插件:
npm install ds-markdown-mermaid-plugin基础用法:
import { ConfigProvider, Markdown } from 'ds-markdown';
import mermaidPlugin from 'ds-markdown-mermaid-plugin';
function App() {
  const content = `
# 流程图示例
\`\`\`mermaid
flowchart TD
    A[开始] --> B{判断条件}
    B -->|是| C[处理A]
    B -->|否| D[处理B]
    C --> E[结束]
    D --> E
\`\`\`
`;
  return (
    <ConfigProvider>
      <Markdown plugins={[mermaidPlugin]}>{content}</Markdown>
    </ConfigProvider>
  );
}支持的图表类型:
- 🔄 流程图 (Flowchart) - 展示流程和决策路径
 - 📋 序列图 (Sequence Diagram) - 展示对象间的交互时序
 - 📅 甘特图 (Gantt Chart) - 项目计划和时间线
 - 🏗️ 类图 (Class Diagram) - 面向对象设计
 - 🥧 饼图 (Pie Chart) - 数据比例展示
 - 🔀 状态图 (State Diagram) - 状态转换流程
 - 📊 Git图 (Git Graph) - 代码分支历史
 - 🗺️ 用户旅程图 (User Journey) - 用户体验流程
 
配置 Mermaid:
import { ConfigProvider } from 'ds-markdown';
const mermaidConfig = {
  theme: 'default', // 主题:default, neutral, dark, forest
  flowchart: {
    useMaxWidth: true,
    htmlLabels: true,
  },
  sequence: {
    diagramMarginX: 50,
    diagramMarginY: 10,
  },
};
return (
  <ConfigProvider mermaidConfig={mermaidConfig}>
    <Markdown plugins={[mermaidPlugin]}>{chartContent}</Markdown>
  </ConfigProvider>
);相关链接:
import { createBuildInPlugin } from 'ds-markdown/plugins';
// 创建自定义插件
const customPlugin = createBuildInPlugin({
  remarkPlugin: yourRemarkPlugin,
  rehypePlugin: yourRehypePlugin,
  id: Symbol('custom-plugin'),
  components: {
    // 自定义组件映射 🆕
    CustomComponent: MyCustomComponent,
  },
});
// 使用自定义插件
<DsMarkdown plugins={[katexPlugin, customPlugin]}>内容</DsMarkdown>;ds-markdown 提供了丰富的UI组件,可以单独使用或与markdown组件配合。
import {
  Button,
  IconButton,
  ToolTip,
  Segmented,
  CopyButton,
  DownloadButton
} from 'ds-markdown';
// 按钮组件
<Button icon={<span>📄</span>} onClick={() => {}}>
  点击按钮
</Button>
// 工具提示
<ToolTip title="提示信息">
  <IconButton icon={<span>📋</span>} onClick={() => {}} />
</ToolTip>
// 分段控制器
<Segmented
  Segmented={[
    { label: '图表', value: 'diagram' },
    { label: '代码', value: 'code' }
  ]}
  value={value}
  onChange={setValue}
/>
// 代码块操作
<CopyButton codeContent="console.log('Hello')" />
<DownloadButton codeContent="console.log('Hello')" language="javascript" />:root {
  --ds-button-bg-color: #f5f5f5;
  --ds-button-hover-color: #e0e0e0;
  --ds-tooltip-bg-color: rgba(0, 0, 0, 0.8);
}import { ConfigProvider } from 'ds-markdown';
import zhCN from 'ds-markdown/i18n/zh';
import enUS from 'ds-markdown/i18n/en';
// 中文
<ConfigProvider locale={zhCN}>
  <DsMarkdown {...props} />
</ConfigProvider>
// 英文
<ConfigProvider locale={enUS}>
  <DsMarkdown {...props} />
</ConfigProvider>ConfigProvider 是一个全局配置提供者,用于为 ds-markdown 组件提供多语言、Mermaid 图表和 KaTeX 数学公式的配置。
interface ConfigProviderProps {
  locale?: Locale; // 语言包配置
  mermaidConfig?: IMarkdownMermaidConfig; // Mermaid 图表配置
  katexConfig?: IMarkdownKatexConfig; // KaTeX 数学公式配置
  children: React.ReactNode; // 子组件
}import { ConfigProvider } from 'ds-markdown';
import zhCN from 'ds-markdown/i18n/zh';
import enUS from 'ds-markdown/i18n/en';
<ConfigProvider locale={zhCN}>
  <DsMarkdown {...props} />
</ConfigProvider>;const mermaidConfig = {
  theme: 'dark', // 主题:'default' | 'forest' | 'dark' | 'neutral'
  fontFamily: 'Arial', // 字体
  logLevel: 'warn', // 日志级别
  securityLevel: 'strict', // 安全级别
  startOnLoad: true, // 页面加载时自动启动
  // ... 更多 Mermaid 配置选项
};
<ConfigProvider mermaidConfig={mermaidConfig}>
  <DsMarkdown {...props} />
</ConfigProvider>;const katexConfig = {
  throwOnError: false, // 错误时不抛出异常
  errorColor: '#cc0000', // 错误颜色
  macros: {
    // 自定义宏
    '\\RR': '\\mathbb{R}',
    '\\NN': '\\mathbb{N}',
  },
  minRuleThickness: 0.05, // 最小规则厚度
  colorIsTextColor: false, // 颜色是否为文本颜色
  // ... 更多 KaTeX 配置选项
};
<ConfigProvider katexConfig={katexConfig}>
  <DsMarkdown {...props} />
</ConfigProvider>;获取完整的配置上下文:
import { useConfig } from 'ds-markdown';
function MyComponent() {
  const { locale, mermaidConfig, katexConfig } = useConfig();
  return (
    <div>
      <p>当前语言: {locale.language}</p>
      <p>Mermaid 主题: {mermaidConfig?.theme}</p>
    </div>
  );
}仅获取语言包配置:
import { useLocale } from 'ds-markdown';
function MyComponent() {
  const locale = useLocale();
  return (
    <div>
      <p>当前语言: {locale.language}</p>
      <p>复制按钮文本: {locale.copyButton}</p>
    </div>
  );
}import { ConfigProvider } from 'ds-markdown';
import zhCN from 'ds-markdown/i18n/zh';
const mermaidConfig = {
  theme: 'dark',
  fontFamily: 'Consolas, monospace',
  logLevel: 'warn',
};
const katexConfig = {
  throwOnError: false,
  errorColor: '#cc0000',
  macros: {
    '\\RR': '\\mathbb{R}',
  },
};
function App() {
  return (
    <ConfigProvider locale={zhCN} mermaidConfig={mermaidConfig} katexConfig={katexConfig}>
      <DsMarkdown content="# Hello World" />
    </ConfigProvider>
  );
}import DsMarkdown from 'ds-markdown';
function DynamicSpeedDemo() {
  const content = `# 动态速度控制示例
这是一个演示动态速度控制的示例。当剩余字符较多时,打字速度会加快;
当剩余字符较少时,打字速度会放缓,提供更好的阅读体验。
## 流式数据场景
在 AI 流式对话中,后端可能会一次性推送大量文本,使用动态速度控制可以:
- 快速处理大量文本,减少等待时间
- 在接近完成时放缓速度,让用户有时间阅读
- 提供更自然的打字体验`;
  return (
    <DsMarkdown
      interval={{
        min: 8, // 最快 8ms(剩余字符多时)
        max: 80, // 最慢 80ms(剩余字符少时)
        curve: 'ease-out', // 减速曲线
      }}
      timerType="requestAnimationFrame"
    >
      {content}
    </DsMarkdown>
  );
}import { useRef } from 'react';
import { MarkdownCMD, MarkdownCMDRef } from 'ds-markdown';
function StreamingChat() {
  const markdownRef = useRef<MarkdownCMDRef>(null);
  // 模拟 AI 流式响应
  const simulateAIResponse = async () => {
    markdownRef.current?.clear();
    // 思考阶段
    markdownRef.current?.push('🤔 正在分析您的问题...', 'thinking');
    await delay(1000);
    markdownRef.current?.push('\n\n✅ 分析完成,开始回答', 'thinking');
    // 流式回答
    const chunks = [
      '# React 19 新特性解析\n\n',
      '## 🚀 React Compiler\n',
      'React 19 最大的亮点是引入了 **React Compiler**:\n\n',
      '- 🎯 **自动优化**:无需手动 memo 和 useMemo\n',
      '- ⚡ **性能提升**:编译时优化,运行时零开销\n',
      '- 🔧 **向后兼容**:现有代码无需修改\n\n',
      '希望这个解答对您有帮助!🎉',
    ];
    for (const chunk of chunks) {
      await delay(100);
      markdownRef.current?.push(chunk, 'answer');
    }
  };
  const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
  return (
    <div className="chat-container">
      <button onClick={simulateAIResponse}>🤖 询问 React 19 新特性</button>
      <MarkdownCMD ref={markdownRef} interval={10} timerType="requestAnimationFrame" />
    </div>
  );
}// ✅ 推荐配置
<DsMarkdown
  timerType="requestAnimationFrame"
  interval={15} // 15-30ms 为最佳体验
/>
// ✅ 流式数据推荐配置
<DsMarkdown
  timerType="requestAnimationFrame"
  interval={{
    min: 8,     // 快速处理大量文本
    max: 60,    // 接近完成时放缓
    curve: 'ease-out'  // 自然减速
  }}
/>// ✅ 推荐:命令式 API
const ref = useRef<MarkdownCMDRef>(null);
useEffect(() => {
  ref.current?.push(newChunk, 'answer');
}, [newChunk]);// ✅ 推荐:按需加载
import { katexPlugin } from 'ds-markdown/plugins';
import 'ds-markdown/katex.css'; // 仅在需要时引入
<DsMarkdown plugins={[katexPlugin]}>数学公式内容</DsMarkdown>;// ✅ 推荐:独立安装插件
npm install ds-markdown-mermaid-plugin
// ✅ 推荐:配置适合的主题
const mermaidConfig = {
  theme: 'default', // 根据应用主题选择
  flowchart: { useMaxWidth: true },
};
<ConfigProvider mermaidConfig={mermaidConfig}>
  <DsMarkdown plugins={[mermaidPlugin]} />
</ConfigProvider>