Radix UI Primitives

MessagePrimitive

Components for rendering message content, parts, and attachments.

A single message in a conversation. Messages may consist of multiple parts.

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

Contains all parts of the message.

This primitive renders a <div> element unless asChild is set.

MessagePrimitiveRootProps
asChild: boolean= 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.

Parts

The content of the message. This renders a separate component for each message part.

MessagePrimitivePartsProps
components?: MessagePartComponents

The components to render for each message part.

MessagePartComponents
Text?: TextMessagePartComponent

The component to render for each text message part.

Image?: ImageMessagePartComponent

The component to render for each image message part.

Source?: SourceMessagePartComponent

The component to render for each source message part.

File?: FileMessagePartComponent

The component to render for each file message part.

Unstable_Audio?: Unstable_AudioMessagePartComponent

The component to render for each audio message part.

Reasoning?: ReasoningMessagePartComponent

The component to render for each reasoning message part. Cannot be used alongside ChainOfThought.

tools?: object

Configuration for tool call rendering. Cannot be used alongside ChainOfThought.

by_name?: Record<string, ToolCallMessagePartComponent>

Map of tool names to their specific components.

Fallback?: ToolCallMessagePartComponent

Fallback component for tool calls not matched by by_name.

Override?: ComponentType<ToolCallMessagePartProps>

Override component that handles all tool calls. When set, by_name and Fallback are ignored.

data?: object

Configuration for data part rendering.

by_name?: Record<string, DataMessagePartComponent>

Map of data event names to their specific components.

Fallback?: DataMessagePartComponent

Fallback component for data events not matched by by_name.

ToolGroup?: ComponentType<PropsWithChildren<{ startIndex: number; endIndex: number }>>

Deprecated: This feature is still experimental and subject to change. Component for rendering grouped consecutive tool calls. When provided, consecutive tool-call message parts will be automatically grouped and wrapped with this component. Cannot be used alongside ChainOfThought.

ToolGroupProps
startIndex: number

Index of the first tool call in the group.

endIndex: number

Index of the last tool call in the group.

children: ReactNode

The rendered tool call components within the group.

ReasoningGroup?: ReasoningGroupComponent

Component for rendering grouped consecutive reasoning parts. Cannot be used alongside ChainOfThought.

ReasoningGroupProps
startIndex: number

Index of the first reasoning part in the group.

endIndex: number

Index of the last reasoning part in the group.

children: ReactNode

The rendered reasoning part components within the group.

ChainOfThought?: ComponentType

When set, groups all consecutive reasoning and tool-call parts into a single collapsible component. Mutually exclusive with Reasoning, tools, ToolGroup, and ReasoningGroup.

Empty?: EmptyMessagePartComponent

Component to render when the message has no parts, or when unstable_showEmptyOnNonTextEnd is enabled and the last part is not text or reasoning.

unstable_showEmptyOnNonTextEndunstable?: boolean

When enabled, shows the Empty component if the last part in the message is anything other than Text or Reasoning. Defaults to true.

Quote

Renders a quote block if the message has quote metadata (message.metadata.custom.quote). Place this above MessagePrimitive.Parts.

<MessagePrimitive.Quote>
  {({ text, messageId }) => <QuoteBlock text={text} messageId={messageId} />}
</MessagePrimitive.Quote>
MessagePrimitiveQuoteProps
children: (quote: QuoteInfo) => ReactNode

Render function called when a quote is present. Receives { text, messageId }.

PartByIndex

Renders a single message part at the specified index.

<MessagePrimitive.PartByIndex
  index={0}
  components={{
    Text: MyTextComponent,
    Image: MyImageComponent
  }}
/>
MessagePrimitive.PartByIndex.Props
index: number

The index of the message part to render.

components?: MessagePartComponents

The components to render for the message part.

Attachments

Renders all attachments of the message.

MessagePrimitive.Attachments.Props
components?: AttachmentComponents

The components to render for each attachment.

AttachmentComponents
Image?: ComponentType

The component to render for image attachments.

Document?: ComponentType

The component to render for document attachments.

File?: ComponentType

The component to render for file attachments.

Attachment?: ComponentType

The fallback component to render for any attachment type.

AttachmentByIndex

Renders a single attachment at the specified index within the message.

<MessagePrimitive.AttachmentByIndex
  index={0}
  components={{
    Image: MyImageAttachment,
    Document: MyDocumentAttachment
  }}
/>
MessagePrimitive.AttachmentByIndex.Props
index: number

The index of the attachment to render.

components?: AttachmentComponents

The components to render for the attachment.

useMessageQuote()

A hook that returns the quote info attached to the current message, if any. Reads from message.metadata.custom.quote.

import { useMessageQuote } from "@assistant-ui/react";

