npx skills add https://github.com/assistant-ui/tool-ui --skill tool-ui使用此技能可快速从需求过渡到可运行的 Tool UI 集成。
如果项目没有现成的聊天界面/运行时,建议使用 assistant-ui。如果应用已有可用的运行时,则将 assistant-ui 视为可选。
读取用户项目中的 components.json 并验证:
components.json 是否存在。推荐方式(AI 辅助集成):
npx tool-agent "integrate the <component> component"
使用下方目录中的组件特定提示(例如 integrate the plan component for step-by-step task workflows with status tracking)。
替代方式(直接注册表安装):
npx shadcn@latest add @tool-ui/<component-id>
安装多个组件:
npx tool-agent "integrate the plan, progress-tracker, and approval-card components for planning flows"
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
或通过 shadcn:
npx shadcn@latest add @tool-ui/plan @tool-ui/progress-tracker @tool-ui/approval-card
所有 25 个 Tool UI 组件及其对应的 tool-agent 提示和 shadcn 安装命令:
进度
| 组件 | 描述 | tool-agent 提示 | shadcn |
|---|---|---|---|
plan | 带状态跟踪的分步任务工作流 | integrate the plan component for step-by-step task workflows with status tracking | npx shadcn@latest add @tool-ui/plan |
progress-tracker | 多步骤操作的实时状态反馈 | integrate the progress tracker component for real-time status feedback on multi-step operations | npx shadcn@latest add @tool-ui/progress-tracker |
输入
| 组件 | 描述 | tool-agent 提示 | shadcn |
|---|---|---|---|
option-list | 让用户从多个选项中选择 | integrate the option list component to let users select from multiple choices | npx shadcn@latest add @tool-ui/option-list |
parameter-slider | 数值参数调整控件 | integrate the parameter slider component for numeric parameter adjustment controls | npx shadcn@latest add @tool-ui/parameter-slider |
preferences-panel | 用于用户偏好的紧凑设置面板 | integrate the preferences panel component for compact user settings | npx shadcn@latest add @tool-ui/preferences-panel |
question-flow | 带分支的多步骤引导式问题 | integrate the question flow component for multi-step guided questions with branching | npx shadcn@latest add @tool-ui/question-flow |
显示
| 组件 | 描述 | tool-agent 提示 | shadcn |
|---|---|---|---|
citation | 显示带有归属信息的来源引用 | integrate the citation component to display source references with attribution | npx shadcn@latest add @tool-ui/citation |
item-carousel | 用于浏览集合的水平轮播图 | integrate the item carousel component for horizontal browsing of collections | npx shadcn@latest add @tool-ui/item-carousel |
link-preview | 带有 Open Graph 数据的丰富链接预览 | integrate the link preview component for rich link previews with Open Graph data | npx shadcn@latest add @tool-ui/link-preview |
stats-display | 视觉网格中的关键指标和 KPI | integrate the stats display component for key metrics and KPIs in a visual grid | npx shadcn@latest add @tool-ui/stats-display |
terminal | 显示命令行输出和日志 | integrate the terminal component to show command-line output and logs | npx shadcn@latest add @tool-ui/terminal |
weather-widget | 带有预报和天气状况的天气显示 | integrate the weather widget component for weather display with forecasts and conditions | npx shadcn@latest add @tool-ui/weather-widget |
产物
| 组件 | 描述 | tool-agent 提示 | shadcn |
|---|---|---|---|
chart | 使用交互式图表可视化数据(需要 recharts) | integrate the chart component to visualize data with interactive charts | npx shadcn@latest add @tool-ui/chart |
code-block | 显示语法高亮的代码片段 | integrate the code block component for syntax-highlighted code snippets | npx shadcn@latest add @tool-ui/code-block |
code-diff | 比较代码变更,带有语法高亮的差异(需要 @pierre/diffs) | integrate the code diff component to compare code changes with syntax-highlighted diffs | npx shadcn@latest add @tool-ui/code-diff |
data-table | 在可排序表格中呈现结构化数据 | integrate the data table component to present structured data in sortable tables | npx shadcn@latest add @tool-ui/data-table |
message-draft | 在发送前审查和批准消息 | integrate the message draft component to review and approve messages before sending | npx shadcn@latest add @tool-ui/message-draft |
instagram-post | 渲染 Instagram 帖子预览 | integrate the instagram post component to render Instagram post previews | npx shadcn@latest add @tool-ui/instagram-post |
linkedin-post | 渲染 LinkedIn 帖子预览 | integrate the linkedin post component to render LinkedIn post previews | npx shadcn@latest add @tool-ui/linkedin-post |
x-post | 渲染 X 帖子预览 | integrate the x post component to render X/Twitter post previews | npx shadcn@latest add @tool-ui/x-post |
确认
| 组件 | 描述 | tool-agent 提示 | shadcn |
|---|---|---|---|
approval-card | 用于智能体操作的二元确认 | integrate the approval card component for binary confirmation of agent actions | npx shadcn@latest add @tool-ui/approval-card |
order-summary | 显示带有明细定价的购买项 | integrate the order summary component to display purchases with itemized pricing | npx shadcn@latest add @tool-ui/order-summary |
媒体
| 组件 | 描述 | tool-agent 提示 | shadcn |
|---|---|---|---|
audio | 带有封面和元数据的音频播放 | integrate the audio component for audio playback with artwork and metadata | npx shadcn@latest add @tool-ui/audio |
image | 显示带有元数据和归属信息的图像 | integrate the image component to display images with metadata and attribution | npx shadcn@latest add @tool-ui/image |
image-gallery | 带有全屏灯箱查看器的瀑布流网格 | integrate the image gallery component with masonry grid and lightbox viewer | npx shadcn@latest add @tool-ui/image-gallery |
video | 带有控件和海报的视频播放 | integrate the video component for video playback with controls and poster | npx shadcn@latest add @tool-ui/video |
显示(地理地图)
| 组件 | 描述 | tool-agent 提示 | shadcn |
|---|---|---|---|
geo-map | 显示地理定位实体和车队位置 | integrate the geo map component to display geolocated entities and fleet positions | npx shadcn@latest add @tool-ui/geo-map |
tool-agent(推荐):
npx tool-agent "integrate the plan, progress-tracker, and approval-card components for planning flows"
npx tool-agent "integrate citation, link-preview, code-block, and code-diff for research output"
npx tool-agent "integrate data-table, chart, and stats-display for data visualization"
npx tool-agent "integrate image, image-gallery, video, and audio for media display"
shadcn(直接):
npx shadcn@latest add @tool-ui/plan @tool-ui/progress-tracker @tool-ui/approval-card
npx shadcn@latest add @tool-ui/citation @tool-ui/link-preview @tool-ui/code-block @tool-ui/code-diff
npx shadcn@latest add @tool-ui/data-table @tool-ui/chart @tool-ui/stats-display
# npm i recharts # chart 的 peer 依赖
npx shadcn@latest add @tool-ui/image @tool-ui/image-gallery @tool-ui/video @tool-ui/audio
安装组件后,通过 Toolkit 将它们接入 assistant-ui。本节涵盖完整的设置过程:提供者、运行时、工具包文件和 ID 处理。
创建一个助手包装器,提供运行时、传输和工具:
"use client";
import { lastAssistantMessageIsCompleteWithToolCalls } from "ai";
import { AssistantRuntimeProvider, Tools, useAui } from "@assistant-ui/react";
import {
AssistantChatTransport,
useChatRuntime,
} from "@assistant-ui/react-ai-sdk";
import { Thread } from "@/components/assistant-ui/thread";
import { toolkit } from "@/components/toolkit";
export const Assistant = () => {
const runtime = useChatRuntime({
transport: new AssistantChatTransport({ api: "/api/chat" }),
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
});
const aui = useAui({ tools: Tools({ toolkit }) });
return (
<AssistantRuntimeProvider runtime={runtime} aui={aui}>
<div className="h-dvh">
<Thread />
</div>
</AssistantRuntimeProvider>
);
};
关键点:
useChatRuntime + AssistantChatTransport:连接到你的聊天 API。Tools({ toolkit }):将工具定义和渲染器转发给模型。sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls:在工具调用后自动继续(可选,但在工具密集型流程中很常见)。创建一个单一的 toolkit.ts(或 toolkit.tsx)文件,导出一个 Toolkit 对象。每个键是一个工具名称;每个值包含 type、description、parameters 和 render。
工具描述 — 每个工具都必须包含 description。描述何时调用该工具及其扮演的角色,而不是描述可见内容。Tool UI 组件已经将有效载荷(选项、计划、图表等)渲染给用户;重复该内容的描述是多余的。优先使用面向模型的指导(例如“呈现一个供用户遵循的计划”或“让用户选择一个选项”),而不是内容回显(例如“显示带有标签和描述的选项列表”)。
addResult 提交 | 工具实现在服务器端;模型返回结果execute | 必需 — 在客户端运行工具 UI 流程 | 不需要parameters | 必需(模型参数的 schema) | 不使用,如果后端 llm 通过 aisdk 完成,则使用 inputSchemarender | 必需(用于参数、状态、结果、addResult 的 UI) | 必需(用于 result 的 UI)后端工具(模型返回结果;无需用户输入):
import { type Toolkit } from "@assistant-ui/react";
import { Plan } from "@/components/tool-ui/plan";
import { safeParseSerializablePlan } from "@/components/tool-ui/plan/schema";
export const toolkit: Toolkit = {
showPlan: {
type: "backend",
render: ({ result }) => {
const parsed = safeParseSerializablePlan(result);
if (!parsed) return null;
return <Plan {...parsed} />;
},
},
};
前端工具(模型发送参数;用户交互通过 addResult 提交):
import { type Toolkit } from "@assistant-ui/react";
import { OptionList } from "@/components/tool-ui/option-list";
import {
SerializableOptionListSchema,
safeParseSerializableOptionList,
} from "@/components/tool-ui/option-list/schema";
const optionListTool: Toolkit[string] = {
description: "Render selectable options with confirm and clear actions.",
parameters: SerializableOptionListSchema,
render: ({ args, toolCallId, result, addResult }) => {
const parsed = safeParseSerializableOptionList({
...args,
id: args?.id ?? `option-list-${toolCallId}`,
});
if (!parsed) return null;
if (result) {
return <OptionList {...parsed} choice={result} />;
}
return (
<OptionList
{...parsed}
onAction={async (actionId, selection) => {
if (actionId === "confirm" || actionId === "cancel") {
await addResult?.(selection);
}
}}
/>
);
},
};
export const toolkit: Toolkit = {
option_list: optionListTool,
approval_card: {
/* ... */
},
};
当聊天 API 使用 AI SDK(streamText)时,使用 ai 中的 tool() 定义后端工具:
使用 inputSchema
后端工具在服务器端使用 execute;结果被流式传输并通过工具包的 render 函数渲染
import { streamText, tool, convertToModelMessages } from "ai"; import { openai } from "@ai-sdk/openai"; import { z } from "zod";
// 对于前端工具:...frontendTools(clientTools) — clientTools 通过 AssistantChatTransport 从请求体中获取 const result = streamText({ model: openai("gpt-4o"), messages: await convertToModelMessages(messages), tools: { get_weather: tool({ description: "Get the current weather and forecast for a location. Returns data to display in a weather widget.", inputSchema: z.object({ location: z.string().describe("City name, e.g. 'San Francisco'"), units: z .enum(["celsius", "fahrenheit"]) .default("fahrenheit") .describe("Temperature unit"), }), execute: async ({ location, units }) => { // 获取天气数据,返回与你的 widget schema 匹配的形状 return { location, units /* ... */ }; }, }), }, });
| 模式 | 组件 | 用法 |
|---|---|---|
| 以操作为中心 | OptionList, ParameterSlider, PreferencesPanel, ApprovalCard | 直接连接 onAction 或 onConfirm/onCancel;无需 ToolUI 包装器。传递 choice={result} 用于回执状态。 |
| 复合 | OrderSummary, DataTable 等 | 用 ToolUI + ToolUI.Surface + ToolUI.Actions 包装;使用 DecisionActions 或 LocalActions。 |
以操作为中心的示例(OptionList):
return (
<OptionList
{...parsed}
onAction={async (actionId, selection) => {
if (actionId === "confirm" || actionId === "cancel") {
await addResult?.(selection);
}
}}
/>
);
复合示例(带有 DecisionActions 的 OrderSummary):
return (
<ToolUI id={parsed.id}>
<ToolUI.Surface>
<OrderSummary {...parsed} />
</ToolUI.Surface>
<ToolUI.Actions>
<ToolUI.DecisionActions
actions={[
{ id: "cancel", label: "Cancel", variant: "outline" },
{ id: "confirm", label: "Purchase" },
]}
onAction={(action) =>
createDecisionResult({ decisionId: parsed.id, action })
}
onCommit={(decision) => addResult?.(decision)}
/>
</ToolUI.Actions>
</ToolUI>
);
ApprovalCard 使用嵌入式操作;直接连接 onConfirm/onCancel:
return (
<ApprovalCard
{...parsed}
choice={
result === "approved" || result === "denied" ? result : parsed.choice
}
onConfirm={async () => addResult?.("approved")}
onCancel={async () => addResult?.("denied")}
/>
);
Tool UI 使用两个操作界面,作为复合兄弟组件在显示组件外部渲染:
ToolUI.LocalActions:非关键性副作用(导出、复制、打开链接)。处理函数不得调用 addResult(...)。ToolUI.DecisionActions:关键性选择,通过 createDecisionResult(...) 产生一个 DecisionResult 信封。提交回调会调用 addResult(...)。带有操作的显示组件的复合包装器模式:
<ToolUI id={surfaceId}>
<ToolUI.Surface>
<DataTable {...props} />
</ToolUI.Surface>
<ToolUI.Actions>
<ToolUI.LocalActions
actions={[{ id: "export-csv", label: "Export CSV" }]}
onAction={(actionId) => {
/* side effects only */
}}
/>
</ToolUI.Actions>
</ToolUI>
有三个组件是以操作为中心的例外 — 它们保留嵌入式操作属性,而不是兄弟界面。这三个组件共享统一的接口:
actions:由组件渲染的操作按钮。onAction(actionId, state):在操作后运行并接收操作后的状态。onBeforeAction(actionId, state):在操作运行前评估的守卫。| 组件 | 传递给处理器的状态类型 |
|---|---|
OptionList | OptionListSelection |
ParameterSlider | SliderValue[] |
PreferencesPanel | PreferencesValue |
使用复合模式的组件:CodeBlock, CodeDiff, Terminal, ProgressTracker。
上下文通过 createContext + use()(React 19)共享。子组件如果在根组件外部使用会抛出错误。
具有结果的组件使用 choice 属性来渲染已确认/已完成的状态:
| 组件 | choice 类型 | 值 / 形状 |
|---|---|---|
ApprovalCard | `"approved" | "denied"` |
OptionList | `string | string[]` |
OrderSummary | OrderDecision | { action: "confirm", orderId?, confirmedAt? } |
ProgressTracker | ToolUIReceipt | { outcome, summary, identifiers?, at }(共享类型) |
当 choice 存在时,组件以回执模式渲染 — 只读,无操作。
description;为模型编写(何时调用,扮演什么角色)— 避免重复组件将要渲染的内容。注意:前端工具需要一个 execute 函数。后端工具的实现位于服务器端。后端工具不需要。忽略生成的文件。设置完成后:
每周安装数
162
代码仓库
GitHub 星标数
564
首次出现
2026年2月12日
安全审计
安装于
opencode158
gemini-cli156
codex156
github-copilot155
amp149
kimi-cli149
Use this skill to move from request to working Tool UI integration quickly.
Prefer assistant-ui when the project has no existing chat UI/runtime. Treat assistant-ui as optional when the app already has a working runtime.
Read components.json in the user's project and verify:
components.json exists.Preferred (AI-assisted integration):
npx tool-agent "integrate the <component> component"
Use component-specific prompts from the catalog below (e.g. integrate the plan component for step-by-step task workflows with status tracking).
Alternative (direct registry):
npx shadcn@latest add @tool-ui/<component-id>
Multiple components:
npx tool-agent "integrate the plan, progress-tracker, and approval-card components for planning flows"
Or via shadcn:
npx shadcn@latest add @tool-ui/plan @tool-ui/progress-tracker @tool-ui/approval-card
All 25 Tool UI components with tool-agent prompts and shadcn install commands:
Progress
| Component | Description | tool-agent prompt | shadcn |
|---|---|---|---|
plan | Step-by-step task workflows with status tracking | integrate the plan component for step-by-step task workflows with status tracking | npx shadcn@latest add @tool-ui/plan |
progress-tracker | Real-time status feedback for multi-step operations | integrate the progress tracker component for real-time status feedback on multi-step operations | npx shadcn@latest add @tool-ui/progress-tracker |
Input
| Component | Description | tool-agent prompt | shadcn |
|---|---|---|---|
option-list | Let users select from multiple choices | integrate the option list component to let users select from multiple choices | npx shadcn@latest add @tool-ui/option-list |
parameter-slider | Numeric parameter adjustment controls | integrate the parameter slider component for numeric parameter adjustment controls | npx shadcn@latest add @tool-ui/parameter-slider |
Display
| Component | Description | tool-agent prompt | shadcn |
|---|---|---|---|
citation | Display source references with attribution | integrate the citation component to display source references with attribution | npx shadcn@latest add @tool-ui/citation |
item-carousel | Horizontal carousel for browsing collections | integrate the item carousel component for horizontal browsing of collections | npx shadcn@latest add @tool-ui/item-carousel |
Artifacts
| Component | Description | tool-agent prompt | shadcn |
|---|---|---|---|
chart | Visualize data with interactive charts (needs recharts) | integrate the chart component to visualize data with interactive charts | npx shadcn@latest add @tool-ui/chart |
code-block | Display syntax-highlighted code snippets | integrate the code block component for syntax-highlighted code snippets |
Confirmation
| Component | Description | tool-agent prompt | shadcn |
|---|---|---|---|
approval-card | Binary confirmation for agent actions | integrate the approval card component for binary confirmation of agent actions | npx shadcn@latest add @tool-ui/approval-card |
order-summary | Display purchases with itemized pricing | integrate the order summary component to display purchases with itemized pricing | npx shadcn@latest add @tool-ui/order-summary |
Media
| Component | Description | tool-agent prompt | shadcn |
|---|---|---|---|
audio | Audio playback with artwork and metadata | integrate the audio component for audio playback with artwork and metadata | npx shadcn@latest add @tool-ui/audio |
image | Display images with metadata and attribution | integrate the image component to display images with metadata and attribution | npx shadcn@latest add @tool-ui/image |
Display (Geo Map)
| Component | Description | tool-agent prompt | shadcn |
|---|---|---|---|
geo-map | Display geolocated entities and fleet positions | integrate the geo map component to display geolocated entities and fleet positions | npx shadcn@latest add @tool-ui/geo-map |
tool-agent (recommended):
npx tool-agent "integrate the plan, progress-tracker, and approval-card components for planning flows"
npx tool-agent "integrate citation, link-preview, code-block, and code-diff for research output"
npx tool-agent "integrate data-table, chart, and stats-display for data visualization"
npx tool-agent "integrate image, image-gallery, video, and audio for media display"
shadcn (direct):
npx shadcn@latest add @tool-ui/plan @tool-ui/progress-tracker @tool-ui/approval-card
npx shadcn@latest add @tool-ui/citation @tool-ui/link-preview @tool-ui/code-block @tool-ui/code-diff
npx shadcn@latest add @tool-ui/data-table @tool-ui/chart @tool-ui/stats-display
# npm i recharts # peer for chart
npx shadcn@latest add @tool-ui/image @tool-ui/image-gallery @tool-ui/video @tool-ui/audio
After installing components, wire them into assistant-ui via a Toolkit. This section covers the full setup: provider, runtime, toolkit file, and ID handling.
Create an assistant wrapper that provides runtime, transport, and tools:
"use client";
import { lastAssistantMessageIsCompleteWithToolCalls } from "ai";
import { AssistantRuntimeProvider, Tools, useAui } from "@assistant-ui/react";
import {
AssistantChatTransport,
useChatRuntime,
} from "@assistant-ui/react-ai-sdk";
import { Thread } from "@/components/assistant-ui/thread";
import { toolkit } from "@/components/toolkit";
export const Assistant = () => {
const runtime = useChatRuntime({
transport: new AssistantChatTransport({ api: "/api/chat" }),
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
});
const aui = useAui({ tools: Tools({ toolkit }) });
return (
<AssistantRuntimeProvider runtime={runtime} aui={aui}>
<div className="h-dvh">
<Thread />
</div>
</AssistantRuntimeProvider>
);
};
Key points:
useChatRuntime + AssistantChatTransport: connects to your chat API.Tools({ toolkit }): forwards tool definitions and renderers to the model.sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls: auto-continues after tool calls (optional but common for tool-heavy flows).Create a single toolkit.ts (or toolkit.tsx) that exports a Toolkit object. Each key is a tool name; each value has type, description, parameters, and render.
Tool descriptions — Always include a description on every tool. Describe when to call the tool and what role it plays, not the visible content. The Tool UI component already renders the payload (options, plan, chart, etc.) to the user; a description that repeats that content is redundant. Prefer model-oriented guidance (e.g. "Present a plan for the user to follow" or "Let the user pick one option") over content echo (e.g. "Shows a list of options with labels and descriptions").
addResult | Tool implementation lives on the server; model returns the resultexecute | Required — runs the tool UI flow client-side | Not neededparameters | Required (schema for model args) | does not use, uses inputSchema instead if backend llm is done via aisdkrender | Required (UI for args, status, result, addResult) | Required (UI for result)Backend tools (model returns result; no user input):
import { type Toolkit } from "@assistant-ui/react";
import { Plan } from "@/components/tool-ui/plan";
import { safeParseSerializablePlan } from "@/components/tool-ui/plan/schema";
export const toolkit: Toolkit = {
showPlan: {
type: "backend",
render: ({ result }) => {
const parsed = safeParseSerializablePlan(result);
if (!parsed) return null;
return <Plan {...parsed} />;
},
},
};
Frontend tools (model sends args; user interaction commits via addResult):
import { type Toolkit } from "@assistant-ui/react";
import { OptionList } from "@/components/tool-ui/option-list";
import {
SerializableOptionListSchema,
safeParseSerializableOptionList,
} from "@/components/tool-ui/option-list/schema";
const optionListTool: Toolkit[string] = {
description: "Render selectable options with confirm and clear actions.",
parameters: SerializableOptionListSchema,
render: ({ args, toolCallId, result, addResult }) => {
const parsed = safeParseSerializableOptionList({
...args,
id: args?.id ?? `option-list-${toolCallId}`,
});
if (!parsed) return null;
if (result) {
return <OptionList {...parsed} choice={result} />;
}
return (
<OptionList
{...parsed}
onAction={async (actionId, selection) => {
if (actionId === "confirm" || actionId === "cancel") {
await addResult?.(selection);
}
}}
/>
);
},
};
export const toolkit: Toolkit = {
option_list: optionListTool,
approval_card: {
/* ... */
},
};
When the chat API uses the AI SDK (streamText), define backend tools with tool() from ai:
Use inputSchema
Backend tools use execute on the server; the result is streamed and rendered via the toolkit render function
import { streamText, tool, convertToModelMessages } from "ai"; import { openai } from "@ai-sdk/openai"; import { z } from "zod";
// With frontend tools: ...frontendTools(clientTools) — clientTools come from the request body via AssistantChatTransport const result = streamText({ model: openai("gpt-4o"), messages: await convertToModelMessages(messages), tools: { get_weather: tool({ description: "Get the current weather and forecast for a location. Returns data to display in a weather widget.", inputSchema: z.object({ location: z.string().describe("City name, e.g. 'San Francisco'"), units: z .enum(["celsius", "fahrenheit"]) .default("fahrenheit") .describe("Temperature unit"), }), execute: async ({ location, units }) => { // Fetch weather data, return shape matching your widget schema return { location, units /* ... */ }; }, }), }, });
| Pattern | Components | Usage |
|---|---|---|
| Action-centric | OptionList, ParameterSlider, PreferencesPanel, ApprovalCard | Wire onAction or onConfirm/onCancel directly; no ToolUI wrapper. Pass choice={result} for receipt state. |
Action-centric example (OptionList):
return (
<OptionList
{...parsed}
onAction={async (actionId, selection) => {
if (actionId === "confirm" || actionId === "cancel") {
await addResult?.(selection);
}
}}
/>
);
Compound example (OrderSummary with DecisionActions):
return (
<ToolUI id={parsed.id}>
<ToolUI.Surface>
<OrderSummary {...parsed} />
</ToolUI.Surface>
<ToolUI.Actions>
<ToolUI.DecisionActions
actions={[
{ id: "cancel", label: "Cancel", variant: "outline" },
{ id: "confirm", label: "Purchase" },
]}
onAction={(action) =>
createDecisionResult({ decisionId: parsed.id, action })
}
onCommit={(decision) => addResult?.(decision)}
/>
</ToolUI.Actions>
</ToolUI>
);
ApprovalCard uses embedded actions; wire onConfirm/onCancel directly:
return (
<ApprovalCard
{...parsed}
choice={
result === "approved" || result === "denied" ? result : parsed.choice
}
onConfirm={async () => addResult?.("approved")}
onCancel={async () => addResult?.("denied")}
/>
);
Tool UI uses two action surfaces, rendered as compound siblings outside the display component:
ToolUI.LocalActions: non-consequential side effects (export, copy, open link). Handlers must not call addResult(...).ToolUI.DecisionActions: consequential choices that produce a DecisionResult envelope via createDecisionResult(...). The commit callback calls addResult(...).Compound wrapper pattern for display components with actions:
<ToolUI id={surfaceId}>
<ToolUI.Surface>
<DataTable {...props} />
</ToolUI.Surface>
<ToolUI.Actions>
<ToolUI.LocalActions
actions={[{ id: "export-csv", label: "Export CSV" }]}
onAction={(actionId) => {
/* side effects only */
}}
/>
</ToolUI.Actions>
</ToolUI>
Three components are action-centric exceptions — they keep embedded action props instead of sibling surfaces. All three share a unified interface:
actions: action buttons rendered by the component.onAction(actionId, state): runs after the action and receives post-action state.onBeforeAction(actionId, state): guard evaluated before an action runs.| Component | State type passed to handlers |
|---|---|
OptionList | OptionListSelection |
ParameterSlider | SliderValue[] |
PreferencesPanel | PreferencesValue |
Components using the compound pattern: CodeBlock, CodeDiff, Terminal, ProgressTracker.
Context is shared via createContext + use() (React 19). Subcomponents throw if used outside their Root.
Components with outcomes use a choice prop to render confirmed/completed state:
| Component | choice type | Values / shape |
|---|---|---|
ApprovalCard | `"approved" | "denied"` |
OptionList | `string | string[]` |
OrderSummary | OrderDecision | { action: "confirm", orderId?, confirmedAt? } |
ProgressTracker |
When choice is present, the component renders in receipt mode — read-only, no actions.
description; write it for the model (when to call, what role) — avoid repeating content the component will render.Notes: Frontend tools need an execute function Backend tools have the tool implementation on the server side. Backend tool don't need either Ignore the generated files After setup:
Weekly Installs
162
Repository
GitHub Stars
564
First Seen
Feb 12, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
opencode158
gemini-cli156
codex156
github-copilot155
amp149
kimi-cli149
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
106,200 周安装
preferences-panel | Compact settings panel for user preferences | integrate the preferences panel component for compact user settings | npx shadcn@latest add @tool-ui/preferences-panel |
question-flow | Multi-step guided questions with branching | integrate the question flow component for multi-step guided questions with branching | npx shadcn@latest add @tool-ui/question-flow |
link-preview | Rich link previews with Open Graph data | integrate the link preview component for rich link previews with Open Graph data | npx shadcn@latest add @tool-ui/link-preview |
stats-display | Key metrics and KPIs in a visual grid | integrate the stats display component for key metrics and KPIs in a visual grid | npx shadcn@latest add @tool-ui/stats-display |
terminal | Show command-line output and logs | integrate the terminal component to show command-line output and logs | npx shadcn@latest add @tool-ui/terminal |
weather-widget | Weather display with forecasts and conditions | integrate the weather widget component for weather display with forecasts and conditions | npx shadcn@latest add @tool-ui/weather-widget |
npx shadcn@latest add @tool-ui/code-block |
code-diff | Compare code changes with syntax-highlighted diffs (needs @pierre/diffs) | integrate the code diff component to compare code changes with syntax-highlighted diffs | npx shadcn@latest add @tool-ui/code-diff |
data-table | Present structured data in sortable tables | integrate the data table component to present structured data in sortable tables | npx shadcn@latest add @tool-ui/data-table |
message-draft | Review and approve messages before sending | integrate the message draft component to review and approve messages before sending | npx shadcn@latest add @tool-ui/message-draft |
instagram-post | Render Instagram post previews | integrate the instagram post component to render Instagram post previews | npx shadcn@latest add @tool-ui/instagram-post |
linkedin-post | Render LinkedIn post previews | integrate the linkedin post component to render LinkedIn post previews | npx shadcn@latest add @tool-ui/linkedin-post |
x-post | Render X post previews | integrate the x post component to render X/Twitter post previews | npx shadcn@latest add @tool-ui/x-post |
image-gallery | Masonry grid with fullscreen lightbox viewer | integrate the image gallery component with masonry grid and lightbox viewer | npx shadcn@latest add @tool-ui/image-gallery |
video | Video playback with controls and poster | integrate the video component for video playback with controls and poster | npx shadcn@latest add @tool-ui/video |
| Compound | OrderSummary, DataTable, etc. | Wrap in ToolUI + ToolUI.Surface + ToolUI.Actions; use DecisionActions or LocalActions. |
ToolUIReceipt |
{ outcome, summary, identifiers?, at } (shared type) |