Bubble
Show the text of a chat message on a surface that fits its sender and purpose.
Installation
pnpm dlx shadcn@4.18.0 add @siteplane/bubbleComplete the one-time setup first. Registry JSON lists source files and dependencies.
Usage
import { Bubble, BubbleContent } from "@/siteplane/components/bubble";
<Bubble>
<BubbleContent>Can you move the renewal review to Thursday?</BubbleContent>
</Bubble>;Composition
BubbleGroup (optional, for consecutive bubbles)
└── Bubble
└── BubbleContentBubble places the surface and BubbleContent draws it. A bubble is as wide as its text, up to 80 percent of the width it sits in, and a ghost bubble takes the full width. Long words and URLs wrap inside the bubble instead of widening it. In a conversation, put the bubble in a Message, which holds the avatar, the sender's name, the time and the actions of the turn.
Variants
Choose the variant by who wrote the message and what it holds.
muted, the default, is the quiet neutral fill, for the person's own turns in an AI chat and for the messages of the others in a conversation between people.defaultis the accent fill with the accent's text color, for the person's own messages in a conversation between people.tintedis a tint of the accent with the primary text color, a quieter choice thandefaultfor the person's own messages.outlineis the bordered message surface of the Card'smessagevariant, with a small corner on the sender's side, for messages that need an edge of their own, such as on a filled surface.ghosthas no surface and takes the full width, for the assistant's text and for rich content such as lists, tables and code.destructiveis the error tint with the error text color, for a message or a reply that failed. Say what failed in the text, since the tint alone does not.
Alignment
align="end" places a bubble at the end of its row, for the person who is writing, and start is the default. Inside a Message with align="end", give the bubble the same align. The small corner of an outline bubble moves to the sender's side with it.
Groups
BubbleGroup stacks consecutive bubbles from one sender 8 px apart, such as a message sent in parts. Give each bubble its own align; the group does not set it. For consecutive turns that each have their own time or actions, use a Message Group instead.
AI SDK
The text parts of a UIMessage go into bubbles. The examples on this page show fixed messages; the code below takes them from useChat instead.
Text parts
The person’s text goes into a muted bubble at the end, and the assistant’s into a ghost bubble. A text part has state: "streaming" while it grows, and the bubble grows with it, without animation.
<MessageContent>
{message.parts.map((part, index) =>
part.type === "text" ? (
<Bubble
align={message.role === "user" ? "end" : "start"}
key={index}
variant={message.role === "user" ? "muted" : "ghost"}
>
<BubbleContent>{part.text}</BubbleContent>
</Bubble>
) : null,
)}
</MessageContent>An assistant’s text is Markdown. Render it inside the ghost bubble with your Markdown renderer and the element map of Typeset, which sets it in the kit’s type.
Failed requests
When a request fails, status is "error". Show a destructive bubble with a short message of your own and Try again, as in the error turn of the Message page.
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
Bubble. | default | muted | tinted | outline | ghost | destructive | muted | The surface of the message. ghost has no surface and takes the full width; the others are as wide as their text, up to 80 percent. |
Bubble. | "start" | "end" | "start" | The side of the row the bubble sits on. Inside a Message, give it the Message's align. |
Bubble | div props | — | Draws the surface: 8 px above and below the text, 16 px beside it, a 16 px radius and 14 px text on a 21 px line. Long words and URLs wrap. |
Bubble | div props | — | Consecutive bubbles from one sender, 8 px apart. Each bubble keeps its own align. |
--sp-bubble-padding-block / --sp-bubble-padding-inline | length | 8 px / 16 px | The space above and below and beside the text. Message reads the inline padding, so its header and footer start where the text starts. |
--sp-bubble-radius | length | 16 px | The radius of the surface, the radius of a dialog. It follows the kit's radius setting. |
Every part renders a div and accepts its props, a ref and className. Set the
custom properties on a bubble or on any element around it. A bubble is not
interactive: it takes no render prop and has no hover or focus state, so a
link or a Button goes inside it or into the footer of its Message. The types
BubbleVariant and BubbleAlign are exported.
Usage notes
A bubble is a surface without conversation semantics; the thread around it, such as the log of a Message Scroller, carries them. A variant's color never carries the meaning alone. Pair it with words, as a destructive bubble says what failed.