traces
Expandable Trace
A collapsible "Thought for Xs" summary that expands into a step-by-step trace.
Reading the component's UX doc
Comparing it against similar patterns
Checking accessibility noteskeyboard + reduced motion
Scaffolding the component
"use client";
import * as React from "react";
import { AnimatePresence, motion } from "motion/react";
import { Check, ChevronDown, Sparkles } from "lucide-react";
import { cn } from "@/lib/utils";
export interface TraceStep {
label: string;
meta?: string;
}
export interface ExpandableTraceProps {
durationSeconds?: number;
steps: TraceStep[];
defaultOpen?: boolean;
className?: string;
}
export function ExpandableTrace({
durationSeconds = 4,
steps,
defaultOpen = false,
className,
}: ExpandableTraceProps) {
const [open, setOpen] = React.useState(defaultOpen);
return (
<div className={className}>
<button
type="button"
onClick={() => setOpen((v) => !v)}
aria-expanded={open}
className="flex w-full items-center gap-2 px-4 py-3 text-left text-xs font-medium"
>
<Sparkles className="size-4 text-muted-foreground" />
<span>Thought for {durationSeconds} seconds</span>
<ChevronDown
className={cn(
"ml-auto size-4 text-muted-foreground transition-transform duration-200",
open && "rotate-180"
)}
/>
</button>
<AnimatePresence initial={false}>
{open && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ duration: 0.2, ease: "easeInOut" }}
className="overflow-hidden"
>
<ul className="px-4 pb-4">
{steps.map((step, i) => (
<li key={i} className="flex gap-3">
<div className="flex flex-col items-center">
<span className="flex size-4 shrink-0 items-center justify-center rounded-full bg-muted">
<Check className="size-2.5 text-muted-foreground" />
</span>
{i < steps.length - 1 && <span className="my-1 w-px flex-1 bg-border" />}
</div>
<p className="pb-3 text-xs text-foreground/90">
{step.label}
{step.meta && (
<span className="ml-1.5 text-muted-foreground">{step.meta}</span>
)}
</p>
</li>
))}
</ul>
</motion.div>
)}
</AnimatePresence>
</div>
);
}
A UX spec for this pattern — written for agents implementing or reusing it, not the code.
# Expandable Trace
## Summary
A collapsed-by-default summary line ("Thought for N seconds") that expands into a checklist of the discrete steps an agent took to produce its answer. It gives curious users a way to audit the reasoning without forcing everyone to read it by default.
## When to use
- Right after an agent finishes a multi-step task (tool calls, reasoning, search, edits) and you want to offer transparency without cluttering the default view.
- When the steps have a natural, ordered sequence with a clear notion of "done" per step.
## When not to use
- For a single atomic action with no real sub-steps — there's nothing meaningful to expand into; just state the result.
- As a substitute for real error handling. If a step failed, show that explicitly as a distinct state on that step (not by silently omitting it).
- Auto-expanded by default in a dense feed. Default collapsed keeps the primary answer scannable; only auto-expand when the user asked to see reasoning, or in a dedicated debugging surface.
## Anatomy
- Header button: icon + "Thought for N seconds" + chevron. The entire header is the toggle target, not just the chevron.
- Collapsible body: a vertical list of steps, each with a completion mark, a short label, and optional trailing metadata (e.g. "6 sources").
- A connecting line between steps so they read as one continuous sequence, not disconnected items.
## Behavior
- Collapsed by default.
- Expand/collapse animates height smoothly rather than snapping instantly.
- The step list is a static historical record once rendered — it does not keep updating live. Use the Thinking Loader for the in-progress version of this information.
- The duration shown in the header is fixed once the run is complete; it does not keep counting like the Thinking Loader's timer does.
## Content guidelines
- Step labels are short, neutral action phrases ("Reading the uploaded document"), not first-person narration ("I read the uploaded document").
- Trailing metadata should be a single scannable fact, not another full sentence.
## Accessibility
- The header button needs `aria-expanded` reflecting current state.
- The whole component must be operable by keyboard (Enter/Space on the header toggles it).
- Never hide information the user needs to trust or act on the primary answer exclusively inside the collapsed trace — the answer must stand on its own without expanding this.
## Related patterns
- Thinking Loader is the "in progress" counterpart — this component is what it becomes once work finishes.