Sawana Huang Avatar

Sawana Huang

如何用 Streamdown 渲染 AI 流式 Markdown,并定制代码块(2026 版)

Sawana Huang, Claude Code - Mon Sep 22 2025

按当前官方资料重写的 Streamdown 教程,讲清楚它和 AI Elements Response 的关系、Tailwind 接入、流式 Markdown 渲染,以及如何通过 components 覆盖定制代码块。

这篇我收得比较狠。旧文信息不少,只是重心跑偏了:源码讲了很多,真正接入时最需要知道的事反而被压在后面。

实际落地时,最先会碰到的是这些问题:

  • Streamdown 什么时候该用
  • 它和 AI Elements Response 是什么关系
  • 在 Next.js 里怎么接入
  • Tailwind 样式为什么有时不生效
  • 如果我只是想把代码块字体调大,应该从哪一层改

所以这版就不再拉着源码一段一段过,而是直接讲项目里怎么用。

先把关系说清楚

根据当前官方资料:

  • Streamdown 是一个面向 AI 流式 Markdown 的 react-markdown 替代方案
  • AI Elements 的 Response 组件就是基于它构建的
  • 如果你已经在用 AI Elements,很多时候可以直接从 Response 开始
  • 如果你需要定制代码块、Mermaid、controls、主题等细节,那么直接用 Streamdown 会更灵活

实际用的时候,可以这样分:

  • 想快速渲染 AI 文本:先试 Response
  • 想深度定制 Markdown 渲染:下探到 Streamdown

Streamdown 适合什么场景

react-markdown 能处理“完整 Markdown”。

而 AI 场景里,你经常拿到的是:

  • 正在流式生成的文本
  • 尚未闭合的代码块
  • 还没结束的粗体、链接、标题

这正是 Streamdown 要解决的问题。它的重点在于 Markdown 还没生成完时,页面也尽量稳稳地继续渲染。

它和 AI Elements Response 的关系

官方 README 已经写得很清楚:Streamdown 为 AI Elements 的 Response 组件提供底层能力。

而我们当前仓库里的 Response 包装层也很轻:

"use client";

import { cn } from "@/lib/utils";
import { type ComponentProps, memo } from "react";
import { Streamdown } from "streamdown";

type ResponseProps = ComponentProps<typeof Streamdown>;

export const Response = memo(({ className, ...props }: ResponseProps) => (
  <Streamdown
    className={cn(
      "size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
      className,
    )}
    {...props}
  />
));

也就是说:

  • Response 更像一个更贴 UI 规范的包装层
  • Streamdown 才是底层渲染引擎

完整接入步骤

1. 安装 Streamdown

参考 Streamdown 官方 README

npm install streamdown

如果你已经在项目里通过其他依赖带进来了,也建议先确认当前版本和官方文档是否一致。

2. 在 Tailwind 样式入口里加入 @source

这是最容易漏掉的一步。

根据官方 README,接入 Streamdown 后,需要让 Tailwind 扫描它的分发文件,否则一些样式类不会被收进最终产物。

在你的全局样式文件里加入:

@source "../node_modules/streamdown/dist/index.js";

具体相对路径要按你的项目目录调整。重点是:路径必须能正确指到 node_modules/streamdown/dist/index.js

3. 从最小可用示例开始

如果你只是想渲染一段流式 Markdown,最小示例就是:

"use client";

import { Streamdown } from "streamdown";

export function SimpleMessage({ text }: { text: string }) {
  return <Streamdown>{text}</Streamdown>;
}

这一步先解决“能渲染”,不要一开始就同时处理自定义 code block、Mermaid、复制按钮等问题。

4. 在 AI SDK 消息列表里使用它

官方 README 里给了一个很直接的方向:把消息里的文本 part 喂给 Streamdown。

例如:

"use client";

import { useChat } from "@ai-sdk/react";
import { Streamdown } from "streamdown";

export default function ChatMessages() {
  const { messages } = useChat();

  return (
    <>
      {messages.map((message) => (
        <div key={message.id}>
          {message.parts
            .filter((part) => part.type === "text")
            .map((part, index) => (
              <Streamdown key={index}>{part.text}</Streamdown>
            ))}
        </div>
      ))}
    </>
  );
}

