11 / 37 · Tool use
Tool call
One tool invocation with its request and result tucked behind a disclosure.
Installation
npx shadcn@latest add "@assistant-ui/elements-tool-call"
Usage
import { ToolCall } from "@/components/elements/tool-call";
<ToolCall
label="Searched"
activeLabel="Searching"
query="composer props"
request='{"q":"composer"}'
result='{"hits":2}'
running={false}
open
onOpenChange={setOpen}
/>Props
activeLabel*stringVerb shown with a shimmer while the tool is running, for example Searching the docs.
label*stringTool name shown on the collapsed trigger row.
query*stringShort chip next to the label summarizing the call target.
request*stringRequest payload shown inside the expanded disclosure.
result*stringResult payload shown once the call is no longer running.
running*booleanWhen true a spinner replaces the success check on the trigger.
open*booleanWhether the request and result panel is expanded.
onOpenChange*(open: boolean) => voidCalled when the user expands or collapses the call.
classNamestringExtra classes merged onto the root.
Source
tool-call.tsx"use client";
import { CheckIcon, ChevronRightIcon } from "lucide-react";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { cn } from "@/lib/utils";
import { collapsePanel, field, mono, SwapLabel } from "./surfaces";
export interface ToolCallProps {
label: string;
activeLabel: string;
query: string;
request: string;
result: string;
running: boolean;
open: boolean;
onOpenChange: (open: boolean) => void;
className?: string;
}
export function ToolCall({
label,
activeLabel,
query,
request,
result,
running,
open,
onOpenChange,
className,
}: ToolCallProps) {
return (
<Collapsible
open={open}
onOpenChange={onOpenChange}
className={cn("w-full max-w-sm", className)}
>
<CollapsibleTrigger className="group/trigger text-foreground/55 hover:text-foreground/90 flex items-center gap-2 rounded-md py-1 text-[13.5px] transition-colors outline-none">
<ChevronRightIcon className="size-3.5 shrink-0 opacity-60 transition-transform duration-200 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-open/trigger:rotate-90 group-data-panel-open/trigger:rotate-90 motion-reduce:transition-none" />
<SwapLabel active={running ? 0 : 1} className="text-start">
<span className="relative inline-block leading-none">
<span>{activeLabel}</span>
<span
aria-hidden
className="shimmer pointer-events-none absolute inset-0 motion-reduce:animate-none"
>
{activeLabel}
</span>
</span>
<>{label}</>
</SwapLabel>
<span
className={cn(
mono,
"bg-foreground/[0.06] text-foreground/70 rounded-md px-1.5 py-0.5",
)}
>
{query}
</span>
<span className="ms-auto flex w-4 items-center justify-end">
{!running && (
<CheckIcon className="fade-in zoom-in-90 animate-in size-3.5 text-emerald-500 duration-200" />
)}
</span>
</CollapsibleTrigger>
<CollapsibleContent className={cn(collapsePanel, "outline-none")}>
<div className={cn(field, "mt-2 overflow-hidden rounded-2xl text-xs")}>
<div className="px-3.5 pt-2.5 pb-2">
<p className={cn(mono, "text-foreground/35 mb-1")}>Request</p>
<p className="text-foreground/55 font-mono">{request}</p>
</div>
<div className="bg-foreground/[0.06] mx-3.5 h-px" />
<div className="px-3.5 pt-2 pb-2.5">
<p className={cn(mono, "text-foreground/35 mb-1")}>Result</p>
<p className="text-foreground/90">{result}</p>
</div>
</div>
</CollapsibleContent>
</Collapsible>
);
}