Chat Message
Layout for one user or assistant turn in an AI chat, with avatar, body, bubble, content and media slots.
Description
ChatMessage is a compound component that lays out a single turn of a conversation. The root renders a <div role="article"> and takes from="assistant" (avatar column next to a body column) or from="user" (a stack pushed to the end edge). ChatMessage.Assistant and ChatMessage.User are the same root with from already set. The parts inside are plain elements you arrange yourself, so the component never owns your messages, transport or streaming state.
Use it for the message list of an assistant panel, a support chat or a full-page thread. ChatMessage.Content sets the one body text style for the turn, and Markdown inherits it, so a plain string and a parsed answer look the same. Actions, attachments and loaders from the other chat components drop into the body or under the bubble.
Don't use it for system notices inside a thread (rate limits, errors, model switches); use Inline Alert or Alert Banner. For comment threads with author names, timestamps and replies, build on Avatar and your own layout. For the scrolling container, stick-to-bottom behavior and the scroll button, wrap the messages in Chat Conversation.
ChatMessage is a Create UI Pro component. Install it with a Pro seat.
Installation
Anatomy
Usage
Examples
Every example is a Pro preview (marked with a Pro badge).
Roles
Pass from straight from your message data (message.role in the Vercel AI SDK) to pick the layout. The children still differ per role: assistants get an avatar and a body, users get a bubble.
Avatar
ChatMessage.Avatar wraps Avatar. Give it src and fallback initials, or pass children for a brand glyph. It defaults to size="sm" (32px) and takes every Avatar size, shape and variant.
Without alt the avatar is decorative, because the message label already names the speaker. Add alt when the avatar identifies a real person, as in a shared thread.
Consecutive messages
Set show={false} on every avatar after the first in a run. It renders an empty spacer with the same footprint, so the follow-ups start at the same x position.
Long messages
A short bubble shrinks to its text. A long one stops at 80% of the column, and unbroken strings such as URLs wrap inside it instead of overflowing. The assistant body fills the column; add className="pe-12" to Body if you want a trailing gutter.
Media
ChatMessage.Media stacks attachments. In a user turn put it above the bubble and align the group to the end; in an assistant turn put it inside Body, under the text.
Markdown
Content sets text-paragraph-sm and text-strongest. Markdown has no body size or color of its own, so paragraphs, lists and inline code pick up the same style as a plain string. Headings keep their own scale.
Streaming
Render Markdown with isStreaming inside Content while tokens arrive. Setting aria-busy on the message while it streams tells screen readers the turn is still changing.
With actions
Put ChatMessageActions inside Body for an assistant turn, or directly under the bubble for a user turn. The root carries the group/chat-message marker, so hovering anywhere on the message reveals the row.
Accessibility
ARIA notes:
- The root renders
role="article"with anaria-labelof"Assistant message"or"Your message", so each turn is announced with its speaker. Passaria-labelto translate it or name the sender ("Ayla Karagoz said"). - Pass
roleto change the semantics, for examplerole="listitem"when the messages sit in arole="list"container.Chat Conversationrendersrole="log", which works with the default. ChatMessage.Avatarisaria-hiddenunless you passalt. Withaltit rendersrole="img"with that label, whether the image or the initials are showing.- The hidden-avatar spacer is always
aria-hidden. - The message sets no
aria-live. Announce finished responses from the conversation, not from every streamed token, and setaria-busyon a turn while it streams.
Styling
Tailwind override: pass className to any part. The classes merge with cn(), so text size and color tokens on Content replace the defaults.
Data slots and attributes: the component sets these for CSS targeting:
data-slot="chat-message"on the root, withdata-role("assistant"or"user").data-slot="avatar"on the avatar, which keeps theAvatarattributesdata-size,data-shape,data-strokeanddata-variant.data-slot="chat-message-avatar-spacer"on the empty element rendered byshow={false}.data-slot="chat-message-body"on the assistant body column.data-slot="chat-message-bubble"on the bubble, withdata-rolefrom the root.data-slot="chat-message-content"on the text wrapper, withdata-rolefrom the root.data-slot="chat-message-media"on the attachment stack.
Related Components
- Chat Conversation: the scrolling log around the messages, with stick-to-bottom and a scroll button.
- Markdown: renders assistant text inside
ChatMessage.Contentand inherits its text style. - Chat Message Actions: copy, feedback and regenerate buttons under a message.
- Chat Attachment: image and file tiles for
ChatMessage.Media. - Chat Loader: the placeholder to show before the first token of a reply arrives.
API Reference
ChatMessage
The message root. Renders a <div>, provides the role to every part and extends React.ComponentProps<"div">. Also available as ChatMessage.Root and ChatMessageRoot.
Props
Variants
ChatMessage.Assistant / ChatMessage.User
The root with from fixed to "assistant" or "user". Take every ChatMessage prop except from. Also exported as ChatMessageAssistant and ChatMessageUser.
Props
ChatMessage.Avatar
The speaker avatar, top-aligned in the row. Wraps Avatar, so it extends the Avatar props (every <div> attribute plus size, shape, variant, background and stroke). Also exported as ChatMessageAvatar.
Props
Variants
ChatMessage.Body
The assistant column next to the avatar. A flex column with an 8px gap that takes the remaining width and can shrink below its content. Extends React.ComponentProps<"div">. Also exported as ChatMessageBody.
Props
ChatMessage.Bubble
The user bubble: bg-weak, 16px radius, shrinks to its text and caps at 80% of the column. Extends React.ComponentProps<"div">. Also exported as ChatMessageBubble.
Props
ChatMessage.Content
The text wrapper. Sets text-paragraph-sm text-strongest and wraps long words, for plain text and Markdown alike. Extends React.ComponentProps<"div">. Also exported as ChatMessageContent.
Props
ChatMessage.Media
A flex column with an 8px gap for attachments. Extends React.ComponentProps<"div">. Also exported as ChatMessageMedia.
Props
useChatMessageContext
Returns { role } from the nearest message root, or null outside one. Use it in custom parts that style themselves per role.
Types
chatMessageVariants (the root recipe, with the from variant) and chatMessageContentVariants (the content text style) are exported for composing your own parts.
