Bubble
Displays conversational content in a message bubble. Supports variants, alignment, and collapsible content.
The Bubble component displays framed conversational content. Use it for chat text, short structured output and quoted replies.
For full-featured chat interfaces, use the Message component. Bubble is intentionally scoped to the bubble surface. Place avatars, names, timestamps, metadata, and message-level actions in Message.
Installation
import { Bubble } from "@o/pipeline/experimental/components/chat/bubble";
Usage
import { Bubble } from "@o/pipeline/experimental/components/chat/bubble";
<Bubble.Root>
<Bubble.Content>
I checked the registry output and removed the stale route.
</Bubble.Content>
</Bubble.Root>
A bubble on its own is content and nothing else. What a message collects and
what it offers — reactions, copy, a thread, the time — belong to
Message.Footer, which is a row below the bubble rather
than an overlay on it.
Composition
Use the following composition to build a bubble:
Bubble.Root
└── Bubble.Content
Consecutive bubbles from one sender are grouped by
MessageGroup, not here: what they share is an avatar
and a name, which are the message’s and not the bubble’s.
Features
- Six visual variants, from the reader’s own green bubble to unframed ghost content
- Start and end alignment for sender and receiver bubbles
- Reactions that anchor to the bubble edge with configurable side and alignment
- Bubbles size to their content, up to
min(30rem, 100% - 1rem) - Customizable styling through the
classNameprop on every part
Variants
Use variant to change the visual treatment of the bubble.
| Variant | Description |
|---|---|
default | The reader’s own message, in the brand green. |
secondary | Everyone else’s, and anything unattributed: a tint. |
subtle | Faint neutral fill without a visible border. |
elevated | Raised off the surface rather than tinted. |
ghost | Unframed content for assistant text or rich content. |
danger | A destructive bubble for error or failed actions. |
Green identifies the reader’s own messages. Other messages use neutral surfaces: a tint for another participant, or a fainter fill when the conversation calls for a quieter treatment. Unattributed messages also stay neutral.
A bubble sizes to its content, up to min(30rem, 100% - 1rem). The ghost variant spans the full row for assistant text and rich content.
Alignment
Use align on Bubble.Root to align the bubble to the start or end of the conversation.
| align | Description |
|---|---|
start | Align the bubble to the start of the conversation. |
end | Align the bubble to the end of the conversation. |
Note: When building chat interfaces, you probably want to use alignment on the Message component itself, not the Bubble.Root component. You can use the align prop on the Message component to automatically align the bubble to the start or end of the conversation.
Links and Buttons
A bubble is content, never a control, so Bubble.Content has no render prop to turn it into a link or a button. Put a link in the text, place a Button inside or beside the bubble, or use richer UI for choices such as suggested replies.
Show More / Collapsible
Long bubble content can be composed with Collapsible to allow for a show more or show less interaction. Use the Collapsible.Trigger component to trigger the collapsible content.
Tooltip
Wrap a bubble in a Tooltip to reveal metadata on hover, such as when a message was read.
Popover
Pair a bubble with a Popover to surface more information on demand, such as the full error message for a failed action.
Accessibility
Bubble.Root renders the presentational message surface. Keep conversation-level semantics on the surrounding container and follow the guidelines below.
Meaning Beyond Color
Bubble variants signal role and tone with color. Pair them with text, alignment, or icons so meaning is not conveyed by color alone. For a destructive bubble, keep the error context in the message text rather than relying on the color treatment.
API Reference
Bubble.Root
The bubble’s row. Full width, so hovering anywhere level with the bubble counts as reaching for it rather than only the words themselves; the visible bubble is Bubble.Content, which sizes to its text and hugs the aligned side. It carries group/bubble, so a hover-revealed control inside one bubble responds to that bubble and not its neighbours.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "secondary" | "subtle" | "elevated" | "ghost" | "danger" | "default" | The bubble visual treatment. |
align | "start" | "end" | "start" | The inline alignment of the bubble. |
className | string | - | Additional classes to apply to the root element. |
Bubble.Content
The visible bubble: the fill, the border and the width cap live here, and it is a positioning context, so a Message.Actions placed inside anchors to the bubble rather than to the row.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional classes to apply to the content element. |