Skip to content
+

Chat - Headless messages

Compose thread rows from message grouping primitives, message subparts, and default message-part renderers.

Grouped timeline
Author and time-window grouping
Alice Chen
Alice Chen
I reviewed the incident summary and grouped the first remediation steps.
Sent
The access token mismatch explains the initial deployment confusion.
Sent
Alice Chen
Alice Chen
Nine minutes later, the customer confirmed the reset worked.
Sent
You
You
Let us fold that into the public timeline so support can reuse the steps.
Sent
Also add a note about why we keep message grouping separate from the data model.
Sent
MUI Guide
MUI Guide
Done. The grouping window is now a local presentation choice inside the recipe.

Primitive set

The message surface is built from:

  • MessageGroup
  • Message.Root
  • Message.Avatar
  • Message.Content
  • Message.Meta
  • Message.Actions

MessageGroup

MessageGroup is the default row-level composition helper for threaded chats. It derives the previous and next message, then decides whether the current message starts or ends a visual group.

<MessageGroup groupingWindowMs={300_000} index={index} messageId={id} />

Grouping is based on:

  • author identity
  • author role fallback when no explicit author id exists
  • an adjustable grouping window in milliseconds

This gives you isFirst and isLast grouping state without manual row bookkeeping.

That is especially useful when the thread mixes authored messages, assistant responses, and sparse metadata. The grouping rules stay close to the row composition instead of leaking into application code.

Message.Root

Message.Root resolves a message by id and exposes owner state such as:

  • role
  • status
  • streaming
  • error
  • isGrouped

That owner state powers slot-based styling for user versus assistant messages, streaming states, and grouped rows.

Message.Root is the main bridge between state-driven message identity and the leaf subparts that render avatars, message bodies, metadata, and actions.

Default row stack

The most common message composition inside a group is:

  • Message.Avatar
  • Message.Content
  • Message.Meta
  • optional Message.Actions

MessageGroup can render this default stack for you, or you can provide custom children for a different row layout.

<MessageGroup index={index} messageId={id}>
  <Message.Root messageId={id}>
    <Message.Avatar />
    <Message.Content />
    <Message.Meta />
    <Message.Actions />
  </Message.Root>
</MessageGroup>

This pattern keeps grouping logic and row layout close together while still allowing subpart-level slot replacement.

Default message-part renderers

The headless package also exports helpers for common message parts:

  • getDefaultMessagePartRenderer()
  • renderDefaultTextPart()
  • renderDefaultReasoningPart()
  • renderDefaultToolPart()
  • renderDefaultDynamicToolPart()
  • renderDefaultFilePart()
  • renderDefaultSourceUrlPart()
  • renderDefaultSourceDocumentPart()
  • renderDefaultStepStartPart()
  • renderDefaultDataPart()

Use these helpers when you want to keep the default rendering for most part types and selectively replace only one or two.

function renderPart(part: ChatMessagePart, message: ChatMessage, index: number) {
  if (part.type === 'reasoning') {
    return <CustomReasoningPanel part={part} />;
  }

  const renderer = getDefaultMessagePartRenderer(part);

  return renderer ? renderer({ part, message, index }) : null;
}

The helpers cover text, reasoning, tool parts, files, sources, step boundaries, and data-* parts. That makes them a good fit for progressive customization instead of full renderer rewrites.

When to replace message rendering

Use the default helpers when:

  • the structural message model is correct
  • only certain part types need a custom presentation

Rebuild more of the message surface when:

  • the row layout is completely different
  • message actions need a different placement model
  • grouped and ungrouped messages need distinct markup

See also

  • Continue with Message list for ordering, date boundaries, and thread scrolling behavior.
  • Continue with Customization for slot replacement patterns on message rows and subparts.
  • Continue with Custom message part rendering for the demo version of selective renderer replacement.

API

API

See the documentation below for a complete reference to all of the props and classes available to the components mentioned here.