Message Bubble
- Stable
- WCAG 2.2 evidence
- RTL
One chat message: Direction (incoming, outgoing) x Type (text, reply, file, image) x
Group (single, continued), with sender name and avatar, time, an edited mark and read ticks. Accessibility: the reading order is name, quoted message, content, then edited mark, time and status (WCAG 1.3.1).
The sender's name is read even where it is hidden (continued, outgoing). Ticks and icons are decorative with text
alternatives (1.1.1); delivery state is a shape and a text, never colour only (1.4.1). Text colours meet 4.5:1 in
Light and Dark.
Text wraps and keeps line breaks (1.4.10, 1.4.12). Logical layout: the sides and the tail corner mirror in
right-to-left. Presentational: renders in React Server Components. Consumer duties: render the conversation as a list or a role="log" region; a meaningful alt on images; formatted
times; the translated editedLabel, statusLabel and downloadLabel; say "You" (or similar) as name on outgoing
messages if screen reader users need it.
import { MessageBubble } from "@nexera-ui/react";- 5
- examples
- 19
- props
- 5
- live controls
- 1
- platform
- 8
- WCAG criteria
- 5
- blocks use it
Try every prop. Copy the code.
Change the props and the code updates. Check light and dark, LTR and RTL, and phone width.
import { MessageBubble } from "@nexera-ui/react";
<MessageBubble />Examples 4
The same examples as Storybook, rendered live. Open Code to copy one.
Conversation
A short conversation in a role="log" list: a run of incoming messages, a reply, a file and read ticks.
import { Avatar, MessageBubble } from "@nexera-ui/react";
export function Conversation() {
const ayesha = <Avatar size="md" initials="AK" isDecorative />;
return (
<div role="log" aria-label="Conversation with Ayesha Khan" className="max-w-xl">
<ul className="m-0 flex list-none flex-col gap-2 p-0">
<li>
<MessageBubble name="Ayesha Khan" avatar={ayesha} group="single" time="10:22">
Hi Sara, can I take Friday off for a family event?
</MessageBubble>
</li>
<li>
<MessageBubble name="Ayesha Khan" avatar={ayesha} group="continued" time="10:23" isEdited>
I can hand over the payroll check to Bilal.
</MessageBubble>
</li>
<li>
<MessageBubble
name="You"
direction="outgoing"
type="reply"
quoteAuthor="Ayesha Khan"
quote="Hi Sara, can I take Friday off for a family event?"
time="10:31"
status="read"
>
Approved. Enjoy the long weekend.
</MessageBubble>
</li>
<li>
<MessageBubble
name="You"
direction="outgoing"
type="file"
fileName="Leave policy 2026.pdf"
fileDetails="PDF · 860 KB"
fileHref="#leave-policy"
time="10:32"
status="sent"
>
Here is the policy for reference.
</MessageBubble>
</li>
</ul>
</div>
);
}
Image with caption
An image with a caption; the frame shrinks with the container and keeps its aspect ratio.
import { Avatar, MessageBubble } from "@nexera-ui/react";
const PHOTO =
"data:image/svg+xml;utf8," +
encodeURIComponent(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 260 170"><rect width="260" height="170" fill="#D1FAE5"/><circle cx="130" cy="70" r="32" fill="#047857"/><rect x="70" y="112" width="120" height="40" rx="20" fill="#047857"/></svg>',
);
<MessageBubble
name="Ayesha Khan"
time="10:24"
avatar={<Avatar size="md" initials="AK" isDecorative />}
type="image"
image={<img src={PHOTO} alt="Ayesha at the team offsite" />}
>
From the offsite in Murree
</MessageBubble>;
Long content
Long words, links and line breaks wrap inside the bubble; the bubble never overflows a narrow container.
import { Avatar, MessageBubble } from "@nexera-ui/react";
<div className="max-w-xs">
<MessageBubble
name="Ayesha Khan"
time="10:24"
avatar={<Avatar size="md" initials="AK" isDecorative />}
isEdited
>
Could you check the attached contract? The link is
https://intranet.example.com/hr/contracts/2026/ayesha-khan-employment-contract-final-v3.pdf and
it expires on Friday.
</MessageBubble>
</div>;
Right to left
Right-to-left: incoming messages sit on the right, outgoing ones on the left, and the tail corner mirrors.
import { Avatar, MessageBubble } from "@nexera-ui/react";
export function RightToLeft() {
return (
<div className="flex max-w-xl flex-col gap-2">
<MessageBubble
name="عائشة خان"
avatar={<Avatar size="md" initials="ع" isDecorative />}
time="10:24"
>
مرحبا سارة، هل يمكنني أخذ إجازة يوم الجمعة؟
</MessageBubble>
<MessageBubble direction="outgoing" time="10:31" status="read" statusLabel="مقروءة">
موافق. استمتعي بعطلة نهاية الأسبوع.
</MessageBubble>
</div>
);
}
Props 19
Press "Try it" on a card to load that prop into the playground.
19 props shown
childrenNexeraReactNodeThe message. With
type="image" it is the caption under the image. Line breaks typed by the author are kept; long words wrap.
directionNexera"incoming" | "outgoing"Who sent it. incoming sits at the inline start on bg/sunken; outgoing at the inline end on
brand/primary. Sides swap in right-to-left.
typeNexera"text" | "reply" | "file" | "image"Content: plain text; a reply with the quoted message (quote, quoteAuthor) above the text; a
file card (fileName, fileDetails, fileHref); or an image (image) with an optional caption (children).
groupNexera"single" | "continued"Position in a run of messages from the same person. single shows the sender's name and avatar;
continued hides them (the name stays available to screen readers) and keeps the avatar's space so bubbles line up.
nameNexerastringSender's name. Shown above incoming single messages; otherwise it is
visually hidden but read first, so every message says who wrote it (WCAG 1.3.1).
avatarNexeraReactNodeSender's avatar: pass <Avatar size="md" isDecorative... />; it is
decorative because the name is given. Shown on incoming single messages; continued messages keep a 32 px space.
Pass it on every incoming message of a conversation so the bubbles line up. Ignored on outgoing messages.
timeNexeraReactNodeTime sent, formatted for the reader's locale.
dateTimeNexerastringMachine-readable time of time (ISO 8601). When set, the time is a <time> element.
isEditedNexerabooleanShow the "edited" mark.
editedLabelNexerastringText of the edited mark. Translate it for your locale.
statusNexera"sent" | "read"Delivery ticks of an outgoing message: read draws two ticks, sent one. The ticks differ in shape, not colour, and carry a text alternative (WCAG 1.1.1, 1.4.1).
Not shown when omitted.
statusLabelNexerastringText alternative of the status ticks. Translate it for your locale.
quoteNexeraReactNodeQuoted message of a reply. type="reply" only.
quoteAuthorNexeraReactNodeAuthor of the quoted message. type="reply" only.
fileNameNexeraReactNodeFile name. type="file" only.
fileDetailsNexeraReactNodeFile type and size. type="file" only.
fileHrefNexerastringDownload URL. When set, the file card is a download link with the download icon, keyboard
focusable with a visible focus ring (WCAG 2.1.1, 2.4.7). type="file" only.
downloadLabelNexerastringVisually hidden text that starts the download link's name ("Download Contract 2026.pdf"; WCAG 2.4.4). Translate it.
imageNexeraReactNodeThe image of type="image": pass an <img> with a meaningful alt (WCAG 1.1.1). It fills
the frame, which shrinks with the container and keeps the Figma aspect ratio.
* Required. React Aria props shown are the ones most apps use; the component accepts the rest of its React Aria props too.
Accessible by default.
Built on React Aria, and covered by the WCAG 2.2 evidence generated on every build.
WCAG 2.2 evidence
6 direct · 2 supporting- 1.1.1Non-text ContentLevel A · tested directly
- 1.3.1Info and RelationshipsLevel A · tested directly
- 1.4.1Use of ColorLevel A · tested directly
- 1.4.10ReflowLevel AA · supporting test
- 1.4.12Text SpacingLevel AA · supporting test
- 2.1.1KeyboardLevel A · tested directly
- 2.4.4Link Purpose (In Context)Level A · tested directly
- 4.1.2Name, Role, ValueLevel A · tested directly
Fits any width.
Nexera components respond to the space they are given. Drag the corner of the frame, or pick a width.
Styling hooks
Pass className to add Tailwind classes (merged last). State is exposed as data attributes, so you can style it with variants like data-pressed:.
<MessageBubble className="shadow-sm" />Used in blocks
- ChatMessaging and collaboration
- InboxMessaging and collaboration
- Helpdesk assistantMessaging and collaboration
- Comment threadMessaging and collaboration
- Day separators and system notesMessaging and collaboration
Related components
- ChatHeaderThe top bar of a conversation: avatar, the name as a heading, a status line and actions such as Search and More.
- ComposerThe message box under a conversation: a growing textarea with attach, emoji and voice buttons, a Send button, a "replying to" bar, attachments and a voice recording row.
- ConversationItemOne conversation in a list: avatar, name, time, the last message and an unread count.
- DateSeparatorMarks where a new day starts in a conversation: a label between two lines.
- ReactionA reaction under a message, such as a thumbs up, that the user can add or remove.
- SystemMessageAn event in a conversation that nobody wrote, such as someone joining or a channel being renamed: a small muted pill with an optional icon.
- TypingIndicatorShows that someone is writing a reply: three dots in a bubble and a short text.