如果你已经在项目里有自己的 Response 包装层,也可以统一改用 Response 来维持样式一致性。

5. 先用 components 覆盖默认代码块,别一上来就 fork 整个库

这一步是本文真正的重点。

如果你的需求只是:

  • 调大代码块字体
  • 换复制按钮
  • 自定义下载按钮
  • 只改 code 渲染

最划算的入口通常不是直接改 Streamdown 源码,先通过 components 覆盖它的 code 组件会轻很多。

我们当前仓库里的做法就是这样:

import { Streamdown } from "streamdown";
import { CustomCodeComponent } from "./custom-code-component";

<Streamdown
  components={{
    code: CustomCodeComponent,
  }}
>
  {markdownContent}
</Streamdown>;

这个策略的优势非常直接:

  • 改动范围小
  • 可维护
  • 仍然保留 Streamdown 其他默认能力

一个贴近本仓库的实际例子

当前仓库已经有一套针对代码块字体的定制 demo:

Streamdown 客制化 CodeBlock 演示interactive
对比不同字体大小的代码块渲染效果,展示自定义组件的实现方案

选择版本:

自定义版本 - 使用 text-sm 字体大小,提升可读性

选择演示内容:

当前展示:自定义简短示例

这里是一个简单的工具函数:

typescript

这个函数提供了中文日期格式化功能。

实现原理

原版 Streamdown

preClassName="...text-xs..."

默认使用较小的字体,可能影响代码可读性

自定义版本

preClassName="...text-sm..."

通过 components 参数替换 code 组件,提升字体大小

核心实现步骤:

  1. 基于官方 Streamdown CodeComponent,仅修改字体大小
  2. 将 preClassName 中的 text-xs 改为 text-sm
  3. 使用 React.memo 保持性能优化
  4. 通过 Streamdown 的 components prop 替换默认组件
  5. 保持所有原有功能:语法高亮、复制按钮、下载功能

这套实现的核心思想非常简单:

  • 保留 Streamdown 的整体架构
  • 只替换 components.code
  • 把代码块字体从默认更小的尺寸提高到更易读的尺寸

如果你的目标只是“让 AI 代码块更好读”,这是比整库 fork 更务实的路径。

什么时候该用 Response,什么时候该直接用 Streamdown

优先用 Response

适合这些情况:

  • 你已经在用 AI Elements 风格的消息组件
  • 你只是想统一渲染 AI 文本
  • 你不想自己接管太多 Markdown 细节

直接用 Streamdown

适合这些情况:

  • 你要覆盖 components
  • 你要控制 controls
  • 你要接 Mermaid 配置
  • 你要自己定义容器样式和渲染策略

三个最常见的坑

1. 忘了 @source

这会让你以为“Streamdown 样式不全”或者“某些 class 没生效”,但真正的问题是 Tailwind 没扫到它的分发文件。

2. 需求只是改代码块字体,却一上来复制整套源码

除非你的改动已经深入到内部实现,否则优先尝试 components.code

3. 把它当成普通 Markdown 渲染器来理解

它当然能渲染普通 Markdown,但它真正的价值在“流式、不完整、AI 输出”。

几个常用 props

根据官方 README,除了 children,平时最常碰到的是:

  • parseIncompleteMarkdown
    • 是否解析并样式化未闭合的 Markdown 片段
  • components
    • 自定义组件覆盖入口
  • shikiTheme
    • 代码高亮主题
  • mermaidConfig
    • Mermaid 配置
  • controls
    • 复制 / 下载按钮显示控制

真要继续深挖,也建议先从这几个入口下手。

总结

Streamdown 真正好用的地方,在于 AI 还没把 Markdown 说完的时候,页面也能继续正常工作。真要改样式或代码块,也别急着 fork 整个库,先试 components.code,通常就够用了。

参考资料

作者:Sawana Huang
发布时间:2025年9月22日

声明: 本文采用CC BY-NC-SA 4.0许可协议,转载请注明出处。