const QuoteBlock = () => {
  const quote = useMessageQuote();
  if (!quote) return null;

  return (
    <div className="mb-2 flex items-start gap-1.5 text-sm italic text-muted-foreground">
      {quote.text}
    </div>
  );
};

Returns: QuoteInfo | undefined

type QuoteInfo = {
  readonly text: string;       // the quoted plain text
  readonly messageId: string;  // the source message ID
};

See the Quoting guide for a complete walkthrough.

Unstable_PartsGrouped

Renders the parts of a message grouped by a custom grouping function. Use this component when you need to visually group related message parts together — for example, grouping parts that share a common parent ID, or collecting consecutive tool calls under a single header.

The groupingFunction prop controls how parts are grouped. It receives the full array of message parts and must return an array of group descriptors, each with a groupKey (or undefined for ungrouped parts) and an array of indices into the message parts array. The optional components.Group component is then rendered once per group and receives the groupKey, indices, and the rendered part children.

This API is unstable and may change in a future release.

<MessagePrimitive.Unstable_PartsGrouped
  groupingFunction={(parts) => {
    const groups = new Map<string, number[]>();
    parts.forEach((part, i) => {
      const key = part.parentId ?? `__ungrouped_${i}`;
      const indices = groups.get(key) ?? [];
      indices.push(i);
      groups.set(key, indices);
    });
    return Array.from(groups.entries()).map(([key, indices]) => ({
      groupKey: key.startsWith("__ungrouped_") ? undefined : key,
      indices,
    }));
  }}
  components={{
    Text: ({ text }) => <p>{text}</p>,
    Group: ({ groupKey, indices, children }) => {
      if (!groupKey) return <>{children}</>;
      return (
        <div className="parent-group">
          <h4>Parent: {groupKey}</h4>
          {children}
        </div>
      );
    },
  }}
/>
MessagePrimitive.Unstable_PartsGrouped.Props
groupingFunction: GroupingFunction

A function that receives the array of message parts and returns an array of groups. Each group has a groupKey (string or undefined for ungrouped parts) and an indices array of part positions.

components?: object

The components to render for each message part type and for group wrappers.

Text?: TextMessagePartComponent

The component to render for each text message part.

Reasoning?: ReasoningMessagePartComponent

The component to render for each reasoning message part.

Source?: SourceMessagePartComponent

The component to render for each source message part.

Image?: ImageMessagePartComponent

The component to render for each image message part.

File?: FileMessagePartComponent

The component to render for each file message part.

Unstable_Audio?: Unstable_AudioMessagePartComponent

The component to render for each audio message part.

Empty?: EmptyMessagePartComponent

Component to render when the message has no parts.

tools?: object

Configuration for tool call rendering.

by_name?: Record<string, ToolCallMessagePartComponent>

Map of tool names to their specific components.

Fallback?: ToolCallMessagePartComponent

Fallback component for tool calls not matched by by_name.

Override?: ComponentType<ToolCallMessagePartProps>

Override component that handles all tool calls. When set, by_name and Fallback are ignored.

data?: object

Configuration for data part rendering.

by_name?: Record<string, DataMessagePartComponent>

Map of data event names to their specific components.

Fallback?: DataMessagePartComponent

Fallback component for data events not matched by by_name.

Group?: ComponentType<PropsWithChildren<{ groupKey: string | undefined; indices: number[] }>>

Component for rendering a group of message parts. Receives the groupKey (undefined for ungrouped parts), the indices of all parts in the group, and the rendered part children.

GroupProps
groupKey: string | undefined

The group key, or undefined for ungrouped parts.

indices: number[]

Array of indices of the message parts belonging to this group.

children: ReactNode

The rendered message part components within the group.

Unstable_PartsGroupedByParentId

A convenience wrapper around MessagePrimitive.Unstable_PartsGrouped that groups message parts by their parentId field. Parts without a parentId appear individually at their chronological position; parts sharing the same parentId are collected into a single group at the position of their first occurrence.

This API is unstable and may change in a future release.

<MessagePrimitive.Unstable_PartsGroupedByParentId
  components={{
    Group: ({ groupKey, children }) => {
      if (!groupKey) return <>{children}</>;
      return (
        <div className="parent-group border rounded p-4">
          <h4>Parent ID: {groupKey}</h4>
          {children}
        </div>
      );
    },
  }}
/>

Accepts the same components prop as MessagePrimitive.Unstable_PartsGrouped, but without a groupingFunction prop (the parent-ID grouping function is applied automatically).

Error

Renders children only if the message has an error status.

<MessagePrimitive.Error>
  {/* rendered if the message has an error status */}
  <ErrorPrimitive.Root>
    <ErrorPrimitive.Message />
  </ErrorPrimitive.Root>
</MessagePrimitive.Error>