# Generative UI
URL: /docs/guides/tool-ui
Render tool calls as interactive UI instead of plain text.
***
title: Generative UI
description: Render tool calls as interactive UI instead of plain text.
-----------------------------------------------------------------------
import { ToolUISample } from "@/components/docs/samples/tool-ui-sample";
Create custom UI components for AI tool calls, providing visual feedback and interactive experiences when tools are executed.
## Overview
Tool UIs in assistant-ui allow you to create custom interfaces that appear when AI tools are called. These generative UI components enhance the user experience by:
* **Visualizing tool execution** with loading states and progress indicators
* **Displaying results** in rich, formatted layouts
* **Enabling user interaction** through forms and controls
* **Providing error feedback** with helpful recovery options
This guide demonstrates building tool UIs with the **Vercel AI SDK**.
## Creating Tool UIs
There are two main approaches to creating tool UIs in assistant-ui:
### 1. Client-Defined Tools (`makeAssistantTool`)
If you're creating tools on the client side, use `makeAssistantTool` to register them with the assistant context. Then create a UI component with `makeAssistantToolUI`:
```tsx
import { makeAssistantTool, tool } from "@assistant-ui/react";
import { z } from "zod";
// Define the tool
const weatherTool = tool({
description: "Get current weather for a location",
parameters: z.object({
location: z.string(),
unit: z.enum(["celsius", "fahrenheit"]),
}),
execute: async ({ location, unit }) => {
const weather = await fetchWeatherAPI(location, unit);
return weather;
},
});
// Register the tool
const WeatherTool = makeAssistantTool({
...weatherTool,
toolName: "getWeather",
});
// Create the UI
const WeatherToolUI = makeAssistantToolUI<
{ location: string; unit: "celsius" | "fahrenheit" },
{ temperature: number; description: string }
>({
toolName: "getWeather",
render: ({ args, result, status }) => {
if (status.type === "running") {
return
);
},
});
```
Tools defined with `makeAssistantTool` can be passed to your backend using the
`frontendTools` utility
Learn more about creating tools in the [Tools Guide](/docs/guides/tools).
### 2. UI-Only for Existing Tools (`makeAssistantToolUI`)
If your tool is defined elsewhere (e.g., in your backend API, MCP server, or LangGraph), use `makeAssistantToolUI` to create just the UI component:
```tsx
import { makeAssistantToolUI } from "@assistant-ui/react";
const WeatherToolUI = makeAssistantToolUI<
{ location: string; unit: "celsius" | "fahrenheit" },
{ temperature: number; description: string }
>({
toolName: "getWeather", // Must match the backend tool name
render: ({ args, result, status }) => {
// UI rendering logic only
},
});
```
## Quick Start Example
This example shows how to implement the UI-only approach using `makeAssistantToolUI`:
### Create a Tool UI Component
```tsx
import { makeAssistantToolUI } from "@assistant-ui/react";
import { z } from "zod";
type WeatherArgs = {
location: string;
unit: "celsius" | "fahrenheit";
};
type WeatherResult = {
temperature: number;
description: string;
humidity: number;
windSpeed: number;
};
const WeatherToolUI = makeAssistantToolUI({
toolName: "getWeather",
render: ({ args, status, result }) => {
if (status.type === "running") {
return (
);
},
});
```
### Hook Pattern
Use hooks for dynamic tool UI registration:
When you assign your `makeAssistantToolUI({...})` call to a constant starting with `use…`, you can call it directly as a hook inside your component. This pattern lets you access local props or state when rendering the tool UI.
```tsx
import { useAssistantToolUI } from "@assistant-ui/react";
function DynamicToolUI() {
const [theme, setTheme] = useState("light");
useAssistantToolUI({
toolName: "analyzeData",
render: ({ args, result, status }) => {
// Hook allows access to component state
return (
);
},
});
return null;
}
```
### Inline Pattern
For tools that need access to parent component props:
**Why `useInlineRender`?** By default, a tool UI's `render` function is
static. Use `useInlineRender` when your UI needs access to dynamic component
props (for example, to pass in an `id` or other contextual data).
```tsx
import { useAssistantToolUI, useInlineRender } from "@assistant-ui/react";
function ProductPage({ productId, productName }) {
useAssistantToolUI({
toolName: "checkInventory",
render: useInlineRender(({ args, result }) => {
// Access parent component props
return (
{productName} Inventory
Stock for {productId}: {result.quantity} units
Location: {result.warehouse}
);
}),
});
return
Product details...
;
}
```
## Interactive Tool UIs
### User Input Collection
Create tools that collect user input during execution:
**Pro tip:** Call `addResult(...)` exactly once to complete the tool call.
After it's invoked, the assistant will resume the conversation with your
provided data.
```tsx
const DatePickerToolUI = makeAssistantToolUI<
{ prompt: string },
{ date: string }
>({
toolName: "selectDate",
render: ({ args, result, addResult }) => {
if (result) {
return (
;
},
});
```
Use tool human input (`human()` / `resume()`) for workflows that need to
pause tool execution and wait for user input. Use `addResult()` for "human
tools" where the AI requests a tool call but the entire execution happens
through user interaction.
## Advanced Features
### Tool Status Handling
The `status` prop provides detailed execution state:
```tsx
render: ({ status, args }) => {
switch (status.type) {
case "running":
return ;
case "requires-action":
return ;
case "incomplete":
if (status.reason === "cancelled") {
return
;
},
});
```
Learn more about tool human input in the [Tools Guide](/docs/guides/tools#tool-human-input).
## Best Practices
### 1. Handle All Status States
Always handle loading, error, and success states:
```tsx
render: ({ status, result, args }) => {
if (status.type === "running") return ;
if (status.type === "incomplete") return ;
if (!result) return null;
return ;
};
```
### 2. Provide Visual Feedback
Use animations and transitions for better UX:
```tsx
{/* Tool UI content */}
```
### 3. Make UIs Accessible
Ensure keyboard navigation and screen reader support:
```tsx
```
### 4. Optimize Performance
Use `useInlineRender` to prevent unnecessary re-renders:
```tsx
useAssistantToolUI({
toolName: "heavyComputation",
render: useInlineRender(({ result }) => {
// Expensive rendering logic
return ;
}),
});
```
Generative UI components are only displayed in the chat interface. The actual
tool execution happens on the backend. This separation allows you to create
rich, interactive experiences while keeping sensitive logic secure on the
server.
## Related Guides
* [Tools Guide](/docs/guides/tools) - Learn how to create and use tools with AI models
* [Tool Fallback](/docs/ui/tool-fallback) - Default UI for tools without custom components
* [API Reference](/docs/api-reference/primitives/message-part) - Detailed type definitions and component APIs
* [Message Primitive](/docs/api-reference/primitives/message) - Complete Message component documentation