AI reasoning trace with a shimmering trigger label, a self-timing readout, and a connected step timeline.
npx shadcn@latest add @iconiq/reasoning-stepsimport {
ReasoningStep,
ReasoningStepDetails,
ReasoningStepSource,
ReasoningStepSources,
ReasoningSteps,
ReasoningStepsContent,
ReasoningStepsTrigger,
} from "@/components/ui/reasoning-steps";
export function AssistantReply() {
return (
<ReasoningSteps>
<ReasoningStepsTrigger>Reasoning</ReasoningStepsTrigger>
<ReasoningStepsContent>
<ReasoningStep
description="Parsing the question and pulling out the actual constraints."
label="Reading the request"
status="done"
/>
<ReasoningStep
description="Checking the deploy config mentioned earlier in the thread."
label="Recalling relevant context"
status="done"
/>
<ReasoningStep
description="A quick rollback versus a targeted env-var fix."
label="Weighing two approaches"
status="done"
>
<ReasoningStepDetails summary="See the tradeoffs">
A rollback restores service in under a minute but loses the
config fix for the next deploy. The env-var patch takes longer
to verify but fixes it at the source.
</ReasoningStepDetails>
<ReasoningStepSources>
<ReasoningStepSource>vercel.com/docs</ReasoningStepSource>
<ReasoningStepSource>runbook.md</ReasoningStepSource>
</ReasoningStepSources>
</ReasoningStep>
<ReasoningStep
description="Writing the fix as a short, ordered checklist."
label="Drafting the answer"
status="active"
/>
</ReasoningStepsContent>
</ReasoningSteps>
);
}childrenComposition surface. Place a ReasoningStepsTrigger followed by a ReasoningStepsContent inside.
Type ReactNode
durationElapsed reasoning time in seconds, shown in the trigger once greater than 0. Omit to let the root time itself for as long as any mounted step has status "active".
Type number
defaultOpenInitial open state for uncontrolled usage.
Type boolean·Default false
openControlled open state. Pair with onOpenChange to drive the panel yourself.
Type boolean
onOpenChangeCalled when the trigger is clicked.
Type (open: boolean) => void
classNameExtra classes for the outer card.
Type string
labelStep heading shown in the timeline and the collapsed preview.
Type ReactNode
statusThe in-flight step should be "active"; steps not yet reached should be omitted or "pending", which renders nothing.
Type "pending" | "active" | "done"·Default "done"
descriptionMuted supporting copy under the label.
Type ReactNode
idStable identity used for the root's registry. Auto-generated when omitted.
Type string
childrenNest ReasoningStepDetails and/or ReasoningStepSources under a step's description.
Type ReactNode
classNameExtra classes for the row.
Type string
summaryAlways-visible trigger label, e.g. "See the tradeoffs".
Type ReactNode
defaultOpenInitial open state.
Type boolean·Default false
The disclosure row: just the label and the elapsed-seconds readout — no status icon and no chevron. children defaults to "Reasoning".
No icon runs next to the label. Instead, the label itself gets a looping shimmer sweep clipped to the text while any step is active, and settles to plain text once nothing is in flight.
While collapsed, a small preview line beneath the label slides and blurs between step labels as the active step changes, so the current step stays visible without opening the panel.
The panel: renders its ReasoningStep children in a ul and measures its own natural height with a ResizeObserver instead of animating to Motion's "auto", so the spring never overshoots under a scaled ancestor.
The connecting line under a step is only drawn between rendered steps — it's hidden automatically on whichever step ends up last, with no isLast bookkeeping required from the consumer.
Optional citation and attachment parts for a step. Sources wraps a row of Source pills in a flex-wrap container; each Source blurs and scales in and renders as a link when given href. Image blurs in an attached screenshot or diagram with an optional caption.
ReasoningStepSource renders an anchor when href is passed (opened in a new tab) and a plain span otherwise.
Install the exact registry entry shown on the right when you want the component file and its declared runtime dependencies together.
Dependencies: @base-ui/react, motion, lucide-react.
Contact
Additionally, if you find any bug or issue, feel free to raise an issue.
Reading the request
Parsing the question and pulling out the actual constraints.