Skip to Content
ChatTool Result Rendering

Tool Result Rendering

Frontend tools can include a render method that displays custom React components for their results. This creates rich, interactive content directly in the chat interface, going beyond simple text responses.

AI with Custom Tool Rendering

Tools that render React components

Ready to chat?

Send a message to get started

Custom Rendering with React Components

The render method receives the tool’s return value and renders a React component:

useAIFrontendTool({ name: "create_chart", description: "Create a data visualization chart", parameters: z.object({ title: z.string().describe("Title for the chart"), type: z.enum(["bar", "pie", "line"]).describe("Type of chart"), data: z.array(z.object({ label: z.string().describe("Label for data point"), value: z.number().describe("Value for data point"), })).describe("Data points for the chart"), }), execute: async (params) => { return { chartId: `chart-${Date.now()}`, title: params.title, type: params.type, data: params.data, createdAt: new Date().toISOString(), }; }, render: (result) => ( <ChartComponent title={result.title} type={result.type} data={result.data} /> ), });

Complete Implementation Example

Here’s a full example showing tools with custom rendering:

"use client"; import React, { useState } from "react"; import { ChatContainer, useAIChat, useAIFrontendTool } from "ai-chat-bootstrap"; import { z } from "zod"; // Custom Chart Component function ChartComponent({ data, type, title }: { data: Array<{ label: string; value: number }>; type: "bar" | "pie" | "line"; title: string; }) { const maxValue = Math.max(...data.map(d => d.value)); return ( <div className="p-4 bg-card rounded-lg border"> <h3 className="text-lg font-semibold mb-4">{title}</h3> <div className="space-y-3"> {data.map((item, index) => ( <div key={index} className="flex items-center gap-3"> <div className="w-16 text-sm text-muted-foreground"> {item.label} </div> <div className="flex-1 bg-muted rounded-full h-6 relative"> <div className="h-full bg-primary rounded-full transition-all duration-500" style={{ width: `${(item.value / maxValue) * 100}%` }} /> <div className="absolute inset-0 flex items-center justify-center text-xs font-medium"> {item.value} </div> </div> </div> ))} </div> </div> ); } export function ToolRenderingDemo() { // Chart creation tool with custom rendering useAIFrontendTool({ name: "create_chart", description: "Create a data visualization chart", parameters: z.object({ title: z.string().describe("Title for the chart"), type: z.enum(["bar", "pie", "line"]).describe("Type of chart"), data: z.array(z.object({ label: z.string().describe("Label for data point"), value: z.number().describe("Value for data point"), })).describe("Data points for the chart"), }), execute: async (params) => { return { chartId: `chart-${Date.now()}`, title: params.title, type: params.type, data: params.data, createdAt: new Date().toISOString(), }; }, render: (result) => ( <ChartComponent title={result.title} type={result.type} data={result.data} /> ), }); const chat = useAIChat({ api: "/api/chat", systemPrompt: "You can create charts using the create_chart tool when users request data visualizations." }); return ( <ChatContainer chat={chat} header={{ title: "AI with Custom Rendering" }} ui={{ placeholder: "Ask me to create a chart!" }} /> ); }

Backend Integration

The backend API route works the same way - tools with render methods are handled automatically:

import { createAzure } from "@ai-sdk/azure"; import { convertToModelMessages, streamText } from "ai"; const azure = createAzure({ resourceName: process.env.AZURE_RESOURCE_NAME!, apiKey: process.env.AZURE_API_KEY!, apiVersion: process.env.AZURE_API_VERSION ?? "preview", }); const model = azure(process.env.AZURE_DEPLOYMENT_ID!); export async function POST(req: Request) { const { messages, enrichedSystemPrompt, tools } = await req.json(); const result = await streamText({ model, messages: [ { role: "system", content: enrichedSystemPrompt }, ...convertToModelMessages(messages), ], tools, }); return result.toUIMessageStreamResponse(); }

Advanced Features

Conditional Rendering

Render different components based on the result:

render: (result) => { if (result.type === 'success') { return <SuccessCard message={result.message} />; } if (result.type === 'error') { return <ErrorCard error={result.error} retry={result.retry} />; } return <DefaultCard data={result} />; }

State Management

Tools can interact with external state:

render: (result) => ( <div onClick={() => setSelectedItem(result.id)}> <ItemCard item={result} isSelected={selectedItem === result.id} /> </div> )

Event Handling

Components can trigger additional actions:

render: (result) => ( <div className="p-4 bg-card rounded-lg border"> <h3>{result.title}</h3> <button onClick={() => { // Trigger another tool or action executeAnotherTool(result.id); }} className="mt-2 bg-primary text-primary-foreground px-4 py-2 rounded" > Process Result </button> </div> )

How It Works

  1. Tool Execution: Tool runs and returns structured data
  2. Render Invocation: The render method receives the result
  3. Component Creation: A React component is created and rendered
  4. Chat Integration: The component appears in the chat message
  5. Interactivity: Users can interact with the rendered component
  6. State Updates: Components can trigger further tool executions

API Reference

Next

Continue to Sharing Context →

Last updated on