Message Scroller
A chat scroll container for message transcripts.
MessageScroller is a chat transcript scroller built on the @shadcn/react primitives. It provides the styled ReScript surface for opening position, anchored turns, scroll preservation, and jump controls while leaving message rendering to Message, Bubble, Marker, and Attachment.
Installation
Usage
<MessageScroller.Provider defaultScrollPosition=LastAnchor>
<MessageScroller>
<MessageScroller.Viewport>
<MessageScroller.Content>
<MessageScroller.Item messageId="question" scrollAnchor=true>
<Message align=End>
<Message.Content>
<Bubble>
<Bubble.Content> {"Can you check the docs?"->React.string} </Bubble.Content>
</Bubble>
</Message.Content>
</Message>
</MessageScroller.Item>
</MessageScroller.Content>
</MessageScroller.Viewport>
<MessageScroller.Button ariaLabel="Scroll to latest" />
</MessageScroller>
</MessageScroller.Provider>MessageScroller fills its parent, so place it inside a height-constrained container.
Composition
MessageScroller.Provider
└── MessageScroller
├── MessageScroller.Viewport
│ └── MessageScroller.Content
│ ├── MessageScroller.Item
│ └── MessageScroller.Item
└── MessageScroller.ButtonMessageScroller.Providerowns scroll state and behavior props.MessageScrolleris the styled frame.MessageScroller.Viewportis the scrollable element.MessageScroller.Contentholds transcript rows.MessageScroller.Itemwraps every direct row so the primitive can measure, anchor, preserve position, and jump to it.MessageScroller.Buttonscrolls to the start or end and is inert when there is no content in that direction.
Core Concepts
Anchoring Turns
Mark the row that starts a meaningful turn with scrollAnchor=true. Use stable messageId values so the scroller can preserve and target rows.
<MessageScroller.Item messageId=message.id scrollAnchor={message.role == "user"}>
<Message />
</MessageScroller.Item>Opening Position
Set defaultScrollPosition=LastAnchor to open a saved transcript at the latest anchored turn instead of the absolute bottom.
Loading Earlier Messages
MessageScroller.Viewport preserves the visible row when older messages are prepended. This is enabled by default with preserveScrollOnPrepend=true.
Accessibility
MessageScroller.Viewport is a keyboard-focusable scroll region by default in the upstream primitive. MessageScroller.Content uses log-region defaults for additions. Pass ariaBusy=true while a turn streams if announcements should wait for the completed row.