Generative user interfaces (generative UI) is the process of allowing a large language model (LLM) to go beyond text and "generate UI". This creates a more engaging and AI-native experience for users.
What is the weather in SF?
getWeather("San Francisco")
Thursday, March 7
47°
sunny
7am
48°
8am
50°
9am
52°
10am
54°
11am
56°
12pm
58°
1pm
60°
Thanks!
At the core of generative UI are tools , which are functions you provide to the model to perform specialized tasks like getting the weather in a location. The model can decide when and how to use these tools based on the context of the conversation.
Generative UI is the process of connecting the results of a tool call to a React component. Here's how it works:
You provide the model with a prompt or conversation history, along with a set of tools.
Based on the context, the model may decide to call a tool.
If a tool is called, it will execute and return data.
This data can then be passed to a React component for rendering.
By passing the tool results to React components, you can create a generative UI experience that's more engaging and adaptive to your needs.
Before enhancing your chat interface with dynamic UI elements, you need to create a tool and corresponding React component. A tool will allow the model to perform a specific action, such as fetching weather information.
Create a new file called ai/tools.ts with the following content:
ai/tools.ts
import { tool as createTool } from 'ai';import { z } from 'zod';export const weatherTool = createTool({ description: 'Display the weather for a location', inputSchema: z.object({ location: z.string().describe('The location to get the weather for'), }), execute: async function ({ location }) { await new Promise(resolve => setTimeout(resolve, 2000)); return { weather: 'Sunny', temperature: 75, location }; },});export const tools = { displayWeather: weatherTool,};
In this file, you've created a tool called weatherTool. This tool simulates fetching weather information for a given location. This tool will return simulated data after a 2-second delay. In a real-world application, you would replace this simulation with an actual API call to a weather service.
This component will display the weather information for a given location. It takes three props: temperature, weather, and location (exactly what the weatherTool returns).
Now that you have your tool and corresponding React component, let's integrate them into your chat interface. You'll render the Weather component when the model calls the weather tool.
To check if the model has called a tool, you can check the parts array of the UIMessage object for tool-specific parts. In AI SDK 5.0, tool parts use typed naming: tool-${toolName} instead of generic types.
Use manual input state management with useState instead of the built-in input and handleInputChange.
Use sendMessage instead of handleSubmit to send messages.
Check the parts array of each message for different content types.
Handle tool parts with type tool-displayWeather and their different states (input-available, output-available, output-error).
This approach allows you to dynamically render UI components based on the model's responses, creating a more interactive and context-aware chat experience.
You can enhance your chat application by adding more tools and components, creating a richer and more versatile user experience. Here's how you can expand your application:
To add more tools, simply define them in your ai/tools.ts file:
// Add a new stock toolexport const stockTool = createTool({ description: 'Get price for a stock', inputSchema: z.object({ symbol: z.string().describe('The stock symbol to get the price for'), }), execute: async function ({ symbol }) { // Simulated API call await new Promise(resolve => setTimeout(resolve, 2000)); return { symbol, price: 100 }; },});// Update the tools objectexport const tools = { displayWeather: weatherTool, getStockPrice: stockTool,};
Now, create a new file called components/stock.tsx: