The Reasoning component is a collapsible interface designed to show assistant thinking/reasoning traces (e.g. from models like DeepSeek-R1 or Claude 3.7 Sonnet). It automatically handles stream duration timing, auto-opens while streaming, auto-closes when done (configurable), and renders streaming markdown gracefully using Streamdown.
Preview
Usage
Installation
Manual Installation
Install the required dependencies:
pnpm add @radix-ui/react-collapsible streamdown tw-shimmer
Import the component code into your project:
Copy and paste reasoning.tsx into your components directory (e.g. components/infinity-ui/reasoning.tsx).
Basic Example
import {
Reasoning,
ReasoningTrigger,
ReasoningContent,
} from "@/components/infinity-ui/reasoning";
export default function App() {
const isReasoningStreaming = true;
const reasoningMarkdown = "Analyzing the dataset... \n- Check column names\n- Validate types";
return (
<Reasoning isStreaming={isReasoningStreaming}>
<ReasoningTrigger />
<ReasoningContent>{reasoningMarkdown}</ReasoningContent>
</Reasoning>
);
}
Vercel AI SDK Integration
Some models emit structured reasoning parts in the AI SDK response stream. In practice, models like DeepSeek R1 and Claude can return reasoning content that you can render with Reasoning.
Use Reasoning with Vercel AI SDK by mapping assistant reasoning parts:
Install the AI SDK:
pnpm add ai @ai-sdk/react
Create your chat API route (e.g., enable Anthropic thinking so reasoning parts are returned):
// app/api/chat/route.ts
import { streamText, smoothStream, UIMessage, convertToModelMessages } from "ai";
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: "anthropic/claude-sonnet-4.5",
messages: await convertToModelMessages(messages),
experimental_transform: smoothStream({
chunking: "word",
delayInMs: 18,
}),
providerOptions: {
anthropic: {
thinking: { type: "enabled", budgetTokens: 1024 },
},
},
});
return result.toUIMessageStreamResponse({
sendReasoning: true,
});
}
Render assistant reasoning parts in your chat interface:
"use client";
import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport, isReasoningUIPart, type UIMessage } from "ai";
import {
Reasoning,
ReasoningContent,
ReasoningTrigger,
} from "@/components/infinity-ui/reasoning";
function reasoningPartsFromMessage(message: UIMessage) {
return message.parts.filter(isReasoningUIPart);
}
function reasoningTextFromMessage(message: UIMessage) {
return reasoningPartsFromMessage(message).map((p) => p.text).join("");
}
function reasoningStreamingFromMessage(message: UIMessage) {
const parts = reasoningPartsFromMessage(message);
return parts.some((p) => p.state === "streaming");
}
export default function ChatView() {
const { messages } = useChat({
transport: new DefaultChatTransport({ api: "/api/chat" }),
});
const assistant = [...messages].reverse().find((m) => m.role === "assistant");
if (!assistant) return null;
const reasoningText = reasoningTextFromMessage(assistant);
const isReasoningStreaming = reasoningStreamingFromMessage(assistant);
if (!reasoningText.trim()) return null;
return (
<Reasoning isStreaming={isReasoningStreaming}>
<ReasoningTrigger />
<ReasoningContent>{reasoningText}</ReasoningContent>
</Reasoning>
);
}
API Reference
Reasoning
Root component. Manages streaming timing, open state behavior, and label context. Wraps @radix-ui/react-collapsible's Root primitive.
| Prop | Type | Default | Description |
|---|---|---|---|
isStreaming | boolean | false | Controls reasoning stream lifecycle. Drives timer, auto-open/close states, and trigger label text updates. |
open | boolean | — | Controlled open state of the collapsible content. |
defaultOpen | boolean | false | Initial open state in uncontrolled mode. |
onOpenChange | (open: boolean) => void | — | Callback fired when the open state changes (both manual toggle and stream-driven auto-expand/collapse). |
ReasoningTrigger
Trigger row with a built-in Brain icon and rotating indicator chevron. Wraps @radix-ui/react-collapsible's Trigger primitive.
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Optional custom label override for the trigger button. When omitted, uses the contextual label (e.g. Thinking... or Thought for N seconds). |
ReasoningContent
Collapsible content container that handles smooth expansion animations and renders markdown stream tokens. Wraps @radix-ui/react-collapsible's Content primitive.
| Prop | Type | Default | Description |
|---|---|---|---|
children | string | — | Markdown text to render. This component expects a raw string and streams it through Streamdown. |