MessageScroller

A bounded, auto-sticking chat log that keeps the newest message in view.

Overview

MessageScroller is the scroll viewport for a chat log. It stacks its children vertically and, while the reader is at the bottom, keeps the newest message in view as content grows — pausing the moment they scroll up to read history, and resuming when they return to the bottom. Constrain its height via style.

Anatomy

A vertical flex stack inside a scroll viewport with a themed scrollbar (comp.messageScroller.*). After each update it checks whether the reader is near the bottom; if so, it pins scrollTop to the newest message. Fill it with Message rows.

Variants & States

No visual variants — the one behavioral toggle is stickToBottom.

State Status Notes
Following (at bottom) Implemented New messages auto-scroll into view.
Reading history (scrolled up) Implemented Auto-scroll pauses until the reader returns to the bottom.
stickToBottom=false Implemented Opts out of auto-scrolling entirely.
Disabled Not implemented
Loading Not implemented

Usage

Do

  • Constrain the height via style (maxHeight) — the bound is what makes the log scroll.
  • Fill it with Message rows so avatars, authors, and bubbles stay consistent.
  • Keep stickToBottom on for live chat so new messages appear without manual scrolling.

Don't

  • Don't force-scroll the reader back to the bottom while they're reading history — the pause behavior is intentional.
  • Don't nest another vertical scroll region inside it on the same axis.

Accessibility

Content

N/A — the scroller has no content model of its own. It bounds and follows whatever message rows you place inside.

Props API

React@ghds/react

PropTypeDefaultDescription
stickToBottombooleantrueKeep the view pinned to the newest message while the reader is at the bottom.
...restHTMLAttributes<HTMLDivElement>Native <div> attributes pass through; use style to set maxHeight.

Web Components@ghds/web-components

PropTypeDefaultDescription
stick-to-bottombooleantrueReflected attribute; keep the view pinned to the newest message while the reader is at the bottom.
scrollToBottom()() => voidMethod that jumps the viewport to the newest message.
(default slot)slotThe vertical stack of <gh-message> rows. Set max-height on the host.

React Native@ghds/react-native

PropTypeDefaultDescription
stickToBottombooleantrueKeep the view pinned to the newest message while the reader is at the bottom.
childrenReactNodeThe vertical stack of messages.
testIDstringTest handle for queries.

Code examples

// React — @ghds/react
import { MessageScroller } from '@ghds/react/message-scroller';
import { Message } from '@ghds/react/message';

<MessageScroller style={{ maxHeight: 320 }}>
  {messages.map((m) => (
    <Message key={m.id} side={m.side}>
      {/* … MessageAvatar / MessageContent / Bubble … */}
    </Message>
  ))}
</MessageScroller>;
<!-- Web Components — @ghds/web-components -->
<script type="module">
  import '@ghds/web-components/message-scroller';
  import '@ghds/web-components/message';
  // Imperative: scroller.scrollToBottom() jumps to the newest message
</script>
<gh-message-scroller style="max-height: 320px;">
  <gh-message side="received">
    <span slot="author">Bo Ram</span>
    <gh-bubble variant="received">Hey — are we still on for tomorrow?</gh-bubble>
  </gh-message>
</gh-message-scroller>
// React Native — @ghds/react-native
import { MessageScroller } from '@ghds/react-native/message-scroller';
import { Message } from '@ghds/react-native/message';

<MessageScroller style={{ maxHeight: 320 }}>
  {messages.map((m) => (
    <Message key={m.id} side={m.side}>{/* … */}</Message>
  ))}
</MessageScroller>;

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:00 Hey — are we still on for tomorrow? You 10:01 Yes! See you at 10. Bo Ram 10:02 Should I bring anything? You 10:03 Just the slides, I think. Bo Ram 10:04 Great, I'll print a few copies. You 10:05 Perfect. Coffee on me. Bo Ram 10:06 Deal. See you then! You 10:07 One more thing — which room? Bo Ram 10:08 The one on the third floor. You 10:09 Got it. Thanks!