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. When neither a per-tool nor a wildcard renderer is registered, it falls back to the framework's built-in DefaultToolCallRenderer, so the returned element is effectively always non-null (the signature keeps ReactElement | null for type compatibility).
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") {
// Always returns an element: your registered renderer, the wildcard
// ("*") renderer, or the framework's built-in DefaultToolCallRenderer.
const element = renderToolCall({
toolCall: message.toolCall,
toolMessage: message.toolMessage,
});
return <div className="tool-call-container">{element}</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 (
<CopilotKit
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 />
</CopilotKit>
);
}
"*") renderer, and finally to the framework's built-in DefaultToolCallRenderer.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.DefaultToolCallRenderer rather than returning null, so unhandled tool calls still paint out-of-the-box.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 componentsCopilotKit -- configure static render tool calls at the provider level