showcase/shell-docs/src/content/reference/hooks/useRenderToolCall.mdx
useRenderToolCall returns a renderer function that maps tool calls to React elements. The returned function looks up the first matching render configuration by tool name (falling back to a wildcard "*" renderer if no exact match is found), parses the JSON arguments, determines the current ToolCallStatus, and returns the appropriate React element -- or null if no renderer is registered for the tool.
This hook is primarily used by chat UI components to display visual feedback for tool calls. In most applications you will not call it directly; instead, you register render components via useFrontendTool or useHumanInTheLoop, and the chat components use useRenderToolCall internally to resolve them.
import { useRenderToolCall } from "@copilotkit/react-core/v2";
function useRenderToolCall(): (
props: UseRenderToolCallProps,
) => React.ReactElement | null;
<PropertyReference name="toolCall" type="ToolCall" required>
The tool call object containing the tool `name` and its JSON-encoded `args`.
</PropertyReference>
<PropertyReference name="toolMessage" type="ToolMessage">
The tool result message, if available. When present, indicates the tool call has completed and contains the result string.
</PropertyReference>
The renderer determines the ToolCallStatus based on the inputs:
| Condition | Status | result |
|---|---|---|
No toolMessage and tool call is still loading | ToolCallStatus.InProgress | undefined |
| Tool call is executing (arguments resolved, no result yet) | ToolCallStatus.Executing | undefined |
A matching toolMessage exists | ToolCallStatus.Complete | The result string from the tool message |
The ToolCallStatus enum is exported from @copilotkit/react-core/v2:
| Value | Description |
|---|---|
ToolCallStatus.InProgress | The tool call's arguments are still being streamed. |
ToolCallStatus.Executing | Arguments are fully resolved; the tool is executing. |
ToolCallStatus.Complete | Execution is finished and a result is available. |
function CustomChatMessage({ message }) {
const renderToolCall = useRenderToolCall();
if (message.type === "tool_call") {
const element = renderToolCall({
toolCall: message.toolCall,
toolMessage: message.toolMessage,
});
if (element) {
return <div className="tool-call-container">{element}</div>;
}
return (
<div className="text-gray-400">Tool call: {message.toolCall.name}</div>
);
}
return <div>{message.content}</div>;
}
Tool call renderers are typically registered through useFrontendTool or directly via the renderToolCalls prop on the provider. The useRenderToolCall hook resolves these registrations at render time.
function App() {
// Register a tool with a render component
useFrontendTool(
{
name: "searchDatabase",
description: "Search the product database",
parameters: z.object({
query: z.string().describe("Search query"),
}),
handler: async ({ query }) => {
const results = await fetch(`/api/search?q=${query}`);
return JSON.stringify(await results.json());
},
render: ({ args, status, result }) => {
if (status === ToolCallStatus.InProgress) {
return <div>Searching for "{args.query}"...</div>;
}
if (status === ToolCallStatus.Complete && result) {
const data = JSON.parse(result);
return (
<div>
<p>
Found {data.length} results for "{args.query}"
</p>
<ul>
{data.map((item: any) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
}
return null;
},
},
[],
);
return <ChatInterface />;
}
If no exact name match is found, the hook falls back to a wildcard "*" renderer. This is useful for providing a generic UI for all unhandled tool calls.
function App() {
return (
<CopilotKitProvider
runtimeUrl="/api/copilotkit"
renderToolCalls={[
{
name: "*",
render: ({ name, args, status, result }) => {
if (status === ToolCallStatus.InProgress) {
return (
<div className="text-gray-500 text-sm">Running {name}...</div>
);
}
if (status === ToolCallStatus.Complete) {
return (
<div className="text-green-600 text-sm">{name} completed.</div>
);
}
return null;
},
},
]}
>
<YourApp />
</CopilotKitProvider>
);
}
"*") renderer.args string is parsed from JSON before being passed to the render component. If parsing fails, args default to an empty object.toolMessage prop. The render component always receives a consistent shape with name, args, status, and result.null.CopilotChat, CopilotPopup, and CopilotSidebar components use this hook to render tool calls. You only need to call it directly when building custom chat UIs.useFrontendTool -- register tools with render componentsuseHumanInTheLoop -- register interactive tools with render componentsCopilotKitProvider -- configure static render tool calls at the provider level