Message

Composable conversation primitives for message rows, grouped bubbles, avatars, metadata, live markers, and a mount-only trailing-edge pop-up for newly sent rows.

Preview

YouNow
What should we include in the first release?
Sent
AssistantNow
Start with the smallest workflow that still feels complete.

Install

Add it with the shadcn CLI, or copy the source manually.

$ bunx --bun shadcn add @beui/message

API Reference

Message

from
"user" | "assistant"
animateIn?
boolean

Plays a trailing-edge pop-up once when this message row mounts.

false
className?
string

MessageGroup

spacing?
"default" | "compact"
compact
className?
string

MessageAvatar

placeholder?
boolean

Keep an empty avatar slot so grouped messages remain aligned.

false
className?
string

MessageContent

className?
string

MessageHeader

className?
string

MessageFooter

className?
string

MessageMarker

className?
string

MessageTyping

label?
string
Responding
className?
string

MessageBubble

variant?
"ghost" | "outline" | "solid" | "danger" | "soft" | "tint"
soft
align?
"end" | "start"

Defaults to the surrounding Message alignment when omitted.

animateIn?
boolean

Plays the bubble entrance once when this component mounts.

false
className?
string

MessageBubbleCollapsible

open?
boolean
defaultOpen?
boolean
false
onOpenChange?
((open: boolean) => void)
collapsedLines?
4 | 2 | 3 | 5 | 6
4
moreLabel?
ReactNode
Show more
lessLabel?
ReactNode
Show less
contentClassName?
string
triggerClassName?
string
className?
string

MessageBubbleContent

render?
ReactElement<unknown, string | JSXElementConstructor<any>>

Replaces the content element while preserving bubble styling.

className?
string

MessageBubbleGroup

spacing?
"default" | "compact"
compact
className?
string

MessageScroller

followOutput?
boolean

Keep streamed output pinned while the reader remains near the end.

true
followThreshold?
number

Distance from the end that still counts as following the output.

56
smooth?
boolean

Smoothly follow growing content.

true
onFollowChange?
((following: boolean) => void)

Reports when the reader leaves or returns to the live edge.

label?
string

Accessible label for the scrollable transcript.

Conversation
busy?
boolean

Marks the transcript as waiting for more streamed content.

navigation?
"rail"

Adds a compact rail for navigating between rendered Message rows.

navigationLabel?
string

Accessible label for the optional message navigation rail.

Message navigation
viewportClassName?
string
contentClassName?
string
railClassName?
string
viewportRef?
Ref<HTMLElement>
viewportProps?
Omit<DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>, "className" | "children" | "ref">
contentProps?
Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "className" | "children" | "ref">
className?
string

Composition

Build each conversation row from independent identity, content, and metadata primitives.

MessageGroup
└── Message
    ├── MessageAvatar
    └── MessageContent
        ├── MessageHeader
        ├── MessageBubble
        └── MessageFooter

Note: Message Bubble supplies the visual surface inside the row. Message Scroller positions message rows inside a reader-aware transcript. Agent Activity renders progressive agent work as message content.

How it works

A message is a conversation row. It coordinates the avatar, metadata, content surface, and delivery details while allowing the content itself to be a bubble, an agent activity, a tool result, or a composed response.

Updated