Summit
ComponentsConversation

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 destructive Bubble, 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

The contract is signed.
ElementUsage
ContentRequired
ReactionsOptional
Focus outlineKeyboard 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

VariantPurpose
defaultA message from the current user
secondaryA message from anyone else
mutedA suggestion that users can click, such as “Send a payment reminder”
tintedA softer alternative to default
outlineStructured content, like a list or a summary
ghostLong text that needs the full width of the row, with no frame around it
destructiveA 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.

DoUse secondary for the other sender and default for the current user.
Don’tWhen all the Bubbles are 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.

DoSet align on the Message so that the avatar and the Bubble move together.
Don’tWhen 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".

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

StringRuleExampleCounterexample
A Bubble that is a buttonStart with the verb“Send a payment reminder”“Payment reminder”
A destructive BubbleSay what failed“This message was not delivered.”“Error”
aria-label of the reactionsName 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/bubble

Usage

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.

A 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.

As a Button
<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.

With Reactions
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.

With a Popover
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.

A Long Message
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

PropTypeDefaultDescription
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

PropTypeDefaultDescription
side'top' | 'bottom''bottom'The edge of the Bubble that the pill is positioned on
align'start' | 'end''end'The end of that edge

On this page