Message primitives for rendering assistant and user turns, message parts, attachments, actions, editing, and branch controls.
For examples and usage patterns, see Message.
Anatomy
import { MessagePrimitive } from "@assistant-ui/react";
const UserMessage = () => (
<MessagePrimitive.Root>
User: <MessagePrimitive.Parts />
<BranchPicker />
<ActionBar />
</MessagePrimitive.Root>
);
const AssistantMessage = () => (
<MessagePrimitive.Root>
Assistant: <MessagePrimitive.Parts />
<BranchPicker />
<ActionBar />
</MessagePrimitive.Root>
);API Reference
Root
The root container component for a message. This component provides the foundational wrapper for message content and handles hover state management for the message. It automatically tracks when the user is hovering over the message, which can be used by child components like action bars. When `turnAnchor="top"` is set on the viewport, this component automatically registers itself as the top-anchor user message (when it's the previous user message) or as the top-anchor target (when it's the streaming assistant response). No additional component is required.This primitive renders a <div> element unless asChild is set.
<MessagePrimitive.Root>
<MessagePrimitive.Content />
<ActionBarPrimitive.Root>
<ActionBarPrimitive.Copy />
<ActionBarPrimitive.Edit />
</ActionBarPrimitive.Root>
</MessagePrimitive.Root>- asChildboolean= false
Change the default rendered element for the one passed as a child, merging their props and behavior.
Read the Composition guide for more details.- render?ReactElement | undefined
Parts
Renders the parts of a message with web-specific default components.- components?StandardComponents | ChainOfThoughtComponents | undefined
Component configuration for rendering different types of message content. Use either `Reasoning`/`tools`/`ToolGroup`/`ReasoningGroup` for standard rendering, or `ChainOfThought` to group all reasoning and tool-call parts into a single collapsible component. These two modes are mutually exclusive.
MessagePrimitivePartsProps["components"]- Empty?EmptyMessagePartComponent | undefined
Component for rendering empty messages
- Text?TextMessagePartComponent | undefined
Component for rendering text content
- Source?SourceMessagePartComponent | undefined
Component for rendering source content
- Image?ImageMessagePartComponent | undefined
Component for rendering image content
- File?FileMessagePartComponent | undefined
Component for rendering file content
- Unstable_Audio?Unstable_AudioMessagePartComponent | undefineddeprecated
Component for rendering audio content.
Deprecated: Render audio through the `File` slot instead, branching on an `audio/*` mime type.
- data?DataConfig | undefined
Configuration for data part rendering
MessagePrimitivePartsProps["components"]["data"]- by_name?Record<string, DataMessagePartComponent | undefined> | undefined
Map data event names to specific components
- Fallback?DataMessagePartComponent | undefined
Fallback component for unmatched data events
- Quote?QuoteMessagePartComponent | undefined
Component for rendering a quoted message reference (from metadata, not parts)
- generativeUI?MessagePrimitivePartsProps["components"]["generativeUI"]
Configuration for generative-ui part rendering. `components` is the consumer-provided allowlist of React components the agent's JSON spec is permitted to render. Any name not present in the registry is rejected with a typed `GenerativeUIRenderError` — this is the security boundary in the same-realm rendering path.
MessagePrimitivePartsProps["components"]["generativeUI"]- componentsGenerativeUIComponentRegistry
The component allowlist (the security boundary).
- Fallback?ComponentType<{ component: string; props?: unknown }> | undefined
Optional fallback for unknown component names.
- Reasoning?ReasoningMessagePartComponent | undefined
Component for rendering reasoning content (typically hidden)
- tools?ToolsConfig | undefined
Configuration for tool call rendering
MessagePrimitivePartsProps["components"]["tools"]- by_name?Record<string, ToolCallMessagePartComponent | undefined> | undefined
Map of tool names to their specific components
- Fallback?ComponentType<ToolCallMessagePartProps> | undefined
Fallback component for unregistered tools
- OverrideComponentType<ToolCallMessagePartProps>
Override component that handles all tool calls
- ToolGroup?ComponentType< PropsWithChildren<{ startIndex: number; endIndex: number }> >deprecated
Component for rendering grouped consecutive tool calls.
Deprecated: Use `<MessagePrimitive.GroupedParts>` with a custom `groupBy` instead.
- ReasoningGroup?ReasoningGroupComponentdeprecated
Component for rendering grouped reasoning parts.
Deprecated: Use `<MessagePrimitive.GroupedParts>` with a custom `groupBy` instead.
- ChainOfThoughtComponentTypedeprecated
Deprecated: Use `<MessagePrimitive.GroupedParts>` with a `groupBy` that returns `["group-thought", ...]` for reasoning and tool-call parts. See `@assistant-ui/ui` for a worked example.
- unstable_showEmptyOnNonTextEndboolean | undefined= trueunstable
When enabled, shows the Empty component if the last part in the message is anything other than Text or Reasoning.
- children?(value: { part: EnrichedPartState }) => ReactNode
Render function called for each part. Receives the enriched part state.
PartByIndex
Renders a single message part at the specified index.- indexnumber
- components?MessagePrimitiveParts.Props["components"]
- MessagePrimitivePartByIndexProps["components"]
- Empty?EmptyMessagePartComponent | undefined
Component for rendering empty messages
- Text?TextMessagePartComponent | undefined
Component for rendering text content
- Source?SourceMessagePartComponent | undefined
Component for rendering source content
- Image?ImageMessagePartComponent | undefined
Component for rendering image content
- File?FileMessagePartComponent | undefined
Component for rendering file content
- Unstable_Audio?Unstable_AudioMessagePartComponent | undefineddeprecated
Component for rendering audio content.
Deprecated: Render audio through the `File` slot instead, branching on an `audio/*` mime type.
- data?DataConfig | undefined
Configuration for data part rendering
MessagePrimitivePartByIndexProps["components"]["data"]- by_name?Record<string, DataMessagePartComponent | undefined> | undefined
Map data event names to specific components
- Fallback?DataMessagePartComponent | undefined
Fallback component for unmatched data events
- Quote?QuoteMessagePartComponent | undefined
Component for rendering a quoted message reference (from metadata, not parts)
- generativeUI?MessagePrimitivePartByIndexProps["components"]["generativeUI"]
Configuration for generative-ui part rendering. `components` is the consumer-provided allowlist of React components the agent's JSON spec is permitted to render. Any name not present in the registry is rejected with a typed `GenerativeUIRenderError` — this is the security boundary in the same-realm rendering path.
MessagePrimitivePartByIndexProps["components"]["generativeUI"]- componentsGenerativeUIComponentRegistry
The component allowlist (the security boundary).
- Fallback?ComponentType<{ component: string; props?: unknown }> | undefined
Optional fallback for unknown component names.
- Reasoning?ReasoningMessagePartComponent | undefined
Component for rendering reasoning content (typically hidden)
- tools?ToolsConfig | undefined
Configuration for tool call rendering
MessagePrimitivePartByIndexProps["components"]["tools"]- by_name?Record<string, ToolCallMessagePartComponent | undefined> | undefined
Map of tool names to their specific components
- Fallback?ComponentType<ToolCallMessagePartProps> | undefined
Fallback component for unregistered tools
- OverrideComponentType<ToolCallMessagePartProps>
Override component that handles all tool calls
- ToolGroup?ComponentType< PropsWithChildren<{ startIndex: number; endIndex: number }> >deprecated
Component for rendering grouped consecutive tool calls.
Deprecated: Use `<MessagePrimitive.GroupedParts>` with a custom `groupBy` instead.
- ReasoningGroup?ReasoningGroupComponentdeprecated
Component for rendering grouped reasoning parts.
Deprecated: Use `<MessagePrimitive.GroupedParts>` with a custom `groupBy` instead.
- ChainOfThoughtComponentTypedeprecated
Deprecated: Use `<MessagePrimitive.GroupedParts>` with a `groupBy` that returns `["group-thought", ...]` for reasoning and tool-call parts. See `@assistant-ui/ui` for a worked example.
Content
- components?StandardComponents | ChainOfThoughtComponents | undefined
Component configuration for rendering different types of message content. Use either `Reasoning`/`tools`/`ToolGroup`/`ReasoningGroup` for standard rendering, or `ChainOfThought` to group all reasoning and tool-call parts into a single collapsible component. These two modes are mutually exclusive.
MessagePrimitiveContentProps["components"]- Empty?EmptyMessagePartComponent | undefined
Component for rendering empty messages
- Text?TextMessagePartComponent | undefined
Component for rendering text content
- Source?SourceMessagePartComponent | undefined
Component for rendering source content
- Image?ImageMessagePartComponent | undefined
Component for rendering image content
- File?FileMessagePartComponent | undefined
Component for rendering file content
- Unstable_Audio?Unstable_AudioMessagePartComponent | undefineddeprecated
Component for rendering audio content.
Deprecated: Render audio through the `File` slot instead, branching on an `audio/*` mime type.
- data?DataConfig | undefined
Configuration for data part rendering
MessagePrimitiveContentProps["components"]["data"]- by_name?Record<string, DataMessagePartComponent | undefined> | undefined
Map data event names to specific components
- Fallback?DataMessagePartComponent | undefined
Fallback component for unmatched data events
- Quote?QuoteMessagePartComponent | undefined
Component for rendering a quoted message reference (from metadata, not parts)
- generativeUI?MessagePrimitiveContentProps["components"]["generativeUI"]
Configuration for generative-ui part rendering. `components` is the consumer-provided allowlist of React components the agent's JSON spec is permitted to render. Any name not present in the registry is rejected with a typed `GenerativeUIRenderError` — this is the security boundary in the same-realm rendering path.
MessagePrimitiveContentProps["components"]["generativeUI"]- componentsGenerativeUIComponentRegistry
The component allowlist (the security boundary).
- Fallback?ComponentType<{ component: string; props?: unknown }> | undefined
Optional fallback for unknown component names.
- Reasoning?ReasoningMessagePartComponent | undefined
Component for rendering reasoning content (typically hidden)
- tools?ToolsConfig | undefined
Configuration for tool call rendering
MessagePrimitiveContentProps["components"]["tools"]- by_name?Record<string, ToolCallMessagePartComponent | undefined> | undefined
Map of tool names to their specific components
- Fallback?ComponentType<ToolCallMessagePartProps> | undefined
Fallback component for unregistered tools
- OverrideComponentType<ToolCallMessagePartProps>
Override component that handles all tool calls
- ToolGroup?ComponentType< PropsWithChildren<{ startIndex: number; endIndex: number }> >deprecated
Component for rendering grouped consecutive tool calls.
Deprecated: Use `<MessagePrimitive.GroupedParts>` with a custom `groupBy` instead.
- ReasoningGroup?ReasoningGroupComponentdeprecated
Component for rendering grouped reasoning parts.
Deprecated: Use `<MessagePrimitive.GroupedParts>` with a custom `groupBy` instead.
- ChainOfThoughtComponentTypedeprecated
Deprecated: Use `<MessagePrimitive.GroupedParts>` with a `groupBy` that returns `["group-thought", ...]` for reasoning and tool-call parts. See `@assistant-ui/ui` for a worked example.
- unstable_showEmptyOnNonTextEndboolean | undefined= trueunstable
When enabled, shows the Empty component if the last part in the message is anything other than Text or Reasoning.
- children?(value: { part: EnrichedPartState }) => ReactNode
Render function called for each part. Receives the enriched part state.
If
Warning
- system?boolean | undefined
- user?boolean | undefined
- assistant?boolean | undefined
- speaking?boolean | undefined
- hasBranches?boolean | undefined
- copied?boolean | undefined
- lastOrHover?boolean | undefined
- last?boolean | undefined
- hasAttachments?boolean | undefined
- hasContent?boolean | undefined
- submittedFeedback?"positive" | "negative" | null | undefined
Attachments
- components?MessageAttachmentsComponentConfigdeprecated
Deprecated: Use the children render function instead.
MessageAttachmentsComponentConfig- Image?ComponentType | undefined
- Document?ComponentType | undefined
- File?ComponentType | undefined
- Attachment?ComponentType | undefined
- children?(value: { attachment: CompleteAttachment }) => ReactNode
Render function called for each attachment. Receives the attachment.
AttachmentByIndex
Renders a single attachment at the specified index within the current message.- indexnumber
- components?MessageAttachmentsComponentConfig
- MessageAttachmentsComponentConfig
- Image?ComponentType | undefined
- Document?ComponentType | undefined
- File?ComponentType | undefined
- Attachment?ComponentType | undefined
Quote
- children(value: QuoteInfo) => ReactNode
Render function called when a quote is present. Receives quote info.
Error
GroupedParts
Groups adjacent message parts into a tree of coalesced runs and renders each node — group or part — through a single `children` function. The render function receives `{ part, children }` where `part.type` is either a `"group-…"` literal (for a group, `children` is the recursively-rendered subtree) or a real part type (`"text"`, `"tool-call"`, …) for a leaf (`children` is a sentinel that throws if rendered — use `part.type` to distinguish).<MessagePrimitive.GroupedParts
groupBy={groupPartByType({
reasoning: ["group-thought", "group-reasoning"],
"tool-call": ["group-thought", "group-tool"],
})}
>
{({ part, children }) => {
switch (part.type) {
case "group-thought": return <Thought>{children}</Thought>;
case "group-reasoning": return <Reasoning>{children}</Reasoning>;
case "group-tool": return <ToolStack>{children}</ToolStack>;
case "text": return <MarkdownText />;
case "tool-call": return part.toolUI ?? <ToolFallback {...part} />;
case "indicator": return <LoadingDots />;
default: return null;
}
}}
</MessagePrimitive.GroupedParts>- groupBy( part: PartState, context: GroupByContext, ) => readonly TKey[] | null
Maps each part to a group-key path. Adjacent parts that share a prefix coalesce into the same group. Return `[]` (or `null`) to leave a part ungrouped. Group keys must start with `"group-"` so the renderer's `switch (part.type)` can tell groups apart from real part types. **Prefer groupPartByType** for the common case of mapping by `part.type` — it ships a stable memo fingerprint so the tree survives unrelated re-renders. Use an inline function only when the helper isn't expressive enough (e.g. branching on `part.toolName` or part metadata). The second argument is a GroupByContext carrying the tool-UI registry, for grouping that depends on it (e.g. standalone tool calls).
- indicatorIndicatorMode= "no-text"
Controls emission of the synthetic IndicatorPart — a trailing `{ part: { type: "indicator", status } }` render call you handle with `case "indicator"` to show loading/status UI.
- children(info: RenderInfo<TKey>) => ReactNode
Render function called once per group node, once per leaf part, and (when the `indicator` condition is met) once for the trailing IndicatorPart. Switch on `part.type`: `"group-…"` cases wrap `children`; real part types (`"text"`, `"tool-call"`, …) render the part directly; `"indicator"` renders status/loading UI. Leaf parts receive the same EnrichedPartState that `<MessagePrimitive.Parts>` would produce (`toolUI`, `addResult`, `resume`, `respondToApproval`, `dataRendererUI`).
GenerativeUI
Renders a generative-ui message part using a consumer-provided allowlist. The agent emits a `generative-ui` message part containing a JSON spec (see [GenerativeUISpec](/docs/api-reference/generative-ui/spec#generativeuispec)). This primitive walks the spec and resolves each `component` name against the allowlist. Names not in the allowlist throw [GenerativeUIRenderError](/docs/api-reference/generative-ui/rendering#generativeuirendererror) unless a `Fallback` is provided. Stream-friendly: a partial spec renders progressively as it is filled in.<MessagePrimitive.GenerativeUI
components={{ Card: MyCard, Button: MyButton }}
/>- componentsGenerativeUIComponentRegistry
The component allowlist. Keys are the names referenced in the spec (e.g. `"Card"`, `"Button"`), values are the React components. This is the security boundary — any name not in the allowlist is rejected with GenerativeUIRenderError.
- spec?GenerativeUISpec | undefined
Optional override spec. If omitted, the primitive reads the `generative-ui` part from the surrounding `MessagePartProvider` / `PartByIndexProvider` context.
MessagePrimitiveGenerativeUIProps["spec"]- rootGenerativeUINode | readonly GenerativeUINode[]
Root node(s) to render.
- Fallback?ComponentType<{ component: string; props?: unknown }> | undefined
Optional fallback for unknown component names.
Unstable_PartsGrouped
Warning
// Group by parent ID (default behavior)
<MessagePrimitive.Unstable_PartsGrouped
components={{
Text: ({ text }) => <p className="message-text">{text}</p>,
Image: ({ image }) => <img src={image} alt="Message image" />,
Group: ({ groupKey, indices, children }) => {
if (!groupKey) return <>{children}</>;
return (
<div className="parent-group border rounded p-4">
<h4>Parent ID: {groupKey}</h4>
{children}
</div>
);
}
}}
/>- groupingFunctionGroupingFunction
Function that takes an array of message parts and returns an array of groups. Each group contains a key (for identification) and an array of indices.
- components?MessagePrimitiveUnstable_PartsGroupedProps["components"]
Component configuration for rendering different types of message content. You can provide custom components for each content type (text, image, file, etc.) and configure tool rendering behavior. If not provided, default components will be used.
MessagePrimitiveUnstable_PartsGroupedProps["components"]- Empty?EmptyMessagePartComponent | undefined
Component for rendering empty messages
- Text?TextMessagePartComponent | undefined
Component for rendering text content
- Reasoning?ReasoningMessagePartComponent | undefined
Component for rendering reasoning content (typically hidden)
- Source?SourceMessagePartComponent | undefined
Component for rendering source content
- Image?ImageMessagePartComponent | undefined
Component for rendering image content
- File?FileMessagePartComponent | undefined
Component for rendering file content
- Unstable_Audio?Unstable_AudioMessagePartComponent | undefineddeprecated
Component for rendering audio content.
Deprecated: Render audio through the `File` slot instead, branching on an `audio/*` mime type.
- data?MessagePrimitiveUnstable_PartsGroupedProps["components"]["data"]
Configuration for data part rendering
MessagePrimitiveUnstable_PartsGroupedProps["components"]["data"]- by_name?Record<string, DataMessagePartComponent | undefined> | undefined
Map data event names to specific components
- Fallback?DataMessagePartComponent | undefined
Fallback component for unmatched data events
- tools?MessagePrimitiveUnstable_PartsGroupedProps["components"]["tools"]
Configuration for tool call rendering
MessagePrimitiveUnstable_PartsGroupedProps["components"]["tools"]- by_name?Record<string, ToolCallMessagePartComponent | undefined> | undefined
Map of tool names to their specific components
- Fallback?ComponentType<ToolCallMessagePartProps> | undefined
Fallback component for unregistered tools
- OverrideComponentType<ToolCallMessagePartProps>
Override component that handles all tool calls
- Group?ComponentType< PropsWithChildren<{ groupKey: string | undefined; indices: number[]; }> >
Component for rendering grouped message parts. When provided, this component will automatically wrap message parts that share the same group key as determined by the groupingFunction. The component receives: - `groupKey`: The group key (or undefined for ungrouped parts) - `indices`: Array of indices for the parts in this group - `children`: The rendered message part components
Unstable_PartsGroupedByParentId
Warning
- components?MessagePrimitiveUnstable_PartsGroupedByParentIdProps props["components"]
Component configuration for rendering different types of message content. You can provide custom components for each content type (text, image, file, etc.) and configure tool rendering behavior. If not provided, default components will be used.
MessagePrimitiveUnstable_PartsGroupedByParentIdProps props["components"]- Empty?EmptyMessagePartComponent | undefined
Component for rendering empty messages
- Text?TextMessagePartComponent | undefined
Component for rendering text content
- Reasoning?ReasoningMessagePartComponent | undefined
Component for rendering reasoning content (typically hidden)
- Source?SourceMessagePartComponent | undefined
Component for rendering source content
- Image?ImageMessagePartComponent | undefined
Component for rendering image content
- File?FileMessagePartComponent | undefined
Component for rendering file content
- Unstable_Audio?Unstable_AudioMessagePartComponent | undefineddeprecated
Component for rendering audio content.
Deprecated: Render audio through the `File` slot instead, branching on an `audio/*` mime type.
- data?MessagePrimitiveUnstable_PartsGroupedByParentIdProps props["components"]["data"]
Configuration for data part rendering
MessagePrimitiveUnstable_PartsGroupedByParentIdProps props["components"]["data"]- by_name?Record<string, DataMessagePartComponent | undefined> | undefined
Map data event names to specific components
- Fallback?DataMessagePartComponent | undefined
Fallback component for unmatched data events
- tools?MessagePrimitiveUnstable_PartsGroupedByParentIdProps props["components"]["tools"]
Configuration for tool call rendering
MessagePrimitiveUnstable_PartsGroupedByParentIdProps props["components"]["tools"]- by_name?Record<string, ToolCallMessagePartComponent | undefined> | undefined
Map of tool names to their specific components
- Fallback?ComponentType<ToolCallMessagePartProps> | undefined
Fallback component for unregistered tools
- OverrideComponentType<ToolCallMessagePartProps>
Override component that handles all tool calls
- Group?ComponentType< PropsWithChildren<{ groupKey: string | undefined; indices: number[]; }> >
Component for rendering grouped message parts. When provided, this component will automatically wrap message parts that share the same group key as determined by the groupingFunction. The component receives: - `groupKey`: The group key (or undefined for ungrouped parts) - `indices`: Array of indices for the parts in this group - `children`: The rendered message part components
groupPartByType
Build a groupBy from a part.type → group-key path lookup.
Parts whose type isn't in the map are left ungrouped. The returned
function carries a stable GROUPBY_MEMO_KEY fingerprint so
<MessagePrimitive.GroupedParts> can memoize its tree across renders.
The synthetic "standalone-tool-call" key matches tool calls that should
render outside the chain-of-thought grouping. MCP-app calls are detected from
the part alone; the registry-driven cases (human tools, the generative-UI
tool, display: "standalone" opt-ins) are resolved from the
GroupByContext that <MessagePrimitive.GroupedParts> passes to the
groupBy function — the helper needs nothing threaded into it.
<MessagePrimitive.GroupedParts
groupBy={groupPartByType({
reasoning: ["group-thought", "group-reasoning"],
"tool-call": ["group-thought", "group-tool"],
"standalone-tool-call": [],
})}
>
{({ part, children }) => { ... }}
</MessagePrimitive.GroupedParts>const groupPartByType: <TKey extends `group-${string}`>(map: Partial<Readonly<Record<GroupPartType, readonly TKey[]>>>) => ((part: PartState, context?: GroupByContext) => readonly TKey[]);