Bubble
A Bubble shows users the text of one message in a frame, at the start or the end of the conversation.
When to Use
- for the text of each entry in a conversation, inside a Message
- as a button for a suggested action, such as “Send a payment reminder”
- as a
destructiveBubble, to tell users that a message failed - to show the reactions to a message on its edge, with
BubbleReactions
When Not to Use
- for the sender’s avatar, name, or time (wrap the Bubble in a Message)
- for a file (use an Attachment)
- for a date, an event, or a status in the conversation (use a Marker)
Anatomy
| Element | Usage |
|---|---|
| Content | Required |
| Reactions | Optional |
| Focus outline | Keyboard focus on a button or a link |
Bubble sets the variant and the alignment and has no surface of its own. BubbleContent is the surface, and BubbleGroup stacks Bubbles.
Variants
| Variant | Purpose |
|---|---|
default | A message from the current user |
secondary | A message from anyone else |
muted | A suggestion that users can click, such as “Send a payment reminder” |
tinted | A softer alternative to default |
outline | Structured content, like a list or a summary |
ghost | Long text that needs the full width of the row, with no frame around it |
destructive | A message that failed, such as “This message was not delivered.” |
By default, a Bubble has the default variant, which is the fill of the current user’s messages. Use secondary for everyone else’s messages.
secondary for the other sender and default for the current user.default, both senders have the same fill, and users can tell them apart only by the side of the row.Behavior
A Bubble is as wide as its text, up to 80% of its container. Longer text wraps, and a word that is longer than the Bubble breaks. A ghost Bubble can fill the full width.
Alignment
To move a Bubble to the end of a flex column, for example a BubbleGroup, set align="end". In a plain block, align has no effect. Inside a Message, the Message’s own align moves the Bubble, the avatar, and the footer together, so avoid setting align on the Bubble there.
align on the Message so that the avatar and the Bubble move together.align is set on a Bubble inside a Message, the Bubble moves to the end of the row, and its avatar stays at the start, away from the text.Reactions
BubbleReactions is positioned on the Bubble’s bottom edge, at its end. To move it to the top edge, set side="top". To move it to the start, set align="start".
Buttons and Links
To render a button or an a in place of the div, pass the element to render on BubbleContent. A trigger works in the same way: pass a PopoverTrigger to render on BubbleContent.
Content
| String | Rule | Example | Counterexample |
|---|---|---|---|
| A Bubble that is a button | Start with the verb | “Send a payment reminder” | “Payment reminder” |
A destructive Bubble | Say what failed | “This message was not delivered.” | “Error” |
aria-label of the reactions | Name every reaction in one phrase | “Reactions: thumbs up and party popper” | “Reactions” |
Accessibility
A Bubble is a div with no role, and it isn’t focusable. When its content is rendered as a button or an a, the content has the role and the keys of that element.
A destructive Bubble must say in its text what went wrong so that users who can’t see the color know that the
message failed. WCAG 2.2 SC 1.4.1 Use of Color doesn’t allow color to be the only sign of a failure.
Set role="img" and an aria-label that names the reactions on BubbleReactions. Screen readers then read one phrase instead of reading each emoji on its own. If users can click a reaction, render a button for each one and name it.
Installation
npx shadcn@latest add @summit/bubbleUsage
import { Bubble, BubbleContent } from '@/components/ui/bubble';
<Bubble variant="secondary">
<BubbleContent>The homepage draft is ready for your review.</BubbleContent>
</Bubble>;A Group
To stack Bubbles from one sender, use BubbleGroup. Set align on each Bubble, not on the group.
import { BubbleGroup } from '@/components/ui/bubble';
<BubbleGroup>
<Bubble variant="secondary">
<BubbleContent>The homepage draft looks great.</BubbleContent>
</Bubble>
<Bubble variant="secondary">
<BubbleContent>Can we move the booking button into the header?</BubbleContent>
</Bubble>
</BubbleGroup>;As a Button
Pass a button with type="button" to render.
<Bubble variant="muted">
<BubbleContent render={<button type="button" />}>Send a payment reminder</BubbleContent>
</Bubble>With Reactions
To choose the edge and the end of that edge, set side and align on BubbleReactions.
import { BubbleReactions } from '@/components/ui/bubble';
<Bubble variant="secondary" className="mb-6">
<BubbleContent>The contract is signed. We can start on Monday.</BubbleContent>
<BubbleReactions role="img" aria-label="Reactions: thumbs up and party popper">
<span>👍</span>
<span>🎉</span>
</BubbleReactions>
</Bubble>;With a Popover
To explain a message, for example one that failed, pass PopoverTrigger to render. Use the Popover for the reason and for what users can do next.
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
<Popover>
<Bubble variant="destructive">
<BubbleContent render={<PopoverTrigger />}>This message was not delivered.</BubbleContent>
</Bubble>
<PopoverContent align="start" className="text-sm">
The email address of the client bounced. Check it and send the message again.
</PopoverContent>
</Popover>;A Long Message
To shorten a long message, place the rest of it in a second Bubble inside a Collapsible. Users can click a link Button under it to show the rest.
import { Button } from '@/components/ui/button';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
<Collapsible className="flex flex-col gap-2">
<Bubble variant="secondary">
<BubbleContent>The first round of feedback covers the homepage and the booking flow.</BubbleContent>
</Bubble>
<CollapsibleContent>
<Bubble variant="secondary">
<BubbleContent>On the homepage, the hero photo crops badly on phones.</BubbleContent>
</Bubble>
</CollapsibleContent>
<CollapsibleTrigger render={<Button variant="link" size="sm" className="self-start px-0" />}>
Show the full message
</CollapsibleTrigger>
</Collapsible>;API Reference
Other props are passed to the element of each part.
Bubble
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'default' | 'secondary' | 'muted' | 'tinted' | 'outline' | 'ghost' | 'destructive' | 'default' | The fill and the text color |
align | 'start' | 'end' | 'start' | The end of the container that the Bubble is aligned to |
BubbleContent
BubbleContent adds no props of its own. See the Base UI useRender documentation for render.
BubbleReactions
| Prop | Type | Default | Description |
|---|---|---|---|
side | 'top' | 'bottom' | 'bottom' | The edge of the Bubble that the pill is positioned on |
align | 'start' | 'end' | 'end' | The end of that edge |