v1.5

Chain of Thought

Collapsible reasoning timeline that shows how an assistant reached its answer, step by step.

Pro
  1. Read the request
    The user wants a login page that matches the existing dashboard.
  2. Check the components
    Button, Input and Field already cover every control on the form.
  3. Plan the layout
    A centered card with email, password and a single submit action.

Description

ChainOfThought is a compound disclosure built on Radix Collapsible. A one-line trigger summarizes the reasoning ("Thought for 6 seconds") and opens a timeline of steps. The steps render as an <ol> of <li> elements, each with a marker on a vertical rail, an optional label and free-form content.

Use it inside an assistant message to expose model reasoning, agent traces, research steps or a multi-stage tool run. Set isStreaming while the model is still thinking: the trigger shimmers and the root reports aria-busy. Give each step a status to show what is done, what is running, what failed and what is still queued.

Don't use it for a single tool call with input and output payloads, use ChatTool. For a plain loading line with no steps, use ChatLoader or TextShimmer. For a numbered, user-driven flow such as checkout, use Stepper, and for general expandable page sections use Accordion.

ChainOfThought is a Create UI Pro component. Install it with a Pro seat.

Installation

pnpm dlx @create-ui/cli add chain-of-thought

Anatomy

<ChainOfThought>
  <ChainOfThoughtTrigger />
  <ChainOfThoughtContent>
    <ChainOfThoughtSteps>
      <ChainOfThoughtStep />
    </ChainOfThoughtSteps>
  </ChainOfThoughtContent>
</ChainOfThought>

Usage

import {
  ChainOfThought,
  ChainOfThoughtContent,
  ChainOfThoughtStep,
  ChainOfThoughtSteps,
  ChainOfThoughtTrigger,
} from "@/components/ui/chain-of-thought"
<ChainOfThought>
  <ChainOfThoughtTrigger>Thought for 4 seconds</ChainOfThoughtTrigger>
  <ChainOfThoughtContent>
    <ChainOfThoughtSteps>
      <ChainOfThoughtStep label="Plan">
        Read the schema first.
      </ChainOfThoughtStep>
    </ChainOfThoughtSteps>
  </ChainOfThoughtContent>
</ChainOfThought>

Examples

Every example is a Pro preview (marked with a Pro badge).

Step status

status sets the marker and text tone of a step. complete is the default, active pulses the dot and shimmers the label, pending draws a hollow dot and error turns the marker and label red. Put the outcome in the label text too ("Migration failed"), since color alone is not announced.

Pro
  1. Read the schema
    Found 4 tables and 2 foreign keys.
  2. Migration failed
    Column users.email already exists.
  3. Write a fix
    Guarding the column with IF NOT EXISTS.
  4. Re-run the tests
    Waiting for the fix to land.

Step icons

icon replaces the dot with a glyph on the rail, sized to 16px and tinted by status. Use it when the kind of step matters more than its order: search, browse, write code.

Pro
  1. Search
    Queried the docs for collapsible animation tokens.
  2. Browse
    Opened the Radix Collapsible reference.
  3. Write code
    Drafted the trigger with a rotating chevron.

Trigger icon

icon on ChainOfThoughtTrigger adds a leading glyph that stays solid while the text shimmers. A long summary truncates with an ellipsis instead of pushing the chevron off the line.

Pro

Controlled

Pass open and onOpenChange to drive the disclosure from your own state, for example to collapse every reasoning block when a new message arrives. Leave both off and use defaultOpen for the uncontrolled case.

Pro

Streaming

isStreaming shimmers the trigger and sets aria-busy on the root. Move status="active" down the list as steps finish, then flip isStreaming off and close the block. The tree stays mounted the whole time, so nothing jumps when reasoning ends.

Pro
  1. Read the request
    Build a pricing table with 3 tiers.
  2. Check the tokens
    Surface and border tokens are in place.
  3. Draft the markup
    One card per tier, the middle one lifted.
  4. Review contrast
    Every label clears 4.5:1 on the surface.

Nested

Steps accept any content, including another ChainOfThought or a second ChainOfThoughtSteps. Each nested timeline keeps its own rail. Tighten a dense list with the --chain-of-thought-step-gap variable on ChainOfThoughtSteps.

Pro
  1. Plan
    Build the login form from the existing primitives.
  2. Explore
  3. Write
    Created app/login/page.tsx with email and password fields.

Accessibility

The trigger is a native <button> from Radix Collapsible, so it is focusable and toggles with the keyboard. The steps are plain content with no interactive parts of their own.

KeyDescription
TabMoves focus to the trigger, then into the content.
EnterOpens or closes the timeline.
SpaceOpens or closes the timeline.

ARIA notes:

  • Radix sets aria-expanded and aria-controls on the trigger and links it to the content region.
  • The root sets aria-busy="true" while isStreaming is on and removes it when streaming ends.
  • Steps render as <ol> and <li>, so screen readers announce the step count and position.
  • The step with status="active" gets aria-current="step". Other statuses are visual only, so say "failed" or "skipped" in the label when it matters.
  • The chevron, the trigger icon and the step rail (dot, icon and connector) are aria-hidden.
  • The focus outline is a 2px outline-strong offset by 2px, never a ring.
  • The open and close animation and the active dot pulse are skipped under prefers-reduced-motion. TextShimmer also stops its sweep there.
  • See the Radix Collapsible docs for the full disclosure behavior.

Styling

Tailwind override: pass className to any part. On the root it sets width and spacing, on the trigger it adjusts color or height, and on ChainOfThoughtSteps it can change the gap between steps:

