Reasoning block
Free · MIT"Thinking 8.7s" disclosure with a live tenths timer, a shimmering label while the model reasons, the current step previewed under the header and the full step list plus notes folded inside.
Installation
$ npx shadcn@latest add https://goodui.dev/r/reasoning-block.jsonThe file lands in components/goodui/reasoning-block.tsx and is yours to edit. With the @goodui registry added, npx shadcn@latest add @goodui/reasoning-block works too.
Usage
import { ReasoningBlock } from "@/components/goodui/reasoning-block"
<ReasoningBlock thinking={!done} steps={summaries.map((s) => ({ title: s }))} duration={done ? seconds : undefined}>
{reasoningText}
</ReasoningBlock>When to use
- Models with visible reasoning or thinking summaries: show the time spent and the steps while the user waits, then fold them away under "Thought for 8.7s".
- Agents that report progress as short step titles before the final answer.
- External tool calls with arguments and outputs: use tool-call.
- A plan with statuses and nesting: use agent-plan.
- Plain loading with no steps to show: use spinners or typing-indicator.
| If you need | Use |
|---|---|
| Live thinking | thinking plus steps that grow as summaries arrive |
| Finished with the API's duration | thinking={false} duration={seconds} |
| Full reasoning text | children (rendered under the steps) |
| No current-step line under the header | preview={false} |
Examples
Expanded by default after it finishes
<ReasoningBlock duration={8.7} defaultOpen steps={[{ title: "Checked Saturday's capacity" }, { title: "Balayage needs 150 minutes" }]} />Translated labels
<ReasoningBlock thinking labels={{ thinking: (s) => `Denke nach ${s}`, thought: (s) => `${s} nachgedacht`, toggle: "Begründung anzeigen" }} />API reference
| Prop | Type | Default |
|---|---|---|
| thinking The model is still reasoning: the label shimmers and the timer runs. | boolean | false |
| duration Final duration in seconds, when you have it from the API. Otherwise the elapsed time is measured here. | number | — |
| steps Step summaries, shown in order; the last one is the current step while thinking. | ReasoningStep[] | [] |
| children Free-form reasoning text shown under the steps. | React.ReactNode | — |
| open | boolean | — |
| defaultOpen | boolean | false |
| onOpenChange | (open: boolean) => void | — |
| preview Show the current step under the header while collapsed and thinking. | boolean | true |
| labels Text for built-in controls and screen readers; pass translations here. | Partial<ReasoningBlockLabels> | — |
Also accepts every <div> attribute.
Accessibility
| Enter / Space | Toggles the step list on the header button. |
- The header is a <button> with aria-expanded, aria-controls and an aria-label that includes the current text ("Show reasoning: Thinking 3.2s").
- The shimmer is a decorative gradient over the label; under reduced motion it is plain text.
- While collapsed and thinking, the current step is announced through a visually hidden polite live region; the animated preview line under the header is decorative, and the step list is ordinary text.
- Built-in strings go through the labels prop for translation.
Motion
- While thinking, the label shimmers (a 1.6 s gradient sweep clipped to the text) and the timer updates every 100 ms with one decimal.
- The current step below the header cross-fades (4px rise, 0.2 s) each time a new summary arrives; the list items spring in (stiffness 480, damping 32) and the current one has a pulsing dot.
- The body opens to its real height on a spring (stiffness 420, damping 36).
- Reduced motion: no shimmer, no pulse, fades only, instant open and close.
Notes for AI
Coding agents read these rules through MCP and llms.txt, or paste this page with Copy page.
- Set thinking while reasoning is in progress; the component measures the elapsed time itself. Pass duration (seconds) when the API reports it, it wins once thinking is false.
- steps is the list so far; append summaries as they arrive. The last one is treated as the current step while thinking.
- Put the raw reasoning text in children; it renders muted under the steps.
- With no steps and no children, the header is static text and not a button.
FAQ
Does the timer keep running if the component re-renders?
Yes, it is anchored to the moment thinking became true and only stops when thinking turns false.
Can I hide the time?
Pass labels.thinking and labels.thought that ignore the seconds argument.
Related
Collapsible card for an agent's tool call: name and arguments in the header, a status that morphs from spinner to drawn check or error, a live duration while running, and argument and output panes inside.
Live checklist of an agent's plan: pending, active with a pulsing halo, drawn check, error and skipped states, nested substeps that fold away when done, and a spring progress bar with a step count.
Reveals an answer token by token at a steady speed with a cursor that blinks while waiting for more, stop and resume, word or character mode, and smooth catch-up when uneven SSE chunks arrive; reduced motion shows sentence chunks.
User, assistant and system messages with avatars, grouping, a hover actions row (copy, good/bad, retry, edit), markdown-ish rendering of lists, quotes and fenced code into CodeBlocks, and a blinking caret while the answer streams.