Components as Tools
Let your agent render rich React components directly in the chat by calling them as tools.
"""LangGraph agent backing the Tool-Based Generative UI demo.The frontend registers `render_bar_chart` and `render_pie_chart` tools via`useComponent`. CopilotKit's LangGraph middleware injects those tools intothe model request at runtime so the agent can call them."""from langchain.agents import create_agentfrom langchain_openai import ChatOpenAIfrom copilotkit import CopilotKitMiddlewareSYSTEM_PROMPT = """You are a data visualization assistant.When the user asks for a chart, call `render_bar_chart` or `render_pie_chart`with a concise title, short description, and a `data` array of`{label, value}` items. Pick bar for comparisons over a small set ofcategories; pick pie for composition / share-of-whole.If the user names a chart subject but does NOT supply concrete numbers(e.g. "show me a pie chart of website traffic by source"), do NOT askthem for data. Invent plausible illustrative sample values yourself,call the appropriate `render_*` tool immediately, and briefly note inthe follow-up that the values are illustrative samples. Always renderthe chart on the first turn -- never reply with a clarifying questionasking for the data.Keep chat responses brief -- let the chart do the talking."""graph = create_agent( model=ChatOpenAI(model="gpt-5.4"), tools=[], middleware=[CopilotKitMiddleware()], system_prompt=SYSTEM_PROMPT,)What is this?#
Tool-based Generative UI is the simplest form of Generative UI: you register
a React component with useComponent, and CopilotKit exposes it to the
agent as a tool. When the agent calls the tool, CopilotKit renders your
component inline in the chat, passing the tool's arguments straight through
as typed props.
Unlike tool rendering, which wraps a real backend tool in a custom UI, tool-based GenUI is the component. There is no handler, no user interaction, no server-side execution. The agent decides when to show it, populates the data, and CopilotKit paints it.
When should I use this?#
Use useComponent when you want to:
- Display rich UI (cards, charts, tables, dashboards) inline in the chat
- Show structured data the agent has derived from its reasoning
- Render previews, status indicators, or visual summaries
- Let the agent present information beyond plain text
For components that need user interaction, see Human-in-the-loop. For operational transparency around a real backend tool, see Tool rendering.
How it works in code#
Install the LangGraph Python SDK
uv add copilotkitpoetry add copilotkitpip install copilotkit --extra-index-url https://copilotkit.gateway.scarf.sh/simple/conda install copilotkit -c copilotkit-channelWire CopilotKit middleware into your graph
Frontend tools registered with useFrontendTool are forwarded to your
agent at runtime. CopilotKitMiddleware is the bridge — drop it into
create_agent's middleware list and the LLM sees the forwarded tool
definitions on every turn.
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from copilotkit import CopilotKitMiddleware
graph = create_agent(
model=ChatOpenAI(model="gpt-5.4"),
tools=[],
middleware=[CopilotKitMiddleware()],
system_prompt="You are a helpful, concise assistant.",
)Import the React hook and Zod in the component that registers the tool. This also applies to the built-in agent, which needs no backend tool-registration step.
import { useComponent } from "@copilotkit/react-core/v2";
import { z } from "zod";useComponent takes a name, a Zod schema for its props, and the component
to render. The runtime registers it as a frontend tool so the agent can
discover it, and the schema becomes that tool's parameter definition — it is
what tells the model which arguments to send.
parameters is optional, but leaving it out advertises the tool with an
empty parameter schema ({ "type": "object", "properties": {} }). The model
then has nothing to fill in, so it calls the tool with no arguments and your
component renders with no props. Pass a schema for any component that needs
data.
useComponent({ name: "render_bar_chart", description: "Display a bar chart with labeled numeric values.", parameters: barChartPropsSchema, render: BarChart, });The component itself is ordinary React: it reads only its props and can stream in as the agent fills the payload. The example above uses Recharts for the bar chart; it doesn't know anything about CopilotKit.
The name you pass to useComponent is what the agent sees as the tool
name. Make it a verb like render_bar_chart or show_weather so the LLM
reliably picks it when the user asks for that visualization.
Rendering in a headless chat#
CopilotKit's built-in chat components paint registered components for you. A headless or custom chat renders the message list itself, so nothing paints a tool call unless you render it — the component is registered and the agent calls it, but the chat stays empty.
Render the tool calls on each assistant message with
CopilotChatToolCallsView:
import { CopilotChatToolCallsView } from "@copilotkit/react-core/v2";
<CopilotChatToolCallsView message={assistantMessage} messages={allMessages} />;It looks up the sibling tool-role message for each tool call and hands both
to the registered renderer. For finer placement, call useRenderToolCall() and
paint each tool call yourself — see
Headless UI.