Skip to content
txbrendan/ui

Thinking State

beta

What 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

  1. Read 12 files (working)

    app/, lib/, registry/txbrendan/ui/

Read 12 files

loading

  1. Read 12 files

    app/, lib/, registry/txbrendan/ui/

  2. Planned the change

  3. Edited card.tsx (working)

Edited card.tsx

done

Thought for 34s, 5 steps

opened

  1. Read 12 files

    app/, lib/, registry/txbrendan/ui/

    3s
  2. Planned the change

    2s
  3. Edited card.tsx

    9s
  4. Ran the tests

    14s
  5. Checked the page in light and dark

    6s
Thought for 34s, 5 steps

summary

Checked 2 files

Install

  1. 1. Add the registry to components.json, once per app. Without it, the shadcn CLI looks up @txbrendan in a public list that txbrendan/ui does not control.

    npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"
  2. 2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.

    npx shadcn@latest add @txbrendan/theme
  3. 3. 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

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" },
  ]}
/>