Message Scroller

Scrolls a conversation, sticking to the newest message as it streams.

View as Markdown
What is Constella?
Constella is a component library on Base UI and StyleX. Every value comes from a token, so a theme is one file, and the motion stays quiet: a press that gives, a popover that settles.
<MessageScrollerProvider>  <MessageScroller>    <MessageScrollerViewport aria-label="Conversation">      <MessageScrollerContent>        {turns.map((turn) => (          <MessageScrollerItem            key={turn.id}            messageId={turn.id}            scrollAnchor={turn.from === "user"}          >            <Message from={turn.from}>…</Message>          </MessageScrollerItem>        ))}      </MessageScrollerContent>    </MessageScrollerViewport>    <MessageScrollerButton />  </MessageScroller></MessageScrollerProvider>

Installation

$ npx shadcn@latest add @constella/message-scroller

Usage

import {  MessageScroller,  MessageScrollerButton,  MessageScrollerContent,  MessageScrollerItem,  MessageScrollerProvider,  MessageScrollerViewport,} from "@/components/ai/message-scroller";

It opens at the newest message and follows a streaming answer while the reader is at the end; scrolled up, it stays put and shows the button to jump back. History loaded above keeps the reader's place. Mark each user message a scrollAnchor, so a new question scrolls to the top. The behavior is shadcn's @shadcn/react/message-scroller.

Composition

MessageScrollerProvider└── MessageScroller    ├── MessageScrollerViewport    │   └── MessageScrollerContent    │       └── MessageScrollerItem    └── MessageScrollerButton

API Reference

PartAdds
MessageScrollerItemmessageId; scrollAnchor
MessageScrollerButtonA pill that rises in while there's more below
useMessageScrollerscrollToEnd, scrollToStart, scrollToMessage

Every part takes sx, applied last.