Skip to content
Component installs need the registry setup— the Base UI shadcn project, the @vegastack namespace and the Cloudflare Access service token.
VegaStack Design

Message

Layout primitives for a conversation row — avatar anchoring, a content column, header/footer slots, and start/end alignment. Server-safe.

Status
stable
Since
0.1.0
Accessibility pattern
presentational layout

Last updated

AL
Ada Lovelace
Morning! Did the deploy finish overnight?
Yes — green across the board. 🎉
Sent 2m ago

Install

Add Message from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.

pnpm dlx shadcn@latest add @vegastack/message

Usage

import { Avatar } from "@/components/ui/avatar";
import { Bubble, BubbleContent } from "@/components/ui/bubble";
import {
  Message,
  MessageAvatar,
  MessageContent,
} from "@/components/ui/message";

<Message>
  <MessageAvatar>
    <Avatar fallback="AL" />
  </MessageAvatar>
  <MessageContent>
    <Bubble>
      <BubbleContent>Hello there!</BubbleContent>
    </Bubble>
  </MessageContent>
</Message>;

Anatomy

Message is pure layout — it owns no colours and is server-safe. Compose Avatar, Bubble, and Marker as children.

Message — data-slot="message"
MessageAvatar — data-slot="message-avatar"
MessageContent — data-slot="message-content"
MessageFooter — data-slot="message-footer"
MessageGroup — data-slot="message-group"
MessageHeader — data-slot="message-header"
  • Message — one row (data-slot="message", data-align). align="end" reverses the row for the current user's own messages.
  • MessageGroup — stacks consecutive messages from one sender.
  • MessageAvatar — anchors an Avatar to the bottom of the row.
  • MessageContent — the vertical column (bubbles, header, footer).
  • MessageHeader / MessageFooter — small muted lines above / below the bubble.

Examples

A conversation

Received (align="start") and sent (align="end") rows, with an avatar, a header, and a footer.

AL
Ada Lovelace
Morning! Did the deploy finish overnight?
Yes — green across the board. 🎉
Sent 2m ago

Message group

MessageGroup stacks consecutive messages from the same sender, with the avatar anchored once to the group.

LT
Linus
I pushed the fix to the branch.
Tests are green now.
Want me to open the PR?

MessageHeader sits above the bubble (sender name) and MessageFooter below (status / timestamp). With a footer, the avatar lifts to stay aligned.

AL
Ada Lovelace
Can you review the latest draft?
10:24 AM
On it — give me five minutes.
Read

Actions

Put a row of Buttons in MessageFooter for per-message actions. Pair a destructive bubble with a retry affordance for a failed message.

AI
Here's a summary of the changes in this release.
Send the invoice to the client.
Failed to send

Attachments

Compose rich content inside a bubble — a ghost bubble for flush media, or an outline bubble framing a file chip.

AL
release-notes.pdf248 KB

Motion

Pass animateIn on a message you append after mount — a streamed reply, or the message the user just sent — to fade + rise it in (motion-enter-up). It's off by default: an existing transcript rendered on page load must not animate every row. Bubble takes the same prop, so set either or both; a MessageScroller composes fine with it.

AL
Ada Lovelace
Ping me when the deploy is done.

Ghost bubble

MessageHeader and MessageFooter drop their inline padding when the bubble uses the ghost variant, so the muted lines align flush with edge-to-edge media.

AL
Ada Lovelace
Shared a screenshot · 2m ago

Agent transcripts

The four chat-family pieces compose a full agent transcript:

  • Thinking markerMarker + the shimmer utility: <Marker><MarkerContent className="shimmer">Thinking</MarkerContent></Marker>
  • Tool callsToolCallChip rows between messages; a spinner leading slot while running, then an icon + meta once resolved.
  • Artifacts — an Attachment with the produced thing's icon in AttachmentMedia, its name in AttachmentTitle, and a status Badge composed in AttachmentActions.
  • Code + resultsCodeBlock for the query and a compact Table with className="font-mono text-code-sm" cells for inline results.
  • Compose states — keep the submit or stop control's accessible name synchronized with its action.

API Reference

Message is the only part with its own prop (align). MessageGroup, MessageAvatar, MessageContent, MessageHeader, and MessageFooter are plain layout wrappers — they accept the standard div props plus className.

PropTypeDefaultDescription
align"end" | "start"'start'Which side the message sits on. - start: received message — avatar/content read left-to-right (default). - end: sent message — the row reverses so content hugs the end edge.
animateInbooleanfalseOpt-in entry animation (motion-enter-up, a fade + slight rise) for a message row that is newly appended to a live thread — e.g. a message that just streamed in, or one the user just sent. **Default off**: an existing transcript rendered on page load must not animate every row. Enable it only on the message(s) you append after mount (mirrors Bubble's animateIn; set either or both — a MessageScroller composes fine with it).

Data attributes and CSS variables on Message

AttributeValues
data-alignmirrors a prop or state value
data-slot"message"

Accessibility

  • Message is presentational layout — it adds no roles. Convey conversation semantics at the container level (e.g. a log / feed region) as your app requires.
  • align only changes visual order; DOM order stays logical for screen readers and keyboard users.
  • Pair with Avatar (which carries the accessible name) rather than relying on the avatar image alone.
ContractStates tested
Behaviourdefault
Accessibilitynative-or-base-ui-semantics, browser-accessibility-test
Visualdefault

Do / Don't

Do
Compose Avatar, Bubble, and Marker inside Message to build a conversation row, and use align='end' for the user's own messages.
Don't
Use Message for non-conversational list rows — reach for a plain layout, Data List, or Settings Row.

On this page