MessagePrimitive

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>
MessagePrimitiveRootProps
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.
MessagePrimitivePartsProps
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.
MessagePrimitivePartByIndexProps
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

MessagePrimitiveContentProps
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

Deprecated. Use `<AuiIf condition={(s) => s.message...} />` instead.
MessagePrimitiveIfProps
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

MessagePrimitiveAttachmentsProps
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.
MessagePrimitiveAttachmentByIndexProps
indexnumber

components?MessageAttachmentsComponentConfig

MessageAttachmentsComponentConfig
Image?ComponentType | undefined

Document?ComponentType | undefined

File?ComponentType | undefined

Attachment?ComponentType | undefined

Quote

MessagePrimitiveQuoteProps
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>
MessagePrimitiveGroupedPartsProps
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 }}
/>
MessagePrimitiveGenerativeUIProps
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

Deprecated. Prefer `<MessagePrimitive.GroupedParts>` for adjacent grouping — it dispatches all rendering through one `switch (part.type)` and supports nested group paths. Keep this primitive only for non-adjacent clustering (e.g., gathering parts with the same parent-id across the message).
Renders the parts of a message grouped by a custom grouping function. This component allows you to group message parts based on any criteria you define. The grouping function receives all message parts and returns an array of groups, where each group has a key and an array of part indices.
// 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>
      );
    }
  }}
/>
MessagePrimitiveUnstable_PartsGroupedProps
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

Deprecated. Use MessagePrimitive.Unstable_PartsGrouped instead for more flexibility
Renders the parts of a message grouped by their parent ID. This is a convenience wrapper around Unstable_PartsGrouped with parent ID grouping.
MessagePrimitiveUnstable_PartsGroupedByParentIdProps
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[]);