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

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

Done
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

PropTypeDefaultDescription
childrenReact.ReactNodeforMessage 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.
roleChatRole—user, assistant or system. Assistant defaults to layout="flat". Overrides `variant`.
layoutChatLayout—bubble or flat. Defaults to flat for role="assistant", bubble otherwise (and for `variant`).
statusChatStatusreadyTurn lifecycle. Default "ready". submitted shows the pending indicator, streaming a live caret, stopped a label, error a retry row.
errorstring—Message shown when status="error".
onRetry() => void—Called by the Retry button in the error row.
actionsReact.ReactNode—Footer actions (copy, regenerate, feedback). Hidden while submitted or streaming.
indicatorReact.ReactNodeisReplaces the pending indicator shown while status="submitted". Default is a Murmur with pendingLabel.
pendingLabelstringThinking…Text beside the pending indicator. Default "Thinking…".
authorstring—Name shown above the bubble.
timestampstring—Timestamp / status shown under the bubble.
avatarSrcstring—Avatar image for the message sender.
avatarFallbackstring—Fallback initials when no avatar image (e.g. "AI", "AB").
hideAvatarboolean—Hide the avatar column.
classNamestring—Optional custom CSS class name.
variant"incoming" | "outgoing" | "system""incoming"—
Configure Theme
Theme
Accent Color
Custom
Gray Family
Appearance
Radius
Scaling
Panel Style
Heading Font
Body Font