///
Better UI distinguishes itself by tightly integrating tool definitions with their user interface (UI) components. This "AI-first UI" approach allows tools to not only be executable by AI models but al
105 views
~105 views from guests
Guest views are estimated from total page views. These include anonymous visitors and users who weren't logged in when they viewed the page.
Better UI distinguishes itself by tightly integrating tool definitions with their user interface (UI) components. This "AI-first UI" approach allows tools to not only be executable by AI models but also to render their own results visually and interactively within an application. This page showcases the example tools provided in the framework's lib/tools.tsx and details how they are consumed and rendered in the interactive chat demo (app/demo/page.tsx).
For an overview of how tools are generally structured, refer to the [Core Concepts] page, and for details on the React hooks used, see the [React API Reference]. For information on integrating tools with AI models, consult [AI Integration with Vercel AI SDK].
lib/tools.tsx)The lib/tools.tsx file defines several example tools, each demonstrating different aspects of Better UI, including input/output schemas, server-side logic, and rich interactive view components.
weatherTool)The weatherTool is a simple example demonstrating how to fetch and display dynamic data.
To retrieve the current weather conditions (temperature and general condition) for a specified city.
city (string).temp (number), city (string), and condition (string)..server() handler simulates fetching weather data using random numbers. In a real application, this would involve an external API call..view() component renders a stylized weather card. It displays:
Fetching weather...) when state.loading is true.state.error is present.condition when data is available.searchTool)The searchTool demonstrates returning a list of results based on a query.
To simulate a search operation and return a list of hypothetical results.
query (string).results, each with an id, title, and score..server() handler returns a predefined list of mock search results that incorporate the input query..view() component renders a list of search results. It shows a Searching... loading state and then displays each result with its title and a formatted score.counterTool)The counterTool is the most interactive example, demonstrating how a tool's view can trigger subsequent executions of itself using the onAction prop.
To manage a simple named counter, allowing actions like increment, decrement, reset, and get.
name (string) for the counter, an action (enum: increment, decrement, reset, get), and an optional amount (number).name, current value, performed action, and previousValue of the counter..server() handler uses an in-memory counterStore to manage counter values. It performs the specified action on the counter and returns the updated state..view() component renders the counter's UI:
onAction: This is where the interactivity comes in. The counterTool.view checks if state?.onAction is provided. If it is, it renders buttons (−1, +1, +5, Reset). When clicked, these buttons invoke onAction with the new tool input (e.g., { name: data.name, action: 'increment', amount: 1 }}). This onAction callback, provided by the useTool hook in the consuming component, triggers a new execution of the counterTool with the updated input, creating a seamless interactive loop.app/demo/page.tsx)The app/demo/page.tsx file provides a client-side React component that demonstrates how to integrate these Better UI tools into an AI chat interface using the Vercel AI SDK. This page acts as the bridge, consuming AI model outputs (including tool calls) and rendering the appropriate tool views.
useChat Hook: The [Vercel AI SDK]'s useChat hook is central to managing the chat state.
messages: An array of UIMessage objects representing the conversation history, including AI text responses and tool calls/outputs.sendMessage: Function to send a new user message to the AI backend.addToolOutput: A crucial function (new in AI SDK v5) that allows you to manually inject the result of a tool execution back into the chat message stream. This is used when a tool's onAction is triggered.Tool Identification (isToolOrDynamicToolUIPart, getToolOrDynamicToolName):
message.parts of each UIMessage.isToolOrDynamicToolUIPart(part) checks if a given message part represents an AI tool call (either a statically defined tool or a dynamically inferred one).getToolOrDynamicToolName(part) safely extracts the name of the tool from the identified tool part.Dynamic Tool View Rendering (toolDef.View):
const toolDef = tools[toolName as keyof typeof tools]; to retrieve the corresponding Better UI Tool instance from the tools object (which imports all tools from lib/tools.tsx).toolDef.View is then invoked, passing the tool's output (from the AI SDK message), the loading state, and the onAction callback.loading prop is determined by whether the tool has output-available in the AI SDK state or if loadingTools[toolCallId] indicates a pending direct execution.executeToolDirect and Interactive onAction:
executeToolDirect: This useCallback function is responsible for directly executing a Better UI tool on the client-side. It makes a POST request to /api/tools/execute (which is configured in src/tool.tsx as the default endpoint for client-side tool execution when no custom .client() handler is present).
addToolOutput({ tool: toolName, toolCallId, output: result }). This updates the specific tool call within the messages array, effectively showing the new result in the chat UI and allowing the AI model to proceed with the updated state.getOnAction: This function creates a memoized callback (onAction) specific to each rendered tool view. When the counterTool.View (or any other interactive tool view) calls onAction (e.g., when a user clicks a button), getOnAction routes that call to executeToolDirect, ensuring the interaction triggers a new, authenticated tool execution.The demo beautifully illustrates the core philosophy of Better UI:
lib/tools.tsx with clear schemas and descriptions..server() handlers, running securely on the backend (as handled by app/api/chat/route.ts and app/api/tools/execute)..view() components, providing rich, dynamic UI that's aware of loading, errors, and user interaction.toAITool() method (used in app/api/chat/route.ts) exposes the tool to AI models, and the useChat hook handles the AI's responses, including tool calls.app/demo/page.tsx stitches everything together, interpreting AI responses and rendering the appropriate tool views, enabling interactive elements within the chat itself.This architecture ensures that tools are robust, secure, and visually appealing, fostering a more engaging and functional AI-powered user experience.