Message

A chat message row composing an avatar, author, timestamp, and bubble.

Overview

Message is a chat message row: an optional avatar, then a content column with an author + timestamp header and a Bubble. The side prop flips the layout so outgoing messages align to the end. Compose it from MessageAvatar, MessageContent, MessageAuthor, and MessageTimestamp, with a Bubble (from @ghds/react/bubble) and an Avatar (from @ghds/react/avatar).

Anatomy

A flex row laid out as: MessageAvatar (a leading Avatar) and MessageContent — a column holding a header (MessageAuthor + MessageTimestamp) above a Bubble. side="sent" reverses the row so the avatar and bubble align to the end. Spacing and text colours come from @ghds/tokens (comp.message.*).

Variants & States

The only axis is side (received aligns to the start, sent to the end). The bubble carries the visual variant.

State Status Notes
received Implemented Aligns to the start (incoming).
sent Implemented Aligns to the end (outgoing).
Hover / Focus Not implemented The row is static; interactive controls set their own states.
Disabled Not implemented
Loading Not implemented

Usage

Do

  • Match side to direction: received for incoming, sent for the current user.
  • Pair Bubble’s variant with side (received bubble on a received row, sent on sent).
  • Give MessageContent align-items: flex-end on sent rows so the header and bubble hug the edge.

Don't

  • Don't hand-build message rows from raw divs — the subcomponents carry the spacing and text tokens.
  • Don't convey who sent a message with colour alone; keep the author name visible.

Accessibility

Content

Keep the header to a short author name and a compact timestamp. The message text lives in the Bubble; don’t repeat the author inside it.

Props API

React@ghds/react

PropTypeDefaultDescription
side'received' | 'sent''received''received' aligns to the start; 'sent' aligns to the end (Message).
...restHTMLAttributes<HTMLDivElement>Native <div> attributes pass through to the row.
MessageAvatar / MessageContentHTMLAttributes<HTMLDivElement>The avatar slot and the header + bubble column.
MessageAuthor / MessageTimestampHTMLAttributes<HTMLSpanElement>The author name and timestamp in the header.

Web Components@ghds/web-components

PropTypeDefaultDescription
side'received' | 'sent''received'Reflected attribute; 'received' aligns to the start, 'sent' to the end.
slot="avatar"slotLeading avatar (e.g. <gh-avatar slot="avatar">).
slot="author" / slot="timestamp"slotThe header author name and timestamp.
(default slot)slotThe message body, typically a <gh-bubble>.

React Native@ghds/react-native

PropTypeDefaultDescription
side'received' | 'sent''received''received' aligns to the start; 'sent' aligns to the end (Message).
childrenReactNodeRow content — compose with the Message* sub-components.
testIDstringTest handle for queries (Message).
MessageAvatar / MessageContentReactNodeThe avatar slot and the header + bubble column.
MessageAuthor / MessageTimestampReactNodeThe author name and timestamp in the header.

Code examples

// React — @ghds/react
import {
  Message,
  MessageAvatar,
  MessageContent,
  MessageAuthor,
  MessageTimestamp,
} from '@ghds/react/message';
import { Bubble } from '@ghds/react/bubble';
import { Avatar } from '@ghds/react/avatar';

<Message side="received">
  <MessageAvatar>
    <Avatar name="Bo Ram" />
  </MessageAvatar>
  <MessageContent>
    <MessageAuthor>Bo Ram</MessageAuthor>
    <MessageTimestamp>10:02</MessageTimestamp>
    <Bubble variant="received">Hey — are we still on for tomorrow?</Bubble>
  </MessageContent>
</Message>;
<!-- Web Components — @ghds/web-components -->
<script type="module">
  import '@ghds/web-components/message';
  import '@ghds/web-components/avatar';
  import '@ghds/web-components/bubble';
</script>
<gh-message side="received">
  <gh-avatar slot="avatar" name="Bo Ram"></gh-avatar>
  <span slot="author">Bo Ram</span>
  <span slot="timestamp">10:02</span>
  <gh-bubble variant="received">Hey — are we still on for tomorrow?</gh-bubble>
</gh-message>
// React Native — @ghds/react-native
import {
  Message,
  MessageAvatar,
  MessageContent,
  MessageAuthor,
  MessageTimestamp,
} from '@ghds/react-native/message';
import { Bubble } from '@ghds/react-native/bubble';
import { Avatar } from '@ghds/react-native/avatar';

<Message side="received">
  <MessageAvatar>
    <Avatar name="Bo Ram" />
  </MessageAvatar>
  <MessageContent>
    <MessageAuthor>Bo Ram</MessageAuthor>
    <MessageTimestamp>10:02</MessageTimestamp>
    <Bubble variant="received">Hey — are we still on for tomorrow?</Bubble>
  </MessageContent>
</Message>;

Live Demo

React and Web Components render live below, driven by the same tokens — toggle dark mode in the header to see both respond identically. React Native can’t run in a browser without additional react-native-web wiring (not set up in this site), so its usage is shown as a code sample instead.

React @ghds/react

Web Components @ghds/web-components

Bo Ram 10:02 Hey — are we still on for tomorrow? You 10:03 Yes! See you at 10.