Skip to content

Chat Message

A single chat message: sender-aware wrapper providing alignment, avatar, name, and metadata around the message content.

Hi! How can I help you today?
<ChatMessage {...args}>
<ChatMessageBubble>Hi! How can I help you today?</ChatMessageBubble>
</ChatMessage>
I'm an assistant message.
I'm a user message.
Conversation started
<div className={columnStyle}>
<ChatMessage sender="assistant">
<ChatMessageBubble>I'm an assistant message.</ChatMessageBubble>
</ChatMessage>
<ChatMessage sender="user">
<ChatMessageBubble>I'm a user message.</ChatMessageBubble>
</ChatMessage>
<ChatMessage sender="system">
<ChatSystemMessage>Conversation started</ChatSystemMessage>
</ChatMessage>
</div>
A filled bubble (default).
A ghost bubble: no background, but the same alignment.
<div className={columnStyle}>
<ChatMessage sender="assistant">
<ChatMessageBubble>A filled bubble (default).</ChatMessageBubble>
</ChatMessage>
<ChatMessage sender="assistant">
<ChatMessageBubble variant="ghost">
A ghost bubble: no background, but the same alignment.
</ChatMessageBubble>
</ChatMessage>
</div>
ModelRequestsTokens
Atlas1,8423.2M
Nova9671.1M
Ember413620K
<div className={columnStyle}>
<ChatMessage sender="assistant">
<ChatMessageBubble width="full">
<Table
columns={usageColumns}
data={usageData}
density="compact"
idKey="model"
label="Model usage"
/>
</ChatMessageBubble>
</ChatMessage>
</div>
Consecutive bubbles from one sender
tighten their corners on the sender side
so they read as one run.
<div className={columnStyle}>
<ChatMessage sender="user">
<ChatMessageBubble group="first">
Consecutive bubbles from one sender
</ChatMessageBubble>
<ChatMessageBubble group="middle">
tighten their corners on the sender side
</ChatMessageBubble>
<ChatMessageBubble group="last">
so they read as one run.
</ChatMessageBubble>
</ChatMessage>
</div>
Navi
Hello! I looked into your question.
Cindy
Thanks, that was fast!
2:31 PMRead
<div className={columnStyle}>
<ChatMessage
avatar={<Avatar name="Navi" size="small" />}
sender="assistant">
<ChatMessageBubble
metadata={
<ChatMessageMetadata
timestamp={
<Timestamp format="time" value="2026-07-14T14:30:00Z" />
}
/>
}
name="Navi">
Hello! I looked into your question.
</ChatMessageBubble>
</ChatMessage>
<ChatMessage avatar={<Avatar name="Cindy Park" />} sender="user">
<ChatMessageBubble
metadata={<ChatMessageMetadata status="read" timestamp="2:31 PM" />}
name="Cindy">
Thanks, that was fast!
</ChatMessageBubble>
</ChatMessage>
</div>
Message with sending status
2:30 PMSending
Message with sent status
2:30 PMSent
Message with delivered status
2:30 PMDelivered
Message with read status
2:30 PMRead
Message with error status
2:30 PMFailed
<div className={columnStyle}>
{(['sending', 'sent', 'delivered', 'read', 'error'] as const).map(
status => (
<ChatMessage key={status} sender="user">
<ChatMessageBubble
metadata={
<ChatMessageMetadata status={status} timestamp="2:30 PM" />
}>
Message with {status} status
</ChatMessageBubble>
</ChatMessage>
),
)}
</div>
Conversation started
Model upgraded
<div className={columnStyle}>
<ChatSystemMessage>Conversation started</ChatSystemMessage>
<ChatSystemMessage icon={<Icon icon={Sparkles} size="sm" />}>
Model upgraded
</ChatSystemMessage>
<ChatSystemMessage variant="divider">Today</ChatSystemMessage>
</div>

A single chat message: sender-aware wrapper providing alignment, avatar, name, and metadata around the message content.

