Thinking Block

Collapsible block showing AI thinking/reasoning with streaming support. Install Thinking Block from the VLLNT UI registry with the shadcn CLI.

Report a bug

When to use this in an AI app

Use Thinking Block to reveal an agent's reasoning or scratchpad separately from its final answer. Collapsible plus streaming support keeps the transcript clean while staying transparent.

Browse all AI agent components

Collapsible block showing AI thinking/reasoning with streaming support. 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 Thinking Block 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/thinking-block.json

Source

"use client"; import { useCallback, useEffect, useId, useState } from "react"; import { ChevronDown, ChevronRight } from "lucide-react"; import { cn } from "../../lib/utils"; export type ThinkingBlockProps = { className?: string; /** Whether the content is still streaming. */ isStreaming?: boolean; /** The thinking text content to display. */ thinking: string; }; /** Collapsible thinking block with streaming support. */ export function ThinkingBlock({ className, isStreaming = false, thinking, }: ThinkingBlockProps) { const [isExpanded, setIsExpanded] = useState(() => isStreaming); const contentId = useId(); // Auto-open when streaming starts useEffect(() => { if (isStreaming) { requestAnimationFrame(() => { setIsExpanded(true); }); } }, [isStreaming]); const toggleExpanded = useCallback(() => { setIsExpanded((previous) => !previous); }, []); return ( <div className={cn("mb-2", className)}> <button aria-controls={contentId} aria-expanded={isExpanded} className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors" onClick={toggleExpanded} type="button" > {isExpanded ? ( <ChevronDown className="size-3" /> ) : ( <ChevronRight className="size-3" /> )} <span> Thinking {isStreaming ? ( <span className="ml-1 animate-pulse">&hellip;</span> ) : null} </span> </button> {isExpanded ? ( <div className="mt-2 p-3 bg-muted/50 rounded text-xs text-muted-foreground whitespace-pre-wrap border-l border-muted-foreground/30 max-h-48 overflow-y-auto" id={contentId} > {thinking} {isStreaming ? <span className="animate-pulse">|</span> : null} </div> ) : null} </div> ); }

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