Message
A single chat turn with a role avatar and a bubble styled per sender.
Message represents one turn of a conversation (chat). It renders nothing by itself: it is an alignment controller (align="start" or "end") that lays its children side by side with flex and reverses the order for sent messages (align="end" → flex-row-reverse).
A Message is typically made of two sibling elements:
- MessageAvatar: the round avatar that identifies the author. Contains an
<Avatar>withAvatarImage+AvatarFallback. Optional: without an avatar, the bubble takes the full width. - MessageContent: the vertical column that stacks
MessageHeader(name / timestamp), theBubblewith itsBubbleContent, andMessageFooter(status, inline actions such as "Delivered" or "Undo"/"Retry" buttons).
Bubble is the colored rectangle of the message. Seven variants: default (primary accent, for the current user), secondary (a less saturated fill), muted (neutral gray, for the other participants), tinted (light primary tint), outline (neutral background + border), ghost (no styling, for long assistant notes), destructive (red, for errors / warnings). BubbleContent is the actual text container; BubbleReactions is absolutely positioned on top (emojis / reactions).
To stack several bubbles from the same author, wrap them in a BubbleGroup inside MessageContent: a single avatar is shown for the whole group.
To show a system state (bot thinking, loading, status note), use Marker in place of MessageContent: <Marker role="status"><MarkerIcon><Spinner /></MarkerIcon><MarkerContent>Checking the logs...</MarkerContent></Marker>.
Installation
$ bext ui add message
Usage
Anatomy
<Message align="start | end">
├── <MessageAvatar> // optional: the round avatar
│ └── <Avatar>...</Avatar>
└── <MessageContent> // vertical column
├── <MessageHeader>... // name + timestamp (optional)
├── <Bubble variant="..."> // the bubble, styled by variant
│ ├── <BubbleContent> // the message text
│ └── <BubbleReactions> // emoji reactions (optional)
└── <MessageFooter>... // status + actions (optional)
<MessageGroup> // wrapper that stacks several Messages from the same author
├── <Message>...</Message>
└── <Message>...</Message>API Reference
Message
| Prop | Type | Default | Description |
|---|---|---|---|
| align | "start" | "end" | "start" | Alignment of the message. "end" triggers flex-row-reverse (avatar on the right, bubble flush right), typically for sent messages. |
| className | string | — | Additional CSS classes on the root <div data-slot="message">. |
| children | ReactNode | — | Children: typically MessageAvatar + MessageContent. |
MessageGroup
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | Wrapper that stacks several <Message> from the same author with gap-2. |
MessageAvatar
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | Avatar container. min-w-8 shrink-0 self-end + translate-y-8 when a MessageFooter is present (to stay aligned with the bubble). |
| children | ReactNode | — | Typically an <Avatar> with AvatarImage + AvatarFallback. |
MessageContent
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | Vertical column that stacks Header + Bubble + Footer with gap-2.5. |
MessageHeader
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | Row above the bubble (author name, timestamp). text-xs muted-foreground. |
MessageFooter
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | — | Row below the bubble ("Delivered" status, "Undo"/"Retry" actions). Right-aligned when align="end". |
Data Attributes
Attributes you can target in custom CSS to override the style for each state.
| Attribute | On | Values | Description |
|---|---|---|---|
| data-slot | every sub-component | "message" | "message-group" | "message-avatar" | "message-content" | "message-header" | "message-footer" | Stable selector for custom styling. |
| data-align | Message | "start" | "end" | Reflects the align prop. Used by MessageFooter for justify-end when align=end. |
Keyboard
| Key | Action |
|---|---|
| Tab | Focus next interactive element (avatar link, footer button). |
| Enter / Space | Activate button/link in header/footer. |
Accessibility
The Message component has no specific ARIA role: it is a visual container. Its children (Avatar, MessageFooter buttons) carry their own aria-label and roles where needed.
For a message thread, wrapping it in a container with role="log" and aria-live="polite" lets screen readers announce new messages without interrupting. MessageScroller gives you the scrollable thread container, but it sets neither attribute and does not forward them: put role="log" and aria-live="polite" on your own wrapper around it.
BubbleReactions does not forward aria-* props, so name each reaction on the child you pass in: <span role="img" aria-label="Thumbs up">👍</span> for a static emoji, an aria-label on the <button> for an icon-only reaction button. The emoji alone is not enough.
The Marker for system states ("Checking the logs…" with a spinner) has no role by default: pass role="status" so that assistive technologies announce it as a non-urgent state change.
Examples
Avatar
Group
Group Chat
Header and Footer
Actions
Attachment
Attachment Group
On This Page


