ChatMessage
A single chat bubble — avatar, name, content, timestamp, and delivery status — aligned left or right depending on who sent it.
<UiChatMessage />ID
id is a stable identifier for the message. This component doesn't use it for anything itself — it matters when you render a list yourself and need a :key, which is exactly what ChatMessages does with it, falling back to the array index when it's missing.
Content
content is the message text, rendered as plain text. The default slot takes over completely when you use one, which is the route for anything richer than a sentence — a link, an image, a code block.
Own
own marks the message as sent by the current viewer rather than received: it aligns right, drops the avatar and name, and becomes the only kind of message that renders a status.
timestamp handles a Date and a string differently — a Date is formatted for you ('10:32 AM'); a string is displayed exactly as given, with no parsing. Pass a Date unless you specifically want custom text there.
The avatar gutter is reserved for received messages only, and is exactly the avatar's width — so a grouped message indents to line up under the avatar above it, and the gap to the bubble is the same gap-sm as everywhere else. Own messages get no gutter, because they never show an avatar and reserving the column only held the bubble off the edge.
Name
name is the sender's display name, printed above the bubble on received messages. It's dropped on own messages — you know who you are — and on grouped ones, where the run above already said it.
Avatar src
avatarSrc shows an image avatar in place of the initials, falling back to them if the image can't load — the same chain Avatar uses.

First name
firstName feeds the avatar's fallback initials — it isn't printed anywhere, since name is what's displayed. Pass both when you have them structured; the avatar is the only thing that reads this.
Last name
lastName is the second half of the avatar's fallback initials, so the circle reads “AL” rather than just “A”. Like firstName it's never rendered as text.
Timestamp
timestamp accepts either a Date, which is formatted as a local time like “10:32 AM”, or a string, which is printed exactly as given. Pass the Date when you have one — the formatting follows the reader's locale — and a string when you want something relative like “just now”.
Status
status shows a small delivery icon beside the timestamp. It only ever renders on own messages, matching how real chat apps report delivery for what you sent and not for what you received.
The status icon (status prop) only ever renders for own messages — there's no equivalent for received ones, matching how every real chat app only shows delivery state for what *you* sent.
Color
color tints an own bubble. Received messages are always neutral, so this has no effect without own.
Grouped
grouped hides the avatar and name so a message reads as part of the run above it. Deciding which messages group is the caller's job — ChatMessages does it for you from the timestamps.
grouped is the only thing this component knows about grouping — it just hides the avatar/name when true. Deciding which messages in a conversation should be grouped is ChatMessages's job, not this component's.
Reactions
reactions renders emoji pills below the bubble, each an emoji, a count and a reacted flag that highlights the pill for the current viewer. Clicking one emits toggle-reaction — the component reports the change and leaves updating the count to you, since the real count lives on your server.
Allow reactions
allowReactions is the hover trigger that opens an emoji picker on the bubble, and it's on by default. Turn it off in a thread where reacting isn't a thing people do — a system log, a transcript — so the affordance doesn't promise something the backend can't store.
API Reference
Generated from the component's source — props, slots and emits as the component actually declares them.
Props
string | numberbooleanfalsestringstring | Date"sending" | "sent" | "delivered" | "read" | "failed""neutral" | "primary" | "secondary" | "success" | "error" | "warning" | "info" | "pending"'primary'booleanfalseChatMessageReaction[]booleantrueSlots
defaultEmits
toggle-reaction[emoji: string]Types
type ChatMessageColor = "neutral" | "primary" | "secondary" | "success" | "error" | "warning" | "info" | "pending";type ChatMessageStatus = "sending" | "sent" | "delivered" | "read" | "failed";interface ChatMessageReaction {
/**
* The reaction emoji, e.g. '👍'.
*/
emoji: string;
/**
* How many people reacted with this emoji.
*/
count: number;
/**
* Whether the current viewer is among those who reacted with this emoji — highlights the pill.
* @defaultValue false
*/
reacted?: boolean;
}interface ChatMessageItem {
/**
* Unique identifier, used as the list key when rendered via `ChatMessages`. Falls back to the array index if omitted.
*/
id?: string | number;
/**
* The plain-text message content. Ignored if the default slot is used instead.
*/
content?: string;
/**
* Whether this message was sent by the current viewer — aligns it to the right and hides its avatar/name.
* @defaultValue false
*/
own?: boolean;
/**
* The sender's display name, shown above the bubble for non-own messages (unless `grouped`).
*/
name?: string;
/**
* The sender's avatar image URL. Falls back to initials, same as `Avatar`.
*/
avatarSrc?: string;
/**
* The sender's first name, used for the avatar's fallback initials.
*/
firstName?: string;
/**
* The sender's last name, used for the avatar's fallback initials.
*/
lastName?: string;
/**
* When to show this message: a `Date` is formatted as a local time (e.g. '10:32 AM'); a string is displayed as-is.
*/
timestamp?: string | Date;
/**
* Delivery status, shown as a small icon next to the timestamp. Only rendered for `own` messages.
*/
status?: ChatMessageStatus;
/**
* The bubble color for `own` messages. Has no effect on received messages, which are always neutral.
* @defaultValue 'primary'
*/
color?: ChatMessageColor;
/**
* Emoji reactions to show as pills below the bubble. Clicking one toggles it — the count and
* `reacted` state are your responsibility to update in response to the `toggle-reaction` event.
*/
reactions?: ChatMessageReaction[];
/**
* Shows a trigger (revealed on hover) that opens an emoji picker for reacting to the message.
* @defaultValue true
*/
allowReactions?: boolean;
}