Chain of Thought

Ordered, status-aware visualization of a model's chain of thought with per-step state. Install Chain of Thought from the VLLNT UI registry with the shadcn CLI.

Report a bug

Ordered, status-aware visualization of a model's chain of thought with per-step state. Part of the ai family in VLLNT UI, it ships as a machine-readable registry entry — copy the source directly into your app with the shadcn CLI and own it, no runtime dependency on a component library.

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Installation

Add Chain of Thought to your project with the shadcn CLI. The source lands in your codebase, ready to adapt:

pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/chain-of-thought.json

Source

import { cva, type VariantProps } from "class-variance-authority"; import { Check, LoaderCircle, X } from "lucide-react"; import { cn } from "../../lib/utils"; const markerVariants = cva( "flex size-6 shrink-0 items-center justify-center rounded-full border text-xs font-medium", { defaultVariants: { status: "pending", }, variants: { status: { active: "border-primary bg-primary/10 text-primary", complete: "border-transparent bg-primary text-primary-foreground", error: "border-transparent bg-destructive text-destructive-foreground", pending: "border-border bg-muted text-muted-foreground", }, }, }, ); export type ChainOfThoughtStatus = NonNullable< VariantProps<typeof markerVariants>["status"] >; export type ChainOfThoughtStep = { /** Optional supporting detail shown under the title. */ description?: string; /** Current status of the step. Defaults to "pending". */ status?: ChainOfThoughtStatus; /** Short step title. */ title: string; }; export type ChainOfThoughtProps = { className?: string; /** Ordered steps to render. */ steps: ChainOfThoughtStep[]; }; function StepMarkerIcon({ index, status, }: { index: number; status: ChainOfThoughtStatus; }) { if (status === "complete") { return <Check className="size-3.5" />; } if (status === "error") { return <X className="size-3.5" />; } if (status === "active") { return <LoaderCircle className="size-3.5 animate-spin" />; } return <span>{index + 1}</span>; } function ChainOfThoughtItem({ index, isLast, step, }: { index: number; isLast: boolean; step: ChainOfThoughtStep; }) { const status = step.status ?? "pending"; return ( <li className="flex gap-3"> <div className="flex flex-col items-center"> <span className={markerVariants({ status })}> <StepMarkerIcon index={index} status={status} /> </span> {isLast ? null : <span className="my-1 w-px flex-1 bg-border" />} </div> <div className={cn("pb-4", isLast && "pb-0")}> <p className={cn( "text-sm font-medium", status === "pending" ? "text-muted-foreground" : "text-foreground", )} > {step.title} </p> {step.description ? ( <p className="mt-0.5 text-xs text-muted-foreground"> {step.description} </p> ) : null} </div> </li> ); } /** * Ordered, status-aware visualization of a model's chain of thought. * * Renders a numbered vertical step list where each step carries a status * ("pending" | "active" | "complete" | "error") reflected in its marker icon * and color. A vertical rail links the steps. * * @example * <ChainOfThought * steps={[ * { status: "complete", title: "Read the file" }, * { status: "active", title: "Apply the edit" }, * { title: "Run the tests" }, * ]} * /> */ export const ChainOfThought = ({ className, ref, steps, }: ChainOfThoughtProps & { ref?: React.Ref<HTMLOListElement> }) => ( <ol className={cn("flex flex-col", className)} ref={ref}> {steps.map((step, index) => ( <ChainOfThoughtItem index={index} isLast={index === steps.length - 1} key={`${index}-${step.title}`} step={step} /> ))} </ol> ); ChainOfThought.displayName = "ChainOfThought";

Stories

Explore every variant and state in the interactive Storybook:

Preview

Switch between light and dark to inspect the embedded Storybook preview.

Dependencies

  • @vllnt/ui@^0.3.0
  • lucide-react