Message
One row in a conversation — an optional avatar, an optional sender and timestamp line, and the message content underneath.
Usage#
import { Avatar, AvatarFallback, Bubble, Message } from "neelam-ui";
<Message
avatar={<Avatar><AvatarFallback>AL</AvatarFallback></Avatar>}
sender="Ada Lovelace"
timestamp="09:24"
>
<Bubble>Morning — is the API doc ready to review?</Bubble>
</Message>Layout, not content#
Message owns the row's layout and nothing else: alignment, where the avatar
sits, and the sender/timestamp line. The message body is composed in as
children rather than baked in — the same split
Card's CardHeader and
Dialog's DialogHeader draw for their own
families.
That is why more than one child works: a quick burst of messages sent together
is several Bubbles in one Message, sharing a single avatar and timestamp.
Variants#
variant matches Bubble's — "incoming" (default)
or "outgoing", which reverses the row and right-aligns the content. Set it
on both: Message positions the row, Bubble colours itself, and they do not
read each other.
avatar is optional. Leaving it off for your own outgoing messages is normal —
plenty of chat UIs show no avatar there at all.
Keyboard#
| Key | Behaviour |
|---|---|
| Tab | Not focusable itself. Interactive content inside — an attachment link, a retry button — keeps its own tab stop, in DOM order. |
Reversal is visual only
variant="outgoing" uses flex-row-reverse, which moves the avatar visually
but does not change DOM order — so keyboard order still matches reading order.
That is the correct behaviour, and worth remembering if you nest controls in
the avatar slot.
Accessibility#
-
A plain
<div>with no role. A message row is content, not a widget. -
The sender name is real text in the row, not an
aria-label, so it is available to everyone rather than only to assistive tech. -
Pass a machine-readable timestamp yourself when precision matters —
timestampaccepts any node, so a<time dateTime="…">fits:<Message timestamp={<time dateTime="2026-09-01T09:24">09:24</time>}>…</Message> -
The avatar you pass is responsible for its own accessible name. An
Avatarwith a fallback and noaltis decorative, which is right when the sender's name is already in the row.
API reference#
| Prop | Type | Default |
|---|---|---|
avatarTypically an `<Avatar>`. Omit to leave the space uncaptured — common for your own outgoing messages, which many chat UIs don't show an avatar next to at all. | ReactNode | — |
sender | ReactNode | — |
timestamp | ReactNode | — |
variantWhose message this is — which side the avatar and content align to. Defaults to `"incoming"`. Match it to any `Bubble` children's own `variant`. | enum | incoming |
Also accepts every native <div> attribute.