<ChainOfThoughtSteps className="[--chain-of-thought-step-gap:0.25rem]" />

Data slots and attributes: the component sets these for CSS targeting:

  • data-slot="chain-of-thought" on the root, with data-status ("streaming" or "complete") and the Radix data-state ("open" or "closed") and data-disabled.
  • data-slot="chain-of-thought-trigger" on the button, with data-state and data-disabled.
  • data-slot="chain-of-thought-trigger-icon" on the leading icon wrapper, when icon is set.
  • data-slot="chain-of-thought-trigger-label" on the text wrapper. The shimmer inside it carries data-slot="text-shimmer" and data-active.
  • data-slot="chain-of-thought-trigger-indicator" on the chevron.
  • data-slot="chain-of-thought-content" on the collapsible region, with data-state, and data-slot="chain-of-thought-content-body" on its padded inner wrapper.
  • data-slot="chain-of-thought-steps" on the <ol>.
  • data-slot="chain-of-thought-step" on each <li>, with data-status ("pending", "active", "complete" or "error").
  • data-slot="chain-of-thought-step-rail" on the marker column, holding data-slot="chain-of-thought-step-indicator" (the 16px marker box), data-slot="chain-of-thought-step-dot" (the default dot, absent when icon is set) and data-slot="chain-of-thought-step-connector" (the line to the next step, hidden on the last one).
  • data-slot="chain-of-thought-step-body", data-slot="chain-of-thought-step-label" and data-slot="chain-of-thought-step-content" on the text column.
[data-slot="chain-of-thought-step"][data-status="error"]
  [data-slot="chain-of-thought-step-content"] {
  /* ... */
}

CSS variables: --chain-of-thought-step-gap sets the space below each step body, which is also how far the connector runs. It defaults to 0.75rem. Don't use gap-* on ChainOfThoughtSteps: a gap breaks the rail between steps. Radix also exposes --radix-collapsible-content-height on the content for the open and close animation.

  • Chat Tool: a single tool call with its input, output and status, rather than a reasoning timeline.
  • Chat Loader: a loading line or dots while the assistant has nothing to show yet.
  • Text Shimmer: the shimmer used by the trigger, on its own for a status line.
  • Stepper: a numbered flow the user moves through, not a log of what the model did.
  • Accordion: general expandable sections on a page.

API Reference

ChainOfThought

The root. Wraps Radix Collapsible.Root, so it accepts every Collapsible root prop and any <div> attribute.

Props

PropTypeDefaultDescription
isStreamingbooleanfalseShimmers the trigger and sets aria-busy on the root.
openboolean-Controlled open state.
defaultOpenbooleanfalseInitial open state when uncontrolled.
onOpenChange(open: boolean) => void-Fires when the trigger opens or closes the timeline.
disabledbooleanfalseDisables the trigger.
asChildbooleanfalseMerge props onto the child element instead of a <div>. (advanced)
classNamestring-Tailwind classes merged via cn().
childrenReact.ReactNode-A ChainOfThoughtTrigger and a ChainOfThoughtContent.

Variants

The root has no variant axis. chainOfThoughtVariants() returns its base classes (w-full min-w-0 text-paragraph-xs).

ChainOfThoughtTrigger

The summary line and toggle. Wraps Radix Collapsible.Trigger and renders a <button>, so any button attribute is accepted.

Props

PropTypeDefaultDescription
iconReact.ReactNode-Leading glyph, sized to 16px. Stays solid while the text shimmers.
disabledbooleanfalseBlocks toggling and dims the text.
asChildbooleanfalseMerge props onto the child element instead of a <button>. (advanced)
classNamestring-Tailwind classes merged via cn().
childrenReact.ReactNode-The summary text. Truncates to one line.

Variants

Set from the root's isStreaming, not as a prop. Exported as chainOfThoughtTriggerVariants.

VariantOptionsDefaultDescription
status"complete" "streaming""complete"complete is text-body and darkens on hover, streaming is text-strongest.

ChainOfThoughtContent

The collapsible region. Wraps Radix Collapsible.Content and animates its height open and closed.

Props

PropTypeDefaultDescription
forceMounttrue-Keep the content mounted while closed. (advanced)
classNamestring-Tailwind classes merged via cn().
childrenReact.ReactNode-Usually one ChainOfThoughtSteps. Any content works.

ChainOfThoughtSteps

The timeline list. Extends React.ComponentProps<"ol">.

Props

PropTypeDefaultDescription
classNamestring-Tailwind classes merged via cn(). Set --chain-of-thought-step-gap here.
childrenReact.ReactNode-ChainOfThoughtStep elements.

ChainOfThoughtStep

One entry on the rail. Extends React.ComponentProps<"li">.

Props

PropTypeDefaultDescription
status"pending" | "active" | "complete" | "error""complete"Marker style and text tone. active also shimmers the label.
labelReact.ReactNode-Short heading above the content, in text-body.
iconReact.ReactNode-Replaces the dot on the rail with a 16px glyph.
classNamestring-Tailwind classes merged via cn().
childrenReact.ReactNode-Step content, in text-strongest (text-body when pending).

Variants

Exported as chainOfThoughtStepIndicatorVariants, applied to the marker box.

VariantOptionsDefaultDescription
status"pending" "active" "complete" "error""complete"text-placeholder for pending and complete, text-strongest for active, text-error-base for error.

Types

type ChainOfThoughtStepStatus = "pending" | "active" | "complete" | "error"