Tool
The Tool
component displays a collapsible interface for showing/hiding tool details. It is designed to take the ToolUIPart
type from the AI SDK and display it in a collapsible interface.
Installation
npx ai-elements@latest add tool
Usage
import { Tool, ToolContent, ToolHeader, ToolOutput, ToolInput,} from '@/components/ai-elements/tool';
<Tool> <ToolHeader type="tool-call" state={'output-available' as const} /> <ToolContent> <ToolInput input="Input to tool call" /> <ToolOutput errorText="Error" output="Output from tool call" /> </ToolContent></Tool>
Usage in AI SDK
Build a simple stateful weather app that renders the last message in a tool using useChat
.
Add the following component to your frontend:
'use client';
import { useChat } from '@ai-sdk/react';import { DefaultChatTransport, type ToolUIPart } from 'ai';import { Button } from '@/components/ui/button';import { Response } from '@/components/ai-elements/response';import { Tool, ToolContent, ToolHeader, ToolInput, ToolOutput,} from '@/components/ai-elements/tool';
type WeatherToolInput = { location: string; units: 'celsius' | 'fahrenheit';};
type WeatherToolOutput = { location: string; temperature: string; conditions: string; humidity: string; windSpeed: string; lastUpdated: string;};
type WeatherToolUIPart = ToolUIPart<{ fetch_weather_data: { input: WeatherToolInput; output: WeatherToolOutput; };}>;
const Example = () => { const { messages, sendMessage, status } = useChat({ transport: new DefaultChatTransport({ api: '/api/weather', }), });
const handleWeatherClick = () => { sendMessage({ text: 'Get weather data for San Francisco in fahrenheit' }); };
const latestMessage = messages[messages.length - 1]; const weatherTool = latestMessage?.parts?.find( (part) => part.type === 'tool-fetch_weather_data', ) as WeatherToolUIPart | undefined;
return ( <div className="max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]"> <div className="flex flex-col h-full"> <div className="space-y-4"> <Button onClick={handleWeatherClick} disabled={status !== 'ready'}> Get Weather for San Francisco </Button>
{weatherTool && ( <Tool defaultOpen={true}> <ToolHeader type="fetch_weather_data" state={weatherTool.state} /> <ToolContent> <ToolInput input={weatherTool.input} /> <ToolOutput output={ <Response> {formatWeatherResult(weatherTool.output)} </Response> } errorText={weatherTool.errorText} /> </ToolContent> </Tool> )} </div> </div> </div> );};
function formatWeatherResult(result: WeatherToolOutput): string { return `**Weather for ${result.location}**
**Temperature:** ${result.temperature} **Conditions:** ${result.conditions} **Humidity:** ${result.humidity} **Wind Speed:** ${result.windSpeed}
*Last updated: ${result.lastUpdated}*`;}
export default Example;
Add the following route to your backend:
import { streamText, UIMessage, convertToModelMessages } from 'ai';import { z } from 'zod';
// Allow streaming responses up to 30 secondsexport const maxDuration = 30;
export async function POST(req: Request) { const { messages }: { messages: UIMessage[] } = await req.json();
const result = streamText({ model: 'openai/gpt-4o', messages: convertToModelMessages(messages), tools: { fetch_weather_data: { description: 'Fetch weather information for a specific location', parameters: z.object({ location: z .string() .describe('The city or location to get weather for'), units: z .enum(['celsius', 'fahrenheit']) .default('celsius') .describe('Temperature units'), }), inputSchema: z.object({ location: z.string(), units: z.enum(['celsius', 'fahrenheit']).default('celsius'), }), execute: async ({ location, units }) => { await new Promise((resolve) => setTimeout(resolve, 1500));
const temp = units === 'celsius' ? Math.floor(Math.random() * 35) + 5 : Math.floor(Math.random() * 63) + 41;
return { location, temperature: `${temp}°${units === 'celsius' ? 'C' : 'F'}`, conditions: 'Sunny', humidity: `12%`, windSpeed: `35 ${units === 'celsius' ? 'km/h' : 'mph'}`, lastUpdated: new Date().toLocaleString(), }; }, }, }, });
return result.toUIMessageStreamResponse();}
Features
- Collapsible interface for showing/hiding tool details
- Visual status indicators with icons and badges
- Support for multiple tool execution states (pending, running, completed, error)
- Formatted parameter display with JSON syntax highlighting
- Result and error handling with appropriate styling
- Composable structure for flexible layouts
- Accessible keyboard navigation and screen reader support
- Consistent styling that matches your design system
- Auto-opens completed tools by default for better UX
Examples
Input Streaming (Pending)
Shows a tool in its initial state while parameters are being processed.
Input Available (Running)
Shows a tool that's actively executing with its parameters.
Output Available (Completed)
Shows a completed tool with successful results. Opens by default to show the results. In this instance, the output is a JSON object, so we can use the CodeBlock
component to display it.
Output Error
Shows a tool that encountered an error during execution. Opens by default to display the error.