Thinking State
betaWhat an agent is doing, step by step. The list grows while it works, then folds into one line, like Thought for 34s, 5 steps.
Preview
live
Read 12 files (working)
loading
Read 12 files
Planned the change
Edited card.tsx (working)
done
opened
Read 12 files
Planned the change
Edited card.tsx
Ran the tests
Checked the page in light and dark
summary
Install
1. Add the registry to
components.json, once per app. Without it, the shadcn CLI looks up@txbrendanin a public list that txbrendan/ui does not control.npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.
npx shadcn@latest add @txbrendan/theme3. Add the component.
npx shadcn@latest add @txbrendan/thinking-state
Use it for
- Above an agent's answer, to show the steps it took: Read 12 files, Ran the tests.
- Pass loading while the agent works. Add each step to the end of steps as it starts. The last one spins.
- Set loading to false when it is done. The list folds into one line that opens again.
- Pass duration, like 34s, and keep it current while loading for a live timer. The folded line uses it.
- Give each step an id, so a new one fades in and the rest stay still.
- Put the files or sources a step used in detail, as a quiet second line.
Avoid it for
- A single wait with no steps. Use Spinner.
- One tool call with its inputs and result. Use ToolCallCard.
- Progress with a known end, like 3 of 5 files sent. Use Progress.
Props
- steps
Required. The steps so far, oldest first. While loading, the last one is under way.
{ id?: string; label: ReactNode; detail?: ReactNode; time?: string }[]
- loading
The agent is still working. The list stays open and grows. When it turns false, the list folds into one line.
boolean · default false
- duration
How long the agent has worked, or worked in all, like "34s".
string
- label
The line while loading. It pulses slowly.
ReactNode · default "Thinking"
- summary
The folded line once done. Default "Thought for 34s, 5 steps", from duration and the step count.
ReactNode
- defaultOpen
Starts open or folded. Default: open while loading, folded when done.
boolean
- open
Controls whether it is open. Pair it with onOpenChange. Then it no longer folds itself.
boolean
- onOpenChange
Called when it opens or folds.
(open: boolean) => void
- className
Extra classes on the root. Later classes win.
string
| Prop | Type | Default | What it does |
|---|---|---|---|
| steps | { id?: string; label: ReactNode; detail?: ReactNode; time?: string }[] | - | Required. The steps so far, oldest first. While loading, the last one is under way. |
| loading | boolean | false | The agent is still working. The list stays open and grows. When it turns false, the list folds into one line. |
| duration | string | - | How long the agent has worked, or worked in all, like "34s". |
| label | ReactNode | "Thinking" | The line while loading. It pulses slowly. |
| summary | ReactNode | - | The folded line once done. Default "Thought for 34s, 5 steps", from duration and the step count. |
| defaultOpen | boolean | - | Starts open or folded. Default: open while loading, folded when done. |
| open | boolean | - | Controls whether it is open. Pair it with onOpenChange. Then it no longer folds itself. |
| onOpenChange | (open: boolean) => void | - | Called when it opens or folds. |
| className | string | - | Extra classes on the root. Later classes win. |
Accessibility
- The line is one button that opens and folds the steps, and it tells screen readers if it is open.
- The steps are an ordered list named Steps. The one under way says (working) to screen readers.
- Each new step is said politely in a hidden status region, and so is the summary when the agent is done.
- The spinners and the pulse stop when the theme turns motion off, and for reduced motion. The list then opens and folds at once.
Examples
While working
<ThinkingState
loading
duration="12s"
steps={[
{ id: "read", label: "Read 12 files", detail: "app/, lib/, registry/" },
{ id: "test", label: "Ran the tests" },
]}
/>Done
<ThinkingState
duration="34s"
steps={[
{ id: "read", label: "Read 12 files" },
{ id: "test", label: "Ran the tests" },
{ id: "fix", label: "Fixed the failing test" },
]}
/>