我最近在给团队搭一个内部 AI 问答助手,第一步就卡在聊天界面的技术选型上。网上搜了一圈,被提到最多的两个方案就是 ChatUI 和 Ant Design X。一个算是阿里系的老牌对话界面组件库,一个是 Ant Design 面向 AI 场景推出的新组件库。不少文章把它们当成“旧版 vs 新版”来对比,实际上手跑了一遍才发现,这俩从设计目标开始就不是同一个物种,选型结论自然也完全不同。
这篇不打算复述官方文档。我想从真实接入的角度,把两个库的核心定位、消息模型、定制边界、流式处理、移动端适配和工程化体验拆开来讲,最后结合我的实际项目给出选型建议。如果你也在为“聊天窗口到底用哪个 UI 库”纠结,这篇文章应该能帮你省下不少试错时间。
1. 两个库看起来都做聊天,出发点差了十万八千里
1.1 ChatUI:把“聊天气泡”这一件事做到极致
ChatUI 当初的设计目标非常聚焦:给对话式交互提供一个开箱即用的 React 组件库。它的核心概念就是 Chat、Bubble、Composer 这一套东西,对应到界面就是消息列表、气泡和输入区。组件本身就内置了 text、image、file、audio、video、system、typing 这些常见的消息类型,开发者只要按约定把消息数据传进去,一个看起来还不错的聊天窗口就出来了。
我印象最深的是它的 useMessages 这个 hook,它把消息列表的增删改、打字中状态都封装好了。最初跑通一个最简单的问答界面,拢共写了不到 50 行代码。对于“只要一个聊天窗口,不想关心别的”的场景,ChatUI 确实是效率最高的选择之一。
但 ChatUI 的边界也在这:它关注的是“对话框内部”,你的产品如果有欢迎页、推荐问题、思维链这类 AI 应用特有模块,它帮不上什么忙,需要自己用别的组件拼。
1.2 Ant Design X:从欢迎页到思维链,覆盖整个 AI 交互链路
Ant Design X 的定位不是“聊天 UI 组件库”,而是“AI 原生应用的前端解决方案”。这一点从它的组件矩阵就能看出来:除了 Bubbles(气泡列表),还有 Welcome(欢迎页)、Suggestion(推荐建议)、Prompt(提示词编辑器)、ThoughtChain(思维链)、Attachment(附件)、Voice(语音)以及做全局配置的 XProvider。
我用 X 做第一个 Demo 时的直观感受是,它不是在帮你拼一个“聊天窗口”,而是在帮你搭一个“完整的 AI 产品页面”。比如用户进来先看到一个 Welcome 页面,下面有几个 Suggestion 推荐问题,点了一个进入对话,回答过程中有一个 ThoughtChain 展示当前 AI 思考到哪一步了,聊到需要多模态时还能接 Attachment 和 Voice。这套链路如果完全用普通组件硬拼,工作量要比 ChatUI 场景大得多。
另外 X 提供了 useXAgent 和 useXChat 这两个 hooks,专门处理 AI 流式对话的状态管理。这是 ChatUI 完全没有的层次,后面的章节我会专门拆开讲。
1.3 定位差异直接影响选型结论
很多人把 Ant Design X 当成 ChatUI 的“升级替代版”,这是一个很常见的误判。ChatUI 是一个部件,Ant Design X 是一个框架层级的东西。如果你的场景只需要一个对话窗口,引进 X 会显得有点重,而且它的优势模块你根本用不上;反过来,如果你在做一个完整的 AI 产品,只用 ChatUI 会发现自己要在系统层面补非常多东西。
我后来用一个类比跟团队解释:ChatUI 类似一套成品厨房柜,主要管“做菜区域”;Ant Design X 则是一套整屋装修方案,从玄关到阳台都给你出了图。选型的第一步不是比谁的柜门好看,而是想清楚你是在装修一个厨房,还是在盖整栋房子。
2. 消息模型和渲染机制是选型的核心分水岭
2.1 ChatUI 用 type + content 的协议驱动渲染
ChatUI 的每个消息对象长这样:
{ type: 'text', content: { text: '你好,我是智能助手' } }消息列表就是一个这样的数组。组件内部根据type去匹配不同消息类型的默认渲染逻辑。如果默认渲染满足不了业务,ChatUI 留了一个自定义渲染器入口renderMessageContent:
<Chat messages={messages} onSend={handleSend} renderMessageContent={(message) => { if (message.type === 'product') { return <ProductCard data={message.content} />; } return null; }} />这种设计思路是:数据层保持简单,所有复杂展示放在渲染器里做。业务上要做商品卡片、图文混排、订单状态这类定制消息时,自定义一个 type,在渲染器里写对应组件就行。它本质上是一个“消息协议 + 渲染分发器”的架构。
不过它的自定义渲染器并不带样式隔离,所有自定义内容需要你自己布局。用久了你会发现,它给你的是“消息级”的自由,但不是“组件级”的集成方案。
2.2 Ant Design X 用 items 和 hooks 组织整个对话流
Ant Design X 的核心呈现组件是 Bubble,批量渲染用Bubble.List:
import { Bubble, Suggestion, XProvider } from '@ant-design/x'; <XProvider> <Bubble.List items={[ { key: '1', role: 'user', content: '帮我总结一下今天的会议' }, { key: '2', role: 'assistant', content: '好的,我来处理。' }, ]} /> </XProvider>content可以传字符串,也可以是任意 ReactNode。气泡的角色、头像、样式都通过roles或styles定制。这个模型对“复杂内容”更开放——你不用先在数据层定义 type,直接在content里塞一个<ProductCard />就行。
更关键的是官方推荐配useXAgent和useXChat实现整个对话状态流:
const [agent] = useXAgent({ request: async ({ message }) => { // 在这里调用后端,支持流式 }, }); const { onRequest, messages } = useXChat({ agent }); // 发送消息 onRequest({ message: '你好' });useXChat负责维护消息列表,agent负责和模型方通信。消息的发送、回复、更新、结束,都在这一套状态机制里流转。相比 ChatUI 手动appendMsg+setTyping的方式,X 把“对话生成”当成一个生命周期来管理,更适合复杂的 AI 交互。
为了看得更清楚,我把流式接收在不采用高层封装时的样子写出来:
const [agent] = useXAgent({ request: async ({ message }, { onUpdate, onSuccess }) => { const response = await fetch('/api/chat', { method: 'POST', body: JSON.stringify({ message }), }); const reader = response.body.getReader(); const decoder = new TextDecoder(); let content = ''; while (true) { const { done, value } = await reader.read(); if (done) break; content += decoder.decode(value, { stream: true }); onUpdate(content); } onSuccess(content); }, });这一段是我实际项目里直接用过的骨架,后面讲踩坑时还会回到它。
2.3 两种消息模式对应两种开发心智
ChatUI 的心智是“消息是我定义的,我控制怎么渲染”;Ant Design X 的心智是“整个对话交互是一个系统,我在系统里挂载业务组件”。
这个差异带来几个实际影响。首先,ChatUI 适合消息形态多、但页面结构固定的业务场景,因为它支持任意自定义消息类型;其次,Ant Design X 适合消息形态相对统一、但整体应用需要快速搭起来的场景,因为它把链路都铺好了。最后,如果你需要深度改样式,ChatUI 要覆盖它的 CSS 变量或 Less 变量,X 则直接走 CSS-in-JS 的 token 体系,改起来更符合现代 React 团队的工程习惯。
3. 实测下来的关键差异:集成、定制、流式、移动端、体积
3.1 集成速度:ChatUI 五分钟跑通,X 需要先接受它的世界观
ChatUI 的接入成本确实低。装包、引入组件和 CSS,创建 messages 数组,再绑定 onSend,一个能发消息、能展示回复的最小闭环就有了。它不要求你额外引入 antd,也不强制你用某个状态管理方案,对老项目非常友好。
Ant Design X 这边,官方要求 React 18 以上,并且底层依赖 antd v5。如果你的项目当前还在 React 16 或者 antd v4,升级就是一笔明面上的成本。而且 X 更希望你按照它的 XProvider、Bubble、useXChat 这套结构组织代码,第一次用会有一个从“我调组件”到“我接入框架”的思维转换期。但一旦接顺了,后面做欢迎页、推荐问题、思维链这些模块会非常快。
说实话,如果只看“从安装到跑通”的时间,ChatUI 赢。但再看“从跑通到完成一个完整 AI 应用”的总时间,X 通常是反超的。
3.2 自定义消息卡片:ChatUI 用渲染器,X 直接交给 ReactNode
我在两个库里都实现了一个“商品推荐卡片”的业务消息。
ChatUI 的做法是先给数据加一个自定义 type:{ type: 'product', content: { id, title, price } },然后在renderMessageContent里加一个 switch 分支返回商品卡片组件。消息一多,分支也变多,需要维护一个“消息类型 → 渲染函数”的映射关系。
Ant Design X 的做法更简单,在获取到模型结果后,直接把<ProductCard data={...} />作为 content 塞给对应气泡。它有赖于“服务端返回结构化数据后由前端映射成 ReactNode”这一套思维。在 X 里,单个气泡的渲染就是 ReactNode,天然可以做富交互,不需要碰消息协议。
论极端灵活性,ChatUI 的消息级协议在跨端、日志回放、消息持久化方面更有优势;论写业务代码的速度,X 直接塞组件明显更爽。
3.3 流式输出体验:setTyping 与 onUpdate 的差别
ChatUI 对“AI 正在输入”的原生模拟是 setTyping 和 typing 类型的消息。你需要自己控制时序:先 setTyping(true),等异步结果回来后 appendMsg 追加完整消息,再 setTyping(false)。好处是直观、好理解,坏处是所有增量逻辑都要自己维护,尤其是处理“打字中”状态和最后消息替换的边界时,很容易在时序上出小 bug。
Ant Design X 的流式更彻底。agent 的onUpdate可以不断更新当前生成中的消息内容,界面上的气泡会跟着平滑变化。配合onSuccess/onError收尾,整个生成生命周期是框架托管的。做惯了大模型应用的团队,会觉得这套流程非常“对味”。
但要提醒一点:X 的这种流式能力是有前提的,后端必须支持真正的流式输出,或者你至少要在前端正确处理 ReadableStream。如果后端只是普通 JSON 一次性返回,那 X 的流式优势是发挥不出来的,这时候用 ChatUI 也完全够。
3.4 移动端与响应式:ChatUI 天生友好,X 需要自己做
ChatUI 从一开始就考虑了移动端场景,触控、滑动、安全区适配这些细节都有内置处理。做一个手机端客服聊天页,它基本能直接顶上去。
Ant Design X 目前的设计体系更偏桌面 Web,组件本身是响应式的,但移动端不是一个零成本的加分项:你需要结合自己的媒体查询、viewport 处理,去适配输入区、气泡宽度和顶部导航。如果你的核心场景在手机端,这点在选择时要非常冷静地评估。
我测过 X 在小屏幕下跑通业务流程没有问题,但那种“顺手”感确实比 ChatUI 差一些。X 的组件更关注信息结构和交互链路,而不是某一个通道的设备细节。
3.5 包体积和与 antd 的耦合程度
这一点很难绕开。项目里如果已经在用 antd v5,那么 Ant Design X 的增量成本主要是它自己的组件代码,体积增加在可接受范围内;但如果项目是干净的 React 技术栈,为了一个聊天窗口把 antd 全家桶引进来,首屏体积会有肉眼可见的增长,尤其是中后台表格、表单这类组件被打包工具一起带进来的时候。
ChatUI 自身相对更独立,体积控制也更好。它内置的消息类型和样式是裁剪过的,不像 antd 那样需要维护一个庞大的组件体系。但换来的是,如果业务里还需要 antd 的表格、弹窗、表单,你得同时维护两套 UI 体系的风格统一。
所以体积问题不能单独看两个库本身,要看“它们进到你的项目里之后带来的总增量”。
4. 一张表看清全部维度差异
| 对比维度 | ChatUI | Ant Design X |
|---|---|---|
| 定位 | 对话式 UI 组件库 | AI 原生应用前端解决方案 |
| 核心范围 | 聊天窗口内部 | 欢迎页、对话、建议、思维链、语音等全链路 |
| 发布方 | 阿里 | Ant Design 团队 |
| 消息模型 | type + content 协议 | items / ReactNode + hooks |
| 自定义消息 | 消息级渲染器,自由扩展 type | 组件级 ReactNode,直接嵌入 |
| 流式输出 | 手动 setTyping + appendMsg | useXAgent / useXChat 托管 |
| 移动端适配 | 内置支持较好 | 偏桌面,需自行适配 |
| antd 依赖 | 独立 | 依赖 antd v5、React 18 |
| 学习成本 | 低 | 中等,需要理解 AI 状态流 |
| 长期维护 | 更新节奏明显放缓 | 社区活跃,迭代快 |
| 适用产品形态 | 客服、IM、消息密集型聊天 | 完整 AI 助手、知识问答、Agent 应用 |
4.1 “谁更轻”不等于“谁更好”
如果只是看接入速度和包体积,ChatUI 确实占优。但很多项目最终放弃 ChatUI,不是因为不好用,而是因为产品演化到后面必然长出欢迎页、推荐问题、思考过程展示这些模块,这些在 X 里是现成的,在 ChatUI 的体系里需要重新拼装。
我见过一个团队用 ChatUI 做了半年客服系统,后来要升级成带“建议问题”和“对话摘要”的智能助手,最后前端那一大坨自定义渲染代码重构了很久。不是说 ChatUI 扩展不了,而是“对话外部”的东西它本就不管。
4.2 维护活跃度是隐藏的选型成本
ChatUI 的核心功能已经很稳定,但 GitHub 上的 issue 和 PR 处理速度明显偏慢。社区里很多问题要靠自己翻源码解决。Ant Design X 更新很快,新组件和新 API 一直在加,但也意味着 API 还在演进,生产环境接入时要做好版本锁定和升级计划。
选型时一定要把“这个库未来一年会不会还有人维护”算进成本。如果是做生命周期很长的企业内部系统,一个停滞的组件库带来的风险远比想象中大。
5. 接入过程中的踩坑实录
5.1 ChatUI 的自定义消息映射,别写成一个巨型 switch
我最初在 ChatUI 里加自定义消息时,渲染器里写满了一个又一个 case。等到第八种消息类型的时候,整个函数已经没法看了。
后来我把消息类型拆成了一个映射表:
const messageRenderers = { product: ProductMessage, order: OrderMessage, system: SystemMessage, }; const renderMessageContent = (message) => { const Renderer = messageRenderers[message.type]; return Renderer ? <Renderer data={message.content} /> : null; };这样每种消息类型只维护自己的渲染组件,代码清晰很多。如果你决定用 ChatUI,强烈建议一开始就按映射表组织,不要图省事堆 switch。
5.2 Ant Design X 不会帮你渲染 Markdown
这是我在 X 上遇到的第一个“坑”。Bubble 的 content 直接传一串 markdown 字符串,渲染出来就是纯文本,没有任何标题、加粗、代码块样式。原因很简单:X 只负责气泡结构,不负责内容解析。
解决办法也不复杂,自己接一个 markdown 渲染库即可:
import ReactMarkdown from 'react-markdown'; <Bubble content={ <ReactMarkdown> {'# 标题\n\n正文内容'} </ReactMarkdown> } />如果你在 X 里要展示代码块、表格、链接预览,最好尽早统一封装一个 “AI 回答内容组件”,把 markdown、代码高亮、内部链接跳转全部收敛在一个地方。不然后面每个气泡都得改。
5.3 流式请求的中断:AbortController 必须自己接
接 X 的流式输出时,我最初只处理了 onUpdate 和 onSuccess,忽略了取消逻辑。结果用户点击“停止生成”后,前端消息是停了,但后端请求还在继续跑,浪费 token 不说,下一轮消息还会串状态。
后面我改成在 agent.request 里接收并传递 abort 信号:
const [agent] = useXAgent({ request: async ({ message }, { onUpdate, onSuccess, signal }) => { const response = await fetch('/api/chat', { method: 'POST', body: JSON.stringify({ message }), signal, }); // 读取 reader 时同样监听 signal }, });这种做法虽然看起来只是加了一个参数,但实际交互体验差别很大。用户中断生成后,UI 层和请求层都能同步停下来,不会出现“前端停了后端还在跑”的尴尬。
5.4 版本兼容与升级风险
ChatUI 的优势是很稳定,但稳定也意味着陈旧。它的样式方案和一些内部实现带着早期 React 生态的影子,如果你的项目用了最新的构建工具链,CSS 加载顺序、主题覆盖之类的细节需要多点耐心解决。
Ant Design X 则是另一个方向的坑:升级太快。我踩过一次小版本升级后某个组件 props 调整导致类型报错的情况。锁版本、跟进 changelog、在预发环境跑一轮回归,这些流程在 X 项目里不能省。
6. 怎么选:按产品形态、技术栈和维护预期来决策
6.1 快速原型和内部工具
如果只是做一个内部用的知识问答工具,或者验证 AI 功能的 demo,ChatUI 是最快出效果的选择。它不需要你建立复杂的组件体系,一个页面几十行代码就能跑起来,改起来也快。
6.2 完整的 AI 产品页面
如果你的产品是一个对外发布的 AI 助手,涉及欢迎页、推荐问题、思考过程可视化、多模态消息,那 Ant Design X 的整体链路设计能帮你少走大量弯路。这些模块散着做,光是样式统一和交互状态的衔接就够喝一壶。
6.3 移动端优先或客服系统
移动端优先、会话密集型、需要大量自定义气泡的场景,ChatUI 可能更合适。它在消息类型扩展、移动端适配方面是经过生产验证的,逻辑很成熟。
6.4 深度定制型业务系统
如果业务消息非常复杂,每一条消息都对应不同的业务组件,而且这些组件要和内部基建深度结合,我的建议是分开看:消息结构用 ChatUI 的类型协议思路做,内容展示直接用 ReactNode 思维。这种场景下,X 的高层封装反而可能碍事,你最终会绕过它自己拼。
6.5 我最后的选择和理由
我在内部 AI 问答助手里选了 Ant Design X,核心原因只有两条:团队技术栈已经在 antd 体系内,以及产品需要欢迎页、推荐问题、思维链这些完整链路。ChatUI 很好,但它对“完整 AI 应用”的覆盖范围不够,我不想在聊天区外面再维护一套风格独立的组件。
但我也要强调,如果换一个侧重移动端客服或者说重度自定义消息卡片的产品,我大概率会回头用 ChatUI,甚至直接基于它的消息协议自己封装一层。
啰嗦了这么多,最后说点我自己的体会。选 UI 库这件事,最怕的就是抛开使用场景去看谁更先进。ChatUI 和 Ant Design X 都有非常明确的适用边界:你的产品如果只是需要一个会话窗口,ChatUI 的轻量和消息渲染器设计会让你很舒服;如果你的产品是一个完整的 AI 应用,欢迎区、建议提示、思维链这些是绕不开的,那 Ant Design X 提供的完整链路价值就体现出来了。选型没有标准答案,只有合不合适。判断小技巧也很简单:先画出你的页面结构,如果页面里除了聊天列表还有很多 AI 特有的模块,那这题答案基本已经出来了。