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.

MarkdownOpen in v0
Open in v0

Installation

$ npx shadcn@latest add https://goodui.dev/r/reasoning-block.json

The 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

Use it for
  • 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.
Reach for something else
  • 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 needUse
Live thinkingthinking plus steps that grow as summaries arrive
Finished with the API's durationthinking={false} duration={seconds}
Full reasoning textchildren (rendered under the steps)
No current-step line under the headerpreview={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

PropTypeDefault
thinking
The model is still reasoning: the label shimmers and the timer runs.
booleanfalse
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—
openboolean—
defaultOpenbooleanfalse
onOpenChange(open: boolean) => void—
preview
Show the current step under the header while collapsed and thinking.
booleantrue
labels
Text for built-in controls and screen readers; pass translations here.
Partial<ReasoningBlockLabels>—

Also accepts every <div> attribute.

Accessibility

Enter / SpaceToggles 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