import { ReasoningSample, ReasoningGroupSample } from "@/components/docs/samples/reasoning";
Getting Started#
Add reasoning#
<InstallCommand shadcn={["reasoning"]} />
This adds a /components/assistant-ui/reasoning.tsx file to your project.
Use in your application#
Previously, reasoning parts were rendered via components.Reasoning and grouped via components.ReasoningGroup on MessagePrimitive.Parts. Both are deprecated; MessagePrimitive.GroupedParts is the supported replacement, and the highlighted lines below show the new pieces.
Render reasoning parts through MessagePrimitive.GroupedParts. Group consecutive reasoning parts with "group-reasoning", then compose ReasoningRoot, ReasoningTrigger, ReasoningContent, and ReasoningText around the grouped children.
While reasoning is streaming, part.status.type === "running". Pass that to defaultOpen so the accordion auto-opens during streaming and lets the user collapse it once the model moves on.
import { MessagePrimitive } from "@assistant-ui/react";
import { MarkdownText } from "@/components/assistant-ui/markdown-text";
import { ToolFallback } from "@/components/assistant-ui/tool-fallback";
import {
Reasoning,
ReasoningContent,
ReasoningRoot,
ReasoningText,
ReasoningTrigger,
} from "@/components/assistant-ui/reasoning"; // [!code ++]
const AssistantMessage: FC = () => {
return (
<MessagePrimitive.Root className="...">
<div className="...">
<MessagePrimitive.GroupedParts // [!code ++]
groupBy={(part) => { // [!code ++]
if (part.type === "reasoning") return ["group-reasoning"]; // [!code ++]
return null; // [!code ++]
}} // [!code ++]
> // [!code ++]
{({ part, children }) => { // [!code ++]
switch (part.type) { // [!code ++]
case "group-reasoning": { // [!code ++]
const running = part.status.type === "running"; // [!code ++]
return ( // [!code ++]
<ReasoningRoot defaultOpen={running}> // [!code ++]
<ReasoningTrigger active={running} /> // [!code ++]
<ReasoningContent aria-busy={running}> // [!code ++]
<ReasoningText>{children}</ReasoningText> // [!code ++]
</ReasoningContent> // [!code ++]
</ReasoningRoot> // [!code ++]
); // [!code ++]
} // [!code ++]
case "text": // [!code ++]
return <MarkdownText />; // [!code ++]
case "reasoning": // [!code ++]
return <Reasoning {...part} />; // [!code ++]
case "tool-call": // [!code ++]
return part.toolUI ?? <ToolFallback {...part} />; // [!code ++]
default: // [!code ++]
return null; // [!code ++]
} // [!code ++]
}} // [!code ++]
</MessagePrimitive.GroupedParts> // [!code ++]
</div>
<AssistantActionBar />
<BranchPicker className="..." />
</MessagePrimitive.Root>
);
};
GroupedParts calls your render function for both group nodes and leaf parts. The case "group-reasoning" branch renders the collapsible shell and must render {children}; that is where the individual reasoning parts get placed. The case "reasoning" branch renders each individual reasoning part inside that shell and must not render children. Removing either case breaks rendering.
How It Works#
The component consists of two parts:
Reasoning: Renders individual reasoning message part content (with markdown support)- Composable group pieces (
ReasoningRoot,ReasoningTrigger,ReasoningContent,ReasoningText): Wrap grouped reasoning children in a collapsible container
Consecutive reasoning parts are grouped by MessagePrimitive.GroupedParts. Use the composable API below to control the grouped layout.
When using the composable API,
ReasoningTextis a plain container. Add<MarkdownText />for markdown rendering.
Variants#
Use the variant prop on ReasoningRoot to change the visual style:
<ReasoningRoot variant="outline">...</ReasoningRoot>
<ReasoningRoot variant="ghost">...</ReasoningRoot>
<ReasoningRoot variant="muted">...</ReasoningRoot>
| Variant | Description |
|---|---|
outline | Rounded border (default) |
ghost | No additional styling |
muted | Muted background |
Legacy ReasoningGroup#
ReasoningGroup is kept for existing code that still uses the deprecated components.ReasoningGroup prop on MessagePrimitive.Parts. New code should use MessagePrimitive.GroupedParts and compose the root/trigger/content pieces directly.
import { ReasoningGroup } from "@/components/assistant-ui/reasoning";
const ReasoningGroupImpl: ReasoningGroupComponent = ({
children,
startIndex,
endIndex,
}) => {
const isReasoningStreaming = useAuiState((s) => {
if (s.message.status?.type !== "running") return false;
const lastIndex = s.message.parts.length - 1;
if (lastIndex < 0) return false;
const lastType = s.message.parts[lastIndex]?.type;
if (lastType !== "reasoning") return false;
return lastIndex >= startIndex && lastIndex <= endIndex;
});
return (
<ReasoningRoot defaultOpen={isReasoningStreaming}>
<ReasoningTrigger active={isReasoningStreaming} />
<ReasoningContent aria-busy={isReasoningStreaming}>
<ReasoningText>{children}</ReasoningText>
</ReasoningContent>
</ReasoningRoot>
);
};
API Reference#
Composable API#
All sub-components are exported for custom layouts:
| Component | Description |
|---|---|
ReasoningRoot | Collapsible container with scroll lock |
ReasoningTrigger | Button with icon, label, and shimmer |
ReasoningContent | Animated collapsible content wrapper |
ReasoningText | Text wrapper with slide/fade animation |
ReasoningFade | Gradient fade overlay at bottom |
import {
ReasoningRoot,
ReasoningTrigger,
ReasoningContent,
ReasoningText,
ReasoningFade,
} from "@/components/assistant-ui/reasoning";
<ReasoningRoot variant="muted">
<ReasoningTrigger active={isStreaming} />
<ReasoningContent>
<ReasoningText>{children}</ReasoningText>
</ReasoningContent>
</ReasoningRoot>
Related Components#
- ToolGroup - Similar grouping pattern for tool calls
- PartGrouping - Advanced grouping options for message parts