Skip to content

Documentation

neelam-ui

Search documentation

Getting Started
Forms
Overlays
Navigation
Data Display
Layout
AI & Chat
Blocks
GitHub repository

Message

One row in a conversation — an optional avatar, an optional sender and timestamp line, and the message content underneath.

AL
Ada Lovelace09:24
Morning — is the API doc ready to review?
09:26
Pushed it just now.
Second half still needs examples.

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#

Keyboard shortcuts
KeyBehaviour
TabNot 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 — timestamp accepts 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 Avatar with a fallback and no alt is decorative, which is right when the sender's name is already in the row.

API reference#

Props for Message
PropTypeDefault
avatar

Typically 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
senderReactNode
timestampReactNode
variant

Whose message this is — which side the avatar and content align to. Defaults to `"incoming"`. Match it to any `Bubble` children's own `variant`.

enumincoming

Also accepts every native <div> attribute.