数据截至 (上游 commit 2822885e57e7)
无样式 UI 组件:ai-react-ui 的 render-prop 组合件
30 秒导读:
@tanstack/ai-react-ui是一层无样式(headless)的 React 组件。它不替你决定聊天界面长什么样——它只负责结构和行为:把消息按 part 类型分发渲染、把输入框接到发送逻辑、把工具审批按钮接到approve/deny。样式和最终呈现,它通过两个口子完全交还给你:render prop(你自己写 JSX)和data-*属性(你自己写 CSS)。
本章讲透 packages/ai-react-ui 的全部源码。它是第 4 章 useChat 之上、离终端用户最近的一层。第 4 章讲的是 hook 内部怎么把 class 状态机桥进 React;本章不重复那部分,只讲这层组件如何把 hook 的状态摆到屏幕上、又如何把控制权让出去。
1. 这是什么(零基础也能懂)
一句话定义
它是一组只有骨架、没有皮肤的聊天组件。你给它一个连接(connection),它帮你把「用户消息 / AI 回复 / 思考过程 / 工具调用 / 工具审批」这些结构摆好、把「输入即发送」「点击即审批」这些行为接好——但每一块具体长什么样、用什么颜色什么圆角,由你说了算。
解决什么问题 / 给谁用
假设你要做一个 AI 聊天页。AI 的回复不再是一段纯文本,而是混合了好几种"零件":一段思考(thinking)、一段正文(text)、一次工具调用(tool-call,可能还要你点"批准")、一段工具返回(tool-result)。手写这套分发逻辑很烦,但市面上"开箱即用"的聊天组件又往往带死了一套设计——你想换个风格就得跟它的 CSS 打架。
这层库走第三条路:结构和行为我给你写好,样式一行不塞(默认渲染只给最朴素的兜底)。你要么用 className + data-* 属性写 CSS,要么用 render prop 把整块 JSX 换成你自己的。给谁用:想要完全掌控外观、又不想重写消息分发/流式/审批逻辑的前端工程师。
它能做什么
- 按 part 类型分发渲染一条消息:text / thinking / tool-call / tool-result 各有各的渲染分支。
- 原生支持工具审批:渲染 approve / deny 按钮,点击直接回写到聊天状态。
- 流式友好:思考中的 part 会在正文出现后自动折叠。
- 每一处都能被接管:从"换一个 text 渲染器"到"整个输入框自己画",都有 render prop。
- Markdown 正文:内置 GFM + 语法高亮 + XSS 消毒,且插件链可扩展/可整条替换。
用起来什么样
最小用法就三个组件叠在一起(示意,基于 src/index.ts:14-24 的文档示例):
import { Chat, ChatMessages, ChatInput, ChatMessage } from '@tanstack/ai-react-ui'
// <Chat> 建立共享状态;<ChatMessages> 渲染列表;<ChatInput> 收发消息
<Chat connection={fetchServerSentEvents('/api/chat')}>
<ChatMessages>
{(message) => <ChatMessage message={message} />}
</ChatMessages>
<ChatInput />
</Chat>
三个组件是平级的兄弟,靠 <Chat> 提供的 React context 串起来——不需要你手动把 messages、sendMessage 一层层往下传。
一句话直觉
把它想成"毛坯房 + 水电已通"。 墙、地、门框都立好了(结构),开关一按灯就亮(行为),但刷什么漆、铺什么地板由你定(样式)。它给的"默认装修"只是让你能立刻住进去看效果,随时可以整面墙推倒重装(render prop)。