Chain of thought
Thinking laid out as steps: a status list, a one-line summary that opens up, a research trail of searches and pages read, options weighed and ruled out, and steps woven into the answer as it writes.
Loading preview…
Install
- 1
Run this command in your Tailwind CSS project. It adds
components/ai/chain-of-thought-default.tsxand installs its dependencies.$ npx shadcn@latest add https://gradientdeck.com/ai-components/r/chain-of-thought-default.json
Usage & API
Example
import { ChainOfThought } from "@/components/ai/chain-of-thought-default"
<ChainOfThought
steps={[]}
/>Props ChainOfThought
| Prop | Type & description | Required |
|---|---|---|
steps | ChainStep[] | Required |
Dependencies
- React
- Tailwind CSS
- motion
- lucide-react
Implementation notes
Steps exports ChainOfThought with a single steps prop. The list is an ordered list labelled Progress. The active step sets aria-current to step, draws a pulse ring unless reduced motion is on, and shines its label. A done step shows a check and fills the connector from the previous icon. A pending step is grey, and its detail and sources stay hidden. Source marks are circles with the first letter of source.name, stacked with a slight overlap. The preview cycles four steps for a savings-account question: Searching with the detail high-yield savings accounts 2026, Reading 6 sources across nerdwallet.com, reddit.com, bankrate.com, investopedia.com, ft.com, and which.co.uk, then Comparing rates, fees and withdrawal limits, then Writing a short list.
Example
import { ThoughtSummary } from "@/components/ai/chain-of-thought-summary"
<ThoughtSummary
steps={[]}
current={1}
done={true}
duration="…"
open={true}
onOpenChange={() => {}}
/>Props ThoughtSummary
| Prop | Type & description | Required |
|---|---|---|
steps | ThoughtStep[] | Required |
current | numberIndex of the step being worked on. Ignored once `done` is true. | Required |
done | boolean | Required |
duration | stringShown once done, e.g. "14s". | Required |
open | boolean | Required |
onOpenChange | (open: boolean) => void | Required |
Dependencies
- React
- Tailwind CSS
- motion
- lucide-react
Implementation notes
Collapsed summary exports ThoughtSummary. Props are steps, current, done, duration, open, and onOpenChange. Each ThoughtStep has an id, a label, and an optional detail. Until done is true, the row is a status with the current label, an ellipsis, a shine, and a counter of current + 1 over the length. current is ignored once done is true. The finished button reads Thought for {duration}, a dot, and the step count, with a chevron. Opening it reveals the labels and details. The preview is a UK sole-trader bill: £48,200 profit, personal allowance £12,570, income tax £7,126.00, Class 4 National Insurance £2,137.80, and a note that Class 2 is no longer charged from April 2024 above £6,725 profit. Copy and the vote buttons under the answer are demo chrome.
Example
import { ResearchTrail } from "@/components/ai/chain-of-thought-research"
<ResearchTrail
steps={[]}
revealed={1}
open={[]}
onToggle={() => {}}
/>Props ResearchTrail
| Prop | Type & description | Required |
|---|---|---|
steps | ResearchStep[] | Required |
revealed | numberHow many actions, counted across all steps, have arrived so far. | Required |
open | string[]Step ids that are open. | Required |
onToggle | (id: string) => void | Required |
Dependencies
- React
- Tailwind CSS
- motion
- lucide-react
Implementation notes
Research trail exports ResearchTrail. Props are steps, revealed, open, and onToggle. revealed counts actions across every step, not steps themselves. A step with nothing revealed yet is omitted. Each ResearchStep has a title and actions of three kinds: search, with a query and result sites; read, with page titles, sites, and optional dates; or note, with a text line. The row summarises what is visible, for example a search count and a page count. An unfinished title shines. A finished step shows a check. Opening a step lists the actions that have arrived. The preview walks a health question through PubMed, BMJ, Cochrane, Harvard, NHS, and other sites, including a note under an indigo rule.
Example
import { OptionsWeighed } from "@/components/ai/chain-of-thought-branches"
<OptionsWeighed
options={[]}
visible={1}
decided={1}
open={null}
onOpenChange={() => {}}
/>Props OptionsWeighed
| Prop | Type & description | Required |
|---|---|---|
options | WeighedOption[] | Required |
visible | numberHow many options have appeared. | Required |
decided | numberHow many of those have a verdict. | Required |
open | string | nullThe option whose reasoning is open, if any. | Required |
onOpenChange | (id: string | null) => void | Required |
Dependencies
- React
- Tailwind CSS
- motion
- lucide-react
Implementation notes
Options weighed exports OptionsWeighed. Props are options, visible, decided, open, and onOpenChange. visible is how many options have appeared. decided is how many of those have a verdict. An option still being weighed shows a pulsing dot, a shining title, and its reasoning, and the button is disabled. A chosen option gets a check, an indigo border, and a Going with this badge. A rejected option gets an X, a struck-through title, and its reason on the same row. Clicking a decided option toggles the longer reasoning. The preview weighs a Postgres 12 to 16 upgrade: pg_dump is rejected because a 1.2 TB restore means hours of downtime, pg_upgrade --link is rejected because there is no way back once 16 takes writes, logical replication is chosen, and a managed blue/green deployment is rejected because the database is self-hosted.
Example
import { WovenAnswer } from "@/components/ai/chain-of-thought-interleaved"
<WovenAnswer
blocks={[]}
at={1}
words={1}
open={[]}
onToggle={() => {}}
/>Props WovenAnswer
| Prop | Type & description | Required |
|---|---|---|
blocks | AnswerBlock[] | Required |
at | numberIndex of the block being written. Everything before it is finished. | Required |
words | numberWords shown of the block being written, when it's text. | Required |
open | string[] | Required |
onToggle | (id: string) => void | Required |
Dependencies
- React
- Tailwind CSS
- motion
- lucide-react
Implementation notes
Woven into the answer exports WovenAnswer. Props are blocks, at, words, open, and onToggle. at is the index of the block being written. words is how many words of the current text block to show. open is the ids of step chips that are expanded. A text block streams word by word. A step block is a chip: while it is the current block it shows working with a shine and the button is disabled; once it is finished it shows done and a chevron. Opening it lists rows of primary, secondary, and meta. The root sets aria-busy while at is still inside the list. The preview plans a Kyoto stop: Nozomi 237 from Tokyo at 15:51 arriving 18:06, Hotel Vischio at ¥17,800 against Granvia at ¥31,000, and a 06:17 JR Nara Line train to Inari for ¥150.
Choosing a layout
Use Steps when you already have a short ordered plan and a status for each item, such as searching, reading, comparing, and writing. Use Collapsed summary when the steps are worth keeping but should not occupy the thread once the answer exists. Use Research trail when the interesting part is the queries and the pages, not a generic progress line. Use Options weighed when the model compared alternatives and rejected some. Use the woven answer when a lookup belongs in the middle of the paragraph it supports, not in a block above it.
Do not use these as a bibliography. The favicon row on a step, and the site chips inside a research action, are part of the trace. The full set of sources after the answer belongs to Sources list, and a marker inside a claim belongs to Inline citation. Do not use them for a pause before any step exists. That is Loader. And do not pass the finished answer as a step label unless you are using the woven variant, which is built to interleave text blocks and step chips.
Why does Collapsed summary hide the list while it is still working?
ThoughtSummary only mounts the step list when done is true and open is true. During the run it shows one label, the step at current, plus a counter. When you set done, current stops mattering and the button shows duration, which is already a string such as 14s, not a number of milliseconds. You own open through onOpenChange. The component does not open itself when the run finishes.
How do visible, revealed, and at differ?
They are three ways of saying how far a run has got, on three different components. OptionsWeighed uses two counts: visible options have been drawn, and decided of those have a verdict. ResearchTrail uses one count, revealed, which walks actions in order across steps, so a step can be half shown. WovenAnswer uses at as a block index and words as a word count inside a text block. Steps is different again: you set status on each ChainStep yourself. None of these props start a timer.