PropTypeDefaultDescription
avatarReactNodeAvatar rendered beside the message. Ignored for system messages.
children*ReactNodeMessage body — typically one or more ChatMessageBubble elements followed by a ChatMessageMetadata.
classNamestringAdditional CSS class names applied to the root element.
data-testidstringTest ID applied to the root element.
density"compact" | "balanced" | "spacious"'balanced'Density preset controlling gaps. Defaults to the surrounding ChatMessageList density.
metadataReactNodeMetadata rendered below the message body. Use when the last child is raw content; if the last child is a ChatMessageBubble, prefer the bubble's metadata prop so it aligns with the bubble padding. Ignored for system messages.
nameReactNodeSender name rendered above the message body. Use when the first child is raw content; if the first child is a ChatMessageBubble, prefer the bubble's name prop so it aligns with the bubble padding. Ignored for system messages.
refRef<HTMLElement>Ref forwarded to the root element.
sender*"assistant" | "system" | "user"Who authored the message. Controls alignment, avatar placement, and how child bubbles style themselves.
styleCSSPropertiesInline styles applied to the root element.
aria-describedbyAriaAttributes['aria-describedby']Identifies the element(s) that describe the region.
aria-labelAriaAttributes['aria-label']Accessible label for the region.
aria-labelledbyAriaAttributes['aria-labelledby']Identifies the element(s) that label the region.
idstringHTML id attribute applied to the root element.

The styled chat "bubble". Reads sender and density from the surrounding ChatMessage so it aligns and colors itself automatically.

PropTypeDefaultDescription
children*ReactNodeBubble content — text or any ReactNode.
classNamestringAdditional CSS class names applied to the bubble element.
data-testidstringTest ID applied to the bubble element.
group"first" | "last" | "middle"Position within a multi-bubble group. Tightens the sender-side corners so consecutive bubbles read as one run: first tightens the bottom corner, middle both, last the top. Leave unset for standalone bubbles.
metadataReactNodeMetadata rendered below the bubble, aligned with the bubble padding — typically a ChatMessageMetadata. Use on the last bubble in a message.
nameReactNodeSender name rendered above the bubble, aligned with the bubble padding. Use on the first bubble in a message.
refRef<HTMLDivElement>Ref forwarded to the bubble element.
styleCSSPropertiesInline styles applied to the bubble element.
variant"ghost" | "filled"'filled'Visual variant: filled draws a background, ghost keeps only the inline padding so content aligns with sibling bubbles.
widthWidthValueBubble width. Numbers are pixels, strings are used as-is, 'full' fills the message column. When set, replaces the default maximum-width cap.
aria-describedbyAriaAttributes['aria-describedby']Identifies the element(s) that describe the region.
aria-labelAriaAttributes['aria-label']Accessible label for the region.
aria-labelledbyAriaAttributes['aria-labelledby']Identifies the element(s) that label the region.
idstringHTML id attribute applied to the root element.

Metadata row for a chat message, rendered as `timestamp · footer · status` (reversed for user messages). Renders nothing when all parts are empty.

PropTypeDefaultDescription
classNamestringAdditional CSS class names applied to the root element.
data-testidstringTest ID applied to the root element.
footerReactNodeFooter content — model info, ratings, reactions.
refRef<HTMLDivElement>Ref forwarded to the root element.
status"error" | "delivered" | "read" | "sending" | "sent"Message delivery status, shown as an icon with a label.
styleCSSPropertiesInline styles applied to the root element.
timestampReactNodeTimestamp content — typically a <Timestamp format="time">.
aria-describedbyAriaAttributes['aria-describedby']Identifies the element(s) that describe the region.
aria-labelAriaAttributes['aria-label']Accessible label for the region.
aria-labelledbyAriaAttributes['aria-labelledby']Identifies the element(s) that label the region.
idstringHTML id attribute applied to the root element.

Centered system notice for chat threads: date separators, "conversation started", membership changes, and similar non-sender content.

PropTypeDefaultDescription
children*ReactNodeSystem message content — text or any ReactNode.
classNamestringAdditional CSS class names applied to the root element.
data-testidstringTest ID applied to the root element.
iconReactNodeIcon rendered before the text. Ignored by the divider variant.
refRef<HTMLDivElement>Ref forwarded to the root element.
styleCSSPropertiesInline styles applied to the root element.
variant"default" | "divider"'default'Visual variant: default is plain centered text, divider renders the content as a labeled horizontal divider (date-separator style).
aria-describedbyAriaAttributes['aria-describedby']Identifies the element(s) that describe the region.
aria-labelAriaAttributes['aria-label']Accessible label for the region.
aria-labelledbyAriaAttributes['aria-labelledby']Identifies the element(s) that label the region.
idstringHTML id attribute applied to the root element.