ChatMessage
ChatMessage is a multi-part component for displaying chat messages — assistant replies on the left, user messages on the right. Built-in typing animation, delivery status, and hover actions.
Playground
<div className="flex flex-col gap-4">
<ChatMessage avatarSize="md">
<ChatMessageAvatar />
<div className="flex flex-col gap-1">
<ChatMessageContent>Sure! Let me help you with that.</ChatMessageContent>
<ChatMessageFooter timestamp="12:33" />
</div>
</ChatMessage>
<ChatMessage role="user" color="muted">
<div className="flex flex-col gap-1 items-end">
<ChatMessageContent>That makes sense!</ChatMessageContent>
<ChatMessageFooter timestamp="12:34" />
</div>
</ChatMessage>
</div>Variants
Sizes
smdefaultlg| Size | Padding | Font | Avatar |
|---|---|---|---|
sm | 12 × 6px | 13px | 24px |
default | 14 × 8px | 14px | 28px |
lg | 16 × 10px | 16px | 32px |
Features
Role
role="assistant" aligns left; role="user" aligns right. Layout and alignment switch automatically based on role.
<ChatMessage role="assistant">
<ChatMessageAvatar />
<div className="flex flex-col gap-1">
<ChatMessageContent>How can I help you?</ChatMessageContent>
<ChatMessageFooter timestamp="12:34" />
</div>
</ChatMessage>
<ChatMessage role="user" color="muted">
<div className="flex flex-col gap-1 items-end">
<ChatMessageContent>That makes sense!</ChatMessageContent>
<ChatMessageFooter timestamp="12:35" status="read" />
</div>
</ChatMessage>UI Type
Messenger uses bubble-style chat UI — History uses flat minimal layout for log-style views. Pick the type that fits your use case.
Typing
Set typing=true to show animated typing dots inside the Content area — use while the AI response streams in.
<ChatMessage role="assistant" typing>
<ChatMessageAvatar />
<ChatMessageContent />
</ChatMessage>Actions
Pass a ReactNode to actions to reveal action buttons on hover — copy, react, or any custom action.
<ChatMessage
role="assistant"
actions={
<button className="p-1 rounded hover:bg-background-muted">
<svg>...</svg>
</button>
}
>
<ChatMessageAvatar />
<div className="flex flex-col gap-1">
<ChatMessageContent>How can I help?</ChatMessageContent>
<ChatMessageFooter timestamp="12:34" />
</div>
</ChatMessage>API
Component Structure
ChatMessage— Compound · Pure ReactChatMessageContext provider + flex containerChatMessageAvatarAvatar — image, initials, or iconChatMessageContentBubble or flat message contentChatMessageFooterTimestamp and delivery statusMulti-part component — ChatMessage (root), ChatMessageAvatar, ChatMessageContent, ChatMessageFooter.
Props
ChatMessage
role"assistant""assistant" | "user"Sender role — assistant aligns left, user aligns right with matching bubble colors
variant"bubble""bubble" | "flat"Bubble style — bubble (bordered) or flat (no background)
color"default""default" | "muted" | "primary" | "dark"Bubble background — default (bordered), muted (subtle bg), primary (brand), dark
size"default""sm" | "default" | "lg"Size scale — sm, default, or lg
radius"2xl""md" | "lg" | "xl" | "2xl"Bubble corner radius
tailtruebooleanAsymmetric tail — zeroes the corner radius on the avatar side of the bubble
avatarSizeundefined"sm" | "md" | "lg"Avatar size — also adjusts the gap between the avatar and bubble
typingfalseboolean | { variant?: "dots" | "cursor", color?: "default" | "primary" | "muted", speed?: "slow" | "default" | "fast", label?: string, showLabel?: boolean }Show animated typing dots inside the Content area
actionsundefinedReactNodeAction buttons revealed on hover — copy, react, etc.
classNameundefinedstringAdditional CSS classes
| Name | Type | Default | Description |
|---|---|---|---|
role | "assistant" | "user" | "assistant" | Sender role — assistant aligns left, user aligns right with matching bubble colors |
variant | "bubble" | "flat" | "bubble" | Bubble style — bubble (bordered) or flat (no background) |
color | "default" | "muted" | "primary" | "dark" | "default" | Bubble background — default (bordered), muted (subtle bg), primary (brand), dark |
size | "sm" | "default" | "lg" | "default" | Size scale — sm, default, or lg |
radius | "md" | "lg" | "xl" | "2xl" | "2xl" | Bubble corner radius |
tail | boolean | true | Asymmetric tail — zeroes the corner radius on the avatar side of the bubble |
avatarSize | "sm" | "md" | "lg" | undefined | Avatar size — also adjusts the gap between the avatar and bubble |
typing | boolean | { variant?: "dots" | "cursor", color?: "default" | "primary" | "muted", speed?: "slow" | "default" | "fast", label?: string, showLabel?: boolean } | false | Show animated typing dots inside the Content area |
actions | ReactNode | undefined | Action buttons revealed on hover — copy, react, etc. |
className | string | undefined | Additional CSS classes |
ChatMessageAvatar
size"md""sm" | "md" | "lg"Avatar display size
srcundefinedstringAvatar image URL
alt"Avatar"stringAlt text for the avatar image
initialsundefinedstringInitials shown when no image (1–2 characters)
iconundefinedReactNodeCustom icon rendered inside the avatar
classNameundefinedstringAdditional CSS classes
| Name | Type | Default | Description |
|---|---|---|---|
size | "sm" | "md" | "lg" | "md" | Avatar display size |
src | string | undefined | Avatar image URL |
alt | string | "Avatar" | Alt text for the avatar image |
initials | string | undefined | Initials shown when no image (1–2 characters) |
icon | ReactNode | undefined | Custom icon rendered inside the avatar |
className | string | undefined | Additional CSS classes |
ChatMessageContent
classNameundefinedstringAdditional CSS classes
| Name | Type | Default | Description |
|---|---|---|---|
className | string | undefined | Additional CSS classes |
ChatMessageFooter
timestampundefinedstringPre-formatted timestamp string (e.g. "12:34 PM")
statusundefinedReactNode | "sending" | "sent" | "read" | "error"Delivery status — sending, sent, read, or error
sizeundefined"sm" | "default" | "lg"Size override for the footer — defaults to context value from ChatMessage root
classNameundefinedstringAdditional CSS classes
| Name | Type | Default | Description |
|---|---|---|---|
timestamp | string | undefined | Pre-formatted timestamp string (e.g. "12:34 PM") |
status | ReactNode | "sending" | "sent" | "read" | "error" | undefined | Delivery status — sending, sent, read, or error |
size | "sm" | "default" | "lg" | undefined | Size override for the footer — defaults to context value from ChatMessage root |
className | string | undefined | Additional CSS classes |
Anatomy
Best Practices
Recommended
- ✓Include ChatMessageAvatar for role="assistant"
- ✓Set status on Footer for user messages to show delivery state
- ✓Leave ChatMessageContent empty when typing=true
Don't
- ✗Don't place ChatMessageAvatar inside role="user"
- ✗Don't use typing and children at the same time
- ✗Don't use for general content — use Card instead
Accessibility
Keyboard
ARIA / WCAG
- Avatar has aria-hidden="true" (decorative element)
- Delivery status uses visible text accessible to screen readers
- Typing dots have aria-hidden="true"