Chain of Thought
A collapsible component that visualizes AI reasoning steps with animated progress indicators
A collapsible compound component that visualizes AI reasoning steps with animated spinners, status indicators, and optional output content. SDK-agnostic — works with any provider that exposes thinking/reasoning data.
Run demo
bunx @gridland/demo chain-of-thoughtcurl -fsSL https://raw.githubusercontent.com/thoughtfulllc/gridland/main/scripts/run-demo.sh | bash -s chain-of-thoughtInstallation
bunx create-gridland add chain-of-thoughtUsage
import {
ChainOfThought,
ChainOfThoughtHeader,
ChainOfThoughtContent,
ChainOfThoughtStep,
} from "@/components/ui/chain-of-thought"<ChainOfThought defaultOpen>
<ChainOfThoughtHeader duration="3.2s" />
<ChainOfThoughtContent>
<ChainOfThoughtStep label="Reading files" status="done" />
<ChainOfThoughtStep label="Planning changes" status="running" isLast />
</ChainOfThoughtContent>
</ChainOfThought>Examples
Collapsed
The default state is collapsed — only the header is visible.
<ChainOfThought>
<ChainOfThoughtHeader duration="3.2s" />
<ChainOfThoughtContent>
<ChainOfThoughtStep label="Reading files" isLast />
</ChainOfThoughtContent>
</ChainOfThought>Expanded
Pass defaultOpen to start expanded. Steps show their status indicators.
<ChainOfThought defaultOpen>
<ChainOfThoughtHeader duration="4.3s">Build pipeline</ChainOfThoughtHeader>
<ChainOfThoughtContent>
<ChainOfThoughtStep label="Reading codebase" description="src/" status="done" />
<ChainOfThoughtStep label="Planning changes" description="auth module" status="done" />
<ChainOfThoughtStep label="Editing files" description="4 files" status="running" />
<ChainOfThoughtStep label="Running tests" description="vitest" status="pending" isLast />
</ChainOfThoughtContent>
</ChainOfThought>Step Output
Pass children to a step to render output content below it with a pipe gutter.
<ChainOfThought defaultOpen>
<ChainOfThoughtHeader duration="2.1s" />
<ChainOfThoughtContent>
<ChainOfThoughtStep label="Reading config" status="done" />
<ChainOfThoughtStep label="Running tests" description="vitest" status="error">
FAIL src/auth.test.ts — expected 200, got 401
</ChainOfThoughtStep>
<ChainOfThoughtStep label="Fixing auth handler" status="running" isLast />
</ChainOfThoughtContent>
</ChainOfThought>Custom Header
Pass children to the header to replace the default "Thought for" label.
<ChainOfThought defaultOpen>
<ChainOfThoughtHeader duration="1.8s">Analyzing code</ChainOfThoughtHeader>
<ChainOfThoughtContent>
<ChainOfThoughtStep label="Parsing AST" status="done" isLast />
</ChainOfThoughtContent>
</ChainOfThought>Custom Icon
Override the default status dot with a custom character.
<ChainOfThought defaultOpen>
<ChainOfThoughtHeader duration="2.0s" />
<ChainOfThoughtContent>
<ChainOfThoughtStep label="Fetching data" icon="↓" status="done" />
<ChainOfThoughtStep label="Transforming" icon="⚙" status="done" isLast />
</ChainOfThoughtContent>
</ChainOfThought>Controlled
Use open and onOpenChange to control the collapsed state externally.
const [open, setOpen] = useState(false)
<ChainOfThought open={open} onOpenChange={setOpen}>
<ChainOfThoughtHeader duration="3.2s" />
<ChainOfThoughtContent>
<ChainOfThoughtStep label="Thinking" status="done" isLast />
</ChainOfThoughtContent>
</ChainOfThought>Compound Components
| Component | Description |
|---|---|
ChainOfThought | Root container with collapsible state |
ChainOfThoughtHeader | Expand/collapse arrow with label and duration |
ChainOfThoughtContent | Content wrapper — renders null when collapsed |
ChainOfThoughtStep | Individual step with status dot, label, and optional output |
API Reference
ChainOfThought
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | - | Controlled open state |
defaultOpen | boolean | false | Default open state (uncontrolled) |
onOpenChange | (open: boolean) => void | - | Called when the open state changes |
children | ReactNode | - | Sub-components |
ChainOfThoughtHeader
| Prop | Type | Default | Description |
|---|---|---|---|
duration | string | - | Duration string shown after the label (e.g. "3.2s") |
children | ReactNode | "Thought for" | Custom header text |
ChainOfThoughtContent
| Prop | Type | Description |
|---|---|---|
children | ReactNode | Steps to render when open |
ChainOfThoughtStep
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | - | Primary label for the step |
description | string | - | Secondary detail shown dimmed after the label |
status | "done" | "running" | "pending" | "error" | "done" | Step status |
icon | string | - | Custom icon character. Overrides the status-based default |
isLast | boolean | false | Hide the vertical pipe connector below |
children | ReactNode | - | Output content rendered below the step |
ChainOfThoughtStepData
Data type for driving steps from an array (e.g. via .map()).
| Field | Type | Description |
|---|---|---|
label | string | Primary label for the step |
description | string | Secondary detail shown dimmed after the label |
status | "done" | "running" | "pending" | "error" | Step status |
icon | string | Custom icon character |
output | string | Output text shown below the step |
Note: The
Steptype alias is deprecated. UseChainOfThoughtStepDatainstead.
Status Indicators
| Status | Dot | Color | Style |
|---|---|---|---|
done | ● | theme.success | Normal |
running | ⠋⠙⠹… | theme.primary | Bold, animated |
pending | ○ | theme.muted | Dim |
error | ● | theme.error | Normal |