Chat Bubble
One message in a conversation, human or AI. Handles who is speaking, bubble or flat layout, and the turn lifecycle (submitted, streaming, ready, stopped, error) so any AI chat framework can map its message parts and status onto it.
// Plain markup
import { ChatBubble } from "@shavin/ui";AI assistant
Your order qualifies for a refund.
Claude · just now
Component Source
<ChatBubble role="assistant" avatarFallback="AI" status="ready" timestamp="Claude · just now">
<Reasoning status="done" duration="3s">Checked the order date against the policy.</Reasoning>
Your order qualifies for a refund.
</ChatBubble>Submitted and streaming
Thinking…
Looking at your order
Component Source
<ChatBubble role="assistant" avatarFallback="AI" status="submitted" />
<ChatBubble role="assistant" avatarFallback="AI" status="streaming">Looking at your order</ChatBubble>Stopped and error
Partial answer
Stopped
Something went wrong.
Component Source
<ChatBubble role="assistant" avatarFallback="AI" status="stopped">Partial answer</ChatBubble>
<ChatBubble role="assistant" avatarFallback="AI" status="error" onRetry={retry} />With a tool call
search_orders
1 result
Component Source
<ChatBubble role="assistant" avatarFallback="AI">
<ToolCall name="search_orders" status="done">1 result</ToolCall>
</ChatBubble>Incoming
Studio AI
Hi! I can help track orders or answer product questions.
1:59 pm
Component Source
<ChatBubble variant="incoming" author="Studio AI" avatarFallback="AI" timestamp="1:59 pm">
Hi! I can help track orders or answer product questions.
</ChatBubble>Outgoing
You
Where's my order 1043?
2:01 pm
Component Source
<ChatBubble variant="outgoing" author="You" avatarFallback="AB" timestamp="2:01 pm">
Where's my order 1043?
</ChatBubble>System
Studio AI is typing…
Component Source
<ChatBubble variant="system">Studio AI is typing…</ChatBubble>A conversation
Today
Studio AI
Your order shipped this morning and arrives tomorrow by 8pm.
You
Perfect, thanks!
Without avatar
Just the bubble.
Component Source
<ChatBubble variant="incoming" hideAvatar>Just the bubble.</ChatBubble>Props
| Prop | Type | Default | Description |
|---|---|---|---|
| children | React.ReactNode | for | Message content. For AI turns pass the parts in order: Reasoning, ToolCall, text, CodeBlock, FileOutput. Use layout="flat" (the default for role="assistant") so cards and code are not squeezed into a bubble. |
| role | ChatRole | — | user, assistant or system. Assistant defaults to layout="flat". Overrides `variant`. |
| layout | ChatLayout | — | bubble or flat. Defaults to flat for role="assistant", bubble otherwise (and for `variant`). |
| status | ChatStatus | ready | Turn lifecycle. Default "ready". submitted shows the pending indicator, streaming a live caret, stopped a label, error a retry row. |
| error | string | — | Message shown when status="error". |
| onRetry | () => void | — | Called by the Retry button in the error row. |
| actions | React.ReactNode | — | Footer actions (copy, regenerate, feedback). Hidden while submitted or streaming. |
| indicator | React.ReactNode | is | Replaces the pending indicator shown while status="submitted". Default is a Murmur with pendingLabel. |
| pendingLabel | string | Thinking… | Text beside the pending indicator. Default "Thinking…". |
| author | string | — | Name shown above the bubble. |
| timestamp | string | — | Timestamp / status shown under the bubble. |
| avatarSrc | string | — | Avatar image for the message sender. |
| avatarFallback | string | — | Fallback initials when no avatar image (e.g. "AI", "AB"). |
| hideAvatar | boolean | — | Hide the avatar column. |
| className | string | — | Optional custom CSS class name. |
| variant | "incoming" | "outgoing" | "system" | "incoming" | — |