WebberUI

AI Tool Call Card

A status card for an AI tool call that transitions smoothly between running / success / error, and expands to show the arguments and result.

This is a WebberUI Pro component

Free during the launch campaign: sign up or sign in, then hit “Copy install command” in the preview above and it installs straight away — no payment, no credit card. The command below returns 401 while you are signed out.

How to install Pro components →See the plans →

Loading preview…
npx shadcn@latest add "https://webberui.com/r/ai-tool-call-card.json?t=<install token>"

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

1240
<AiToolCallCard />

Installation

npx shadcn@latest add "https://webberui.com/r/ai-tool-call-card.json?t=<install token>"

Or, once registries are configured in components.json, install it as @webberui/ai-tool-call-card.

Usage

import { AiToolCallCard } from "@/components/ui/ai-tool-call-card";

<AiToolCallCard
  name="search_web"
  status="running"
  description="Search the document library to answer the user's question"
  args={{ query: "webber ui motion", top_k: 5 }}
/>;

status is driven by your agent's execution flow, and the card transitions automatically as the value changes: the circular indicator's icon pops in, the status text flips vertically, and the border color tints smoothly.

Status transitions

Just bind status to the tool call's real lifecycle and the component handles every transition animation:

const [status, setStatus] = useState<ToolCallStatus>("pending");

async function call() {
  setStatus("running");
  try {
    const result = await runTool();
    setStatus("success");
  } catch {
    setStatus("error");
  }
}

On entering error, uncontrolled mode expands automatically to reveal the error message.

Expanding the arguments and result

The card can expand when args is provided; result (on success) and error (on failure) are shown in their respective sections. The expanded state supports controlled and uncontrolled modes:

<AiToolCallCard
  name="run_sql"
  status="success"
  args={{ table: "orders", limit: 100 }}
  result={<pre>{JSON.stringify(rows, null, 2)}</pre>}
  expanded={open}
  onExpandedChange={setOpen}
/>

Props

PropTypeDefaultDescription
namestringTool (function) name, shown in a monospace face
status"pending" | "running" | "success" | "error"The current status; changing it triggers the transition animation
labelstringPer statusOverrides the status text on the right
descriptionstringA one-line subtitle beneath the name
argsRecord<string, unknown>Call arguments, shown as a monospace key/value list
resultReact.ReactNodeShown in the "Result" section on success
errorReact.ReactNodeShown in the "Error" section on failure
elapsedMsnumberElapsed time (ms), formatted next to the status text
expandedbooleanControlled expanded state
defaultExpandedbooleanfalseInitial expanded state in uncontrolled mode
onExpandedChange(expanded: boolean) => voidCallback when the expanded state changes

How it works

  • The circular indicator swaps icons by status through AnimatePresence: the old icon shrinks away and the new one springs in
  • The status text uses a flip window (an overflow mask), with the old value flipping out upward and the new one flipping in from below
  • The border and the indicator's base color tint smoothly via transition-colors (var(--wb-duration-normal,300ms))
  • While running, an indeterminate bright band sweeps along the bottom line, signalling that the tool is still working
  • The expandable area opens and closes with a height: auto transition, and the content fades in

Accessibility

  • The card contains plain text with role="status" and aria-live="polite", announcing "name: status" whenever the status changes
  • Every animated node is aria-hidden, so nothing is announced twice
  • When expandable, the header row is a <button> carrying aria-expanded / aria-controls, supporting keyboard operation and a focus ring
  • When the user has "reduce motion" enabled: the indicator stops spinning, the flip becomes a fade in / fade out, the progress band is hidden, and expanding switches instantly — while the DOM structure stays consistent (the switch happens only after mount, avoiding a hydration mismatch)

On this page