Thought Trail

An agent's steps as they happen. The step at work shimmers and streams its details; when the next one starts, it folds up into a one-line summary and the rail fills down to the new step. Click any summary to open it again.

  • AI
  • Status
  • Motion

Uses motion, clsx, tailwind-merge

Open in ChatGPTOpen in Claude

Task

Add a dark mode toggle to the settings page

Installation

Add the component on its own with the CLI, or copy the file in by hand.

The CLI writes components/ui/thought-trail.tsx and installs motion, clsx and tailwind-merge. It needs the @keyframeui registry in components.json; add it once per project.

npx shadcn@latest add @keyframeui/thought-trail

Examples

Finished sections built with Thought Trail. Each one installs whole, with the component included.

Chat Reasoning

A chat reply that thinks first. The message's parts are shaped like the AI SDK's, and each reasoning or tool part becomes a step. The trail folds into "Thought for 9s" as soon as the answer starts streaming below it.

React help

Why does my useEffect run twice in development?

Ask a follow-up…

Deploy Log

A CI pipeline with every step listed up front as pending. Logs stream into the running step, pinned to the newest line, and each step keeps its time. Turn on Break a test: the run stops at the tests, which turn red and stay open, and the steps after them never start.

acme/storefront

main8f3c2a1 · Fix cart badge overflow

Research Agent

Steps with their own icons. Sources collect as chips while the agent searches, pages tick off as it reads, and the answer cites what it found once the trail folds away. Open Found 9 sources to see them again.

Deep research

Which vector database should we use for 10M embeddings?

Props

running

TypebooleanDefaultfalse

The agent is working: the header shimmers and counts seconds. When it turns false the trail finishes. The header rolls to its done label and, with collapseOnDone, the trail folds into it a beat later. Turning it true again starts a new run and opens the trail.

label

TypeReactNodeDefault"Working"

Header text while running, e.g. Thinking. If the reader folds the trail mid-run, the current step's title follows it.

doneLabel

Type(info: { seconds, steps, failed }) => ReactNode

Header text once finished. Defaults to Worked for 14s · 5 steps, or Stopped after 9s · 3 steps when a step failed. seconds comes from duration or the trail's own clock and is null when neither knows; steps counts steps that started.

duration

Typenumber

Seconds the run took, for a trail restored from history, where the trail never saw it run. Overrides the trail's own clock in doneLabel.

collapseOnDone

TypebooleanDefaulttrue

Fold the trail into its header 0.8s after it finishes. A failed run stays open, and so does a trail the reader opened or folded during the run.

defaultOpen

TypebooleanDefaultrunning || !collapseOnDone

Open on mount. By default a running trail starts open and a finished one from history starts folded.

open

Typeboolean

Control the fold yourself. The trail then never opens or folds on its own; update it from onOpenChange.

onOpenChange

Type(open: boolean) => void

Called with the new state when the reader clicks the header.

icon

TypeReactNode

Shown before the header text, in the same column as the step dots, so the label lines up with the step titles.

className

Typestring

Extra classes for the trail. Other div props pass through. It renders the header and an <ol> of steps; every step must be a direct child of ThoughtTrail.

step.status

Type"pending" | "active" | "done" | "error"Default"done"

ThoughtTrailStep: where the step is. pending is a dimmed title with a hollow dot, for steps listed before they run. active shimmers and shows its details. done folds them and shows summary with a check, and the rail fills down to the next step. error turns red with a cross, shakes once and keeps its details open. Every change rolls the line to its new text.

step.titleRequired

TypeReactNode

ThoughtTrailStep: what the step is doing, e.g. Searching the docs. One line, truncated. While active, a band of light sweeps across it in its own color.

step.summary

TypeReactNode

ThoughtTrailStep: what it did, e.g. Read 4 pages. Replaces the title once the step is done or failed. Without it, the title stays.

step.icon

TypeReactNode

ThoughtTrailStep: replaces the dot and the check. While active, the working arc circles it; a failure still shows the cross. SVGs are sized to 14px.

step.meta

TypeReactNode

ThoughtTrailStep: small text at the end of the row, like a duration or a count.

step.children

TypeReactNode

ThoughtTrailStep: the details, like streamed text, logs or sources. Open while the step is active or failed, folded once it's done, and the reader can open or close them at any time; a step they opened stays open. As content streams in, the box grows on a spring rather than in jumps.

step.contentClassName

Typestring

ThoughtTrailStep: classes for the details box. Give it a max height (max-h-32 overflow-y-auto) and, while the step is active, it stays scrolled to the newest line unless the reader scrolls up.

step.className

Typestring

ThoughtTrailStep: classes for the step's <li>.