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.
MessagePrimitiveRootPropsasChild: boolean= falseChange 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
Parts
Renders the parts of a message with web-specific default components.MessagePrimitivePartsPropscomponents?: StandardComponents | ChainOfThoughtComponentsComponent 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.
Empty?: EmptyMessagePartComponentComponent for rendering empty messages
Text?: TextMessagePartComponentComponent for rendering text content
Source?: SourceMessagePartComponentComponent for rendering source content
Image?: ImageMessagePartComponentComponent for rendering image content
File?: FileMessagePartComponentComponent for rendering file content
Unstable_Audio?: Unstable_AudioMessagePartComponentComponent for rendering audio content (experimental)
data?: DataConfigConfiguration for data part rendering
by_name?: Record<string, DataMessagePartComponent | undefined>Map data event names to specific components
Fallback?: DataMessagePartComponentFallback component for unmatched data events
Quote?: QuoteMessagePartComponentComponent 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.
components: GenerativeUIComponentRegistryThe component allowlist (the security boundary).
Fallback?: ComponentType<{ component: string; props?: unknown }>Optional fallback for unknown component names.
Reasoning?: ReasoningMessagePartComponentComponent for rendering reasoning content (typically hidden)
tools?: ToolsConfigConfiguration for tool call rendering
by_name?: Record<string, ToolCallMessagePartComponent | undefined>Map of tool names to their specific components
Fallback?: ComponentType<ToolCallMessagePartProps>Fallback component for unregistered tools
Override: ComponentType<ToolCallMessagePartProps>Override component that handles all tool calls
ToolGroupdeprecated?: ComponentType< PropsWithChildren<{ startIndex: number; endIndex: number }> >Component for rendering grouped consecutive tool calls.
Deprecated: Use `<MessagePrimitive.GroupedParts>` with a custom `groupBy` instead.
ReasoningGroupdeprecated?: ReasoningGroupComponentComponent for rendering grouped reasoning parts.
Deprecated: Use `<MessagePrimitive.GroupedParts>` with a custom `groupBy` instead.
ChainOfThoughtdeprecated: ComponentTypeDeprecated: 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_showEmptyOnNonTextEndunstable: boolean= trueWhen enabled, shows the Empty component if the last part in the message is anything other than Text or Reasoning.
children?: (value: { part: EnrichedPartState }) => ReactNodeRender function called for each part. Receives the enriched part state.
PartByIndex
Renders a single message part at the specified index.MessagePrimitivePartByIndexPropsindex: numbercomponents?: MessagePrimitiveParts.Props["components"]Empty?: EmptyMessagePartComponentComponent for rendering empty messages
Text?: TextMessagePartComponentComponent for rendering text content
Source?: SourceMessagePartComponentComponent for rendering source content
Image?: ImageMessagePartComponentComponent for rendering image content
File?: FileMessagePartComponentComponent for rendering file content
Unstable_Audio?: Unstable_AudioMessagePartComponentComponent for rendering audio content (experimental)
data?: DataConfigConfiguration for data part rendering
by_name?: Record<string, DataMessagePartComponent | undefined>Map data event names to specific components
Fallback?: DataMessagePartComponentFallback component for unmatched data events
Quote?: QuoteMessagePartComponentComponent 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.
components: GenerativeUIComponentRegistryThe component allowlist (the security boundary).
Fallback?: ComponentType<{ component: string; props?: unknown }>Optional fallback for unknown component names.
Reasoning?: ReasoningMessagePartComponentComponent for rendering reasoning content (typically hidden)
tools?: ToolsConfigConfiguration for tool call rendering
by_name?: Record<string, ToolCallMessagePartComponent | undefined>Map of tool names to their specific components
Fallback?: ComponentType<ToolCallMessagePartProps>Fallback component for unregistered tools
Override: ComponentType<ToolCallMessagePartProps>Override component that handles all tool calls
ToolGroupdeprecated?: ComponentType< PropsWithChildren<{ startIndex: number; endIndex: number }> >Component for rendering grouped consecutive tool calls.
Deprecated: Use `<MessagePrimitive.GroupedParts>` with a custom `groupBy` instead.
ReasoningGroupdeprecated?: ReasoningGroupComponentComponent for rendering grouped reasoning parts.
Deprecated: Use `<MessagePrimitive.GroupedParts>` with a custom `groupBy` instead.
ChainOfThoughtdeprecated: ComponentTypeDeprecated: 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
MessagePrimitiveContentPropscomponents?: StandardComponents | ChainOfThoughtComponentsComponent 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.
Empty?: EmptyMessagePartComponentComponent for rendering empty messages
Text?: TextMessagePartComponentComponent for rendering text content
Source?: SourceMessagePartComponentComponent for rendering source content
Image?: ImageMessagePartComponentComponent for rendering image content
File?: FileMessagePartComponentComponent for rendering file content
Unstable_Audio?: Unstable_AudioMessagePartComponentComponent for rendering audio content (experimental)
data?: DataConfigConfiguration for data part rendering
by_name?: Record<string, DataMessagePartComponent | undefined>Map data event names to specific components
Fallback?: DataMessagePartComponentFallback component for unmatched data events
Quote?: QuoteMessagePartComponentComponent 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.
components: GenerativeUIComponentRegistryThe component allowlist (the security boundary).
Fallback?: ComponentType<{ component: string; props?: unknown }>Optional fallback for unknown component names.
Reasoning?: ReasoningMessagePartComponentComponent for rendering reasoning content (typically hidden)
tools?: ToolsConfigConfiguration for tool call rendering
by_name?: Record<string, ToolCallMessagePartComponent | undefined>Map of tool names to their specific components
Fallback?: ComponentType<ToolCallMessagePartProps>Fallback component for unregistered tools
Override: ComponentType<ToolCallMessagePartProps>Override component that handles all tool calls
ToolGroupdeprecated?: ComponentType< PropsWithChildren<{ startIndex: number; endIndex: number }> >Component for rendering grouped consecutive tool calls.
Deprecated: Use `<MessagePrimitive.GroupedParts>` with a custom `groupBy` instead.
ReasoningGroupdeprecated?: ReasoningGroupComponentComponent for rendering grouped reasoning parts.
Deprecated: Use `<MessagePrimitive.GroupedParts>` with a custom `groupBy` instead.
ChainOfThoughtdeprecated: ComponentTypeDeprecated: 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_showEmptyOnNonTextEndunstable: boolean= trueWhen enabled, shows the Empty component if the last part in the message is anything other than Text or Reasoning.
children?: (value: { part: EnrichedPartState }) => ReactNodeRender function called for each part. Receives the enriched part state.
If
MessagePrimitiveIfPropssystem?: booleanuser?: booleanassistant?: booleanspeaking?: booleanhasBranches?: booleancopied?: booleanlastOrHover?: booleanlast?: booleanhasAttachments?: booleanhasContent?: booleansubmittedFeedback?: "positive" | "negative" | null
Attachments
MessagePrimitiveAttachmentsPropscomponentsdeprecated?: MessageAttachmentsComponentConfigDeprecated: Use the children render function instead.
Image?: ComponentTypeDocument?: ComponentTypeFile?: ComponentTypeAttachment?: ComponentType
children?: (value: { attachment: CompleteAttachment }) => ReactNodeRender function called for each attachment. Receives the attachment.
AttachmentByIndex
Renders a single attachment at the specified index within the current message.MessagePrimitiveAttachmentByIndexPropsindex: numbercomponents?: MessageAttachmentsComponentConfigImage?: ComponentTypeDocument?: ComponentTypeFile?: ComponentTypeAttachment?: ComponentType
Quote
MessagePrimitiveQuotePropschildren: (value: QuoteInfo) => ReactNodeRender 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).MessagePrimitiveGroupedPartsPropsgroupBy: ( part: PartState, context: GroupByContext, ) => readonly TKey[] | nullMaps 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).
indicator: IndicatorMode= "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>) => ReactNodeRender 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.MessagePrimitiveGenerativeUIPropscomponents: GenerativeUIComponentRegistryThe 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?: GenerativeUISpecOptional override spec. If omitted, the primitive reads the `generative-ui` part from the surrounding `MessagePartProvider` / `PartByIndexProvider` context.
root: GenerativeUINode | readonly GenerativeUINode[]Root node(s) to render.
Fallback?: ComponentType<{ component: string; props?: unknown }>Optional fallback for unknown component names.
Unstable_PartsGrouped
MessagePrimitiveUnstable_PartsGroupedPropsgroupingFunction: GroupingFunctionFunction 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.
Empty?: EmptyMessagePartComponentComponent for rendering empty messages
Text?: TextMessagePartComponentComponent for rendering text content
Reasoning?: ReasoningMessagePartComponentComponent for rendering reasoning content (typically hidden)
Source?: SourceMessagePartComponentComponent for rendering source content
Image?: ImageMessagePartComponentComponent for rendering image content
File?: FileMessagePartComponentComponent for rendering file content
Unstable_Audio?: Unstable_AudioMessagePartComponentComponent for rendering audio content (experimental)
data?: MessagePrimitiveUnstable_PartsGroupedProps["components"]["data"]Configuration for data part rendering
by_name?: Record<string, DataMessagePartComponent | undefined>Map data event names to specific components
Fallback?: DataMessagePartComponentFallback component for unmatched data events
tools?: MessagePrimitiveUnstable_PartsGroupedProps["components"]["tools"]Configuration for tool call rendering
by_name?: Record<string, ToolCallMessagePartComponent | undefined>Map of tool names to their specific components
Fallback?: ComponentType<ToolCallMessagePartProps>Fallback component for unregistered tools
Override: ComponentType<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
MessagePrimitiveUnstable_PartsGroupedByParentIdPropscomponents?: 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.
Empty?: EmptyMessagePartComponentComponent for rendering empty messages
Text?: TextMessagePartComponentComponent for rendering text content
Reasoning?: ReasoningMessagePartComponentComponent for rendering reasoning content (typically hidden)
Source?: SourceMessagePartComponentComponent for rendering source content
Image?: ImageMessagePartComponentComponent for rendering image content
File?: FileMessagePartComponentComponent for rendering file content
Unstable_Audio?: Unstable_AudioMessagePartComponentComponent for rendering audio content (experimental)
data?: MessagePrimitiveUnstable_PartsGroupedByParentIdProps props["components"]["data"]Configuration for data part rendering
by_name?: Record<string, DataMessagePartComponent | undefined>Map data event names to specific components
Fallback?: DataMessagePartComponentFallback component for unmatched data events
tools?: MessagePrimitiveUnstable_PartsGroupedByParentIdProps props["components"]["tools"]Configuration for tool call rendering
by_name?: Record<string, ToolCallMessagePartComponent | undefined>Map of tool names to their specific components
Fallback?: ComponentType<ToolCallMessagePartProps>Fallback component for unregistered tools
Override: ComponentType<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[]);