React UI
Tool calls
Render pending, settled, and custom tool call states in message parts.
Default rendering
Message.Part delegates tool parts to Message.Tool, so most chat surfaces get tool rendering
without extra code.
<Message.Parts>
<Message.Part />
</Message.Parts>
Message.Tool shows the tool name, status, input, output, and error content when each field is
available.
Tool parts come from server-side agent/tool streams. The UI does not execute tools in the browser. For the route and tool definition, see Tools end to end.
Pending and settled states
Use renderWhen to decide when a tool call should appear.
<Message.Tool renderWhen="always" className="tool-call" />
<Message.Tool renderWhen="pending" className="tool-call pending" />
<Message.Tool renderWhen="settled" className="tool-call settled" />
renderWhen="always" is the default. Pending means the input is streaming or available. Settled
means output is available or the tool call ended in an error.
Custom tool cards
Compose the smaller tool primitives when the design system owns the card layout.
import { Message } from "@anvia/react-ui";
export function ToolCard() {
return (
<Message.Tool className="tool-card" renderWhen="always">
<header className="tool-card-header">
<Message.ToolName />
<Message.ToolStatus />
</header>
<section className="tool-card-section">
<Message.ToolInput />
</section>
<section className="tool-card-section">
<Message.ToolOutput />
<Message.ToolError />
</section>
</Message.Tool>
);
}.tool-card {
width: min(560px, 100%);
display: grid;
gap: 10px;
border: 1px solid #3f3f46;
border-radius: 10px;
background: #18181b;
padding: 12px;
}
.tool-card[data-state="input-streaming"],
.tool-card[data-state="input-available"] {
border-color: #facc15;
}
.tool-card[data-state="output-available"] {
border-color: #22c55e;
}
.tool-card[data-state="error"] {
border-color: #fb7185;
}
.tool-card-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.tool-card-section pre {
max-height: 280px;
overflow: auto;
}import { Message } from "@anvia/react-ui";
export function ToolCard() {
return (
<Message.Tool
className="
grid w-full max-w-xl gap-2.5 rounded-lg border border-zinc-700 bg-zinc-900 p-3
data-[state=error]:border-rose-400
data-[state=input-available]:border-yellow-400
data-[state=input-streaming]:border-yellow-400
data-[state=output-available]:border-emerald-500
"
renderWhen="always"
>
<header className="flex items-center justify-between gap-3">
<Message.ToolName />
<Message.ToolStatus className="text-sm text-zinc-400" />
</header>
<section className="[&_pre]:max-h-72 [&_pre]:overflow-auto">
<Message.ToolInput />
</section>
<section className="[&_pre]:max-h-72 [&_pre]:overflow-auto">
<Message.ToolOutput />
<Message.ToolError className="text-rose-200" />
</section>
</Message.Tool>
);
}The child function receives the full tool part when a product component wants to format everything itself.
<Message.Tool>
{(part) => (
<ToolCallCard
name={part.toolName}
state={part.state}
input={part.input}
output={part.output}
error={part.error}
/>
)}
</Message.Tool>
Use this when the tool output is domain-specific:
<Message.Tool>
{(part) =>
part.toolName === "lookup_order" && part.output !== undefined ? (
<OrderLookupResult value={part.output} />
) : (
<Message.ToolOutput />
)
}
</Message.Tool>
Filtering noisy tools
Use Message.Parts filtering when a surface should hide tool calls until a result is available.
<Message.Parts
filter={(part) => part.type !== "tool" || part.state === "output-available"}
/>
Use the inverse for an activity panel that only shows pending tool work.
<Message.Parts filter={(part) => part.type === "tool" && part.state !== "output-available"}>
<Message.Part />
</Message.Parts>
For a combined tool and review flow, see Tool and human input.
