All components

Message Scroller

A chat transcript scroller that anchors turns, follows streamed replies at the live edge, preserves place when history loads, and jumps to any message.

AI
Ready when you are.
ME
I'm building a chat and the scroll behavior keeps jumping around.
AI
Wrap the transcript in MessageScroller. New turns anchor near the top, and auto-scroll only follows while you're at the live edge.
ME
And if I scroll up to read something earlier?
AI
Follow-output lets go. New chunks arrive offscreen until you jump back to the latest reply.

Anchoring Turns

Mark a row with scrollAnchor so a new exchange settles near the top. Anchors are not tied to role — toggle which message owns the turn.

Send a turn to see the selected role settle near the top.

Keeping Context Visible

scrollPreviousItemPeek keeps a slice of the previous item above the new anchor so the thread does not feel like it restarted on a blank page.

AI
Ready when you are.
ME
I'm building a chat and the scroll behavior keeps jumping around.
AI
Wrap the transcript in MessageScroller. New turns anchor near the top, and auto-scroll only follows while you're at the live edge.
New turns keep 64px of the previous item in view.

Following the Live Edge

autoScroll follows streamed replies only while the reader stays at the live edge. Scrolling away — wheel, touch, keys, or the scrollbar — releases the view.

AI
Ready when you are.
ME
I'm building a chat and the scroll behavior keeps jumping around.
AI
Wrap the transcript in MessageScroller. New turns anchor near the top, and auto-scroll only follows while you're at the live edge.
ME
And if I scroll up to read something earlier?
AI
Follow-output lets go. New chunks arrive offscreen until you jump back to the latest reply.
ME
Summarize why chat scroll feels jumpy.
AI
Press Stream to watch the reply grow.
Stay at the bottom to follow. Scroll away and the stream continues offscreen.

Opening Position

defaultScrollPosition controls where a saved thread lands. last-anchor opens on the last meaningful turn; start and end land at the edges.

ME
This is the first message the user sent in the conversation.
AI
Workspace creation rose 8%, but first invite completion only rose 2%.
ME
What should I look at next?
AI
Compare invite drop-off by account size, then check whether users who skip invites still return within 24 hours.
ME
This is the last message the user sent in the conversation.
AI
Start with the invite step. Teams are creating workspaces but waiting to add collaborators.
AI
If that pattern holds, the next experiment should make collaboration useful earlier instead of prompting for invites harder.
last-anchor opens on the last user message. start and end land at the edges.

Loading Earlier Messages

When older rows are prepended, the viewport preserves the visible row so history loads above the reader. Enabled by default via preserveScrollOnPrepend.

AI
Not yet. Queue depth is recovering after we reduced retry concurrency.
ME
Keep watching for customer-visible issues.
AI
I will watch the queue and support tags for another 15 minutes.
ME
If those stay quiet through the next batch window, we can close this.
AI
Agreed. Keep rollback ready if the queue starts climbing again.
Prepended rows keep your place. Use stable messageId values.

Jumping to Messages

useMessageScroller exposes scrollToMessage, scrollToStart, and scrollToEnd from any descendant of the provider — including controls outside the frame.

ME
Review the incident handoff and tell me what to read first.
AI
Start with the summary and the impact section. The regression affected the upload queue.
ME
What was the customer impact?
AI
Impact was limited to delayed processing. No records were dropped.
ME
What actions are open?
AI
Keep the retry window enabled until the next deploy, then add a queue-depth alert.
ME
Give me the follow-up checklist.
AI
Compare the queue recovery graph with the deploy timeline, then add an owner beside each follow-up item.
useMessageScroller drives the transcript from outside the list.

Tracking the Reader

useMessageScrollerVisibility reports the current anchored turn and the message ids on screen. Tracking only runs while something subscribes.

ME
Review the incident handoff and tell me what to read first.
AI
Start with the summary and the impact section. The regression affected the upload queue.
ME
What was the customer impact?
AI
Impact was limited to delayed processing. No records were dropped.
ME
What actions are open?
AI
Keep the retry window enabled until the next deploy, then add a queue-depth alert.
ME
Give me the follow-up checklist.
AI
Compare the queue recovery graph with the deploy timeline, then add an owner beside each follow-up item.
Current turn: —. Visible: 0.

Reading Scroll State

useMessageScrollerScrollable reports which edges still have content. The jump-to-latest button uses the same signal via data-scrollable.

ME
Review the incident handoff and tell me what to read first.
AI
Start with the summary and the impact section. The regression affected the upload queue.
ME
What was the customer impact?
AI
Impact was limited to delayed processing. No records were dropped.
ME
What actions are open?
AI
Keep the retry window enabled until the next deploy, then add a queue-depth alert.
ME
Give me the follow-up checklist.
AI
Compare the queue recovery graph with the deploy timeline, then add an owner beside each follow-up item.
The transcript fits in view.

Install

Source

components/ui/ark-ui/message-scroller.tsx

Inspired by the Message Scroller component from shadcn/ui.