Shadcn Message Scroller
The shadcn/ui Message Scroller is a chat scroll container that anchors turns, opens saved transcripts, follows streamed responses, loads history without jumping, and jumps to any message, styled with Tailwind CSS v4. Unlike a plain overflow-y-auto div, it only auto-follows the bottom while the reader hasn't scrolled away, so streaming AI replies never fight the user for control of the viewport. Message Scroller is a new shadcn primitive for building AI-chat interfaces, and is the only one of the new chat components backed by its own headless package, @shadcn/react.
Key features
- Auto-follows the bottom during streaming, but only while the reader is already there — scrolling up is a deliberate opt-out that preserves their position.
scrollAnchoron aMessageScrollerItemsettles that turn near the top instead of snapping to the document end, with a peek of the prior exchange still visible.MessageScrollerButtonappears when there's unseen content below the fold and jumps back to the latest message on click.useMessageScroller,useMessageScrollerScrollable, anduseMessageScrollerVisibilityhooks for programmatic control and state.MessageScrollerContentdefaults torole="log"semantics so assistive tech announces new messages as they stream in.
Installation
npx shadcn@latest add message-scrollerThis installs the styled wrapper into @/components/ui and adds @shadcn/react as a dependency — the package that implements the actual scroll-anchoring and auto-follow behavior.
Usage
Wrap the thread in a MessageScrollerProvider, then compose MessageScroller, MessageScrollerViewport, MessageScrollerContent, and one MessageScrollerItem per turn.
import {
MessageScroller,
MessageScrollerButton,
MessageScrollerContent,
MessageScrollerItem,
MessageScrollerProvider,
MessageScrollerViewport,
} from "@/components/ui/message-scroller"
import { Message, MessageAvatar, MessageContent } from "@/components/ui/message"
export function Example() {
return (
<MessageScrollerProvider>
<MessageScroller className="h-96">
<MessageScrollerViewport>
<MessageScrollerContent className="p-4">
{messages.map((message) => (
<MessageScrollerItem
key={message.id}
messageId={message.id}
scrollAnchor={message.role === "user"}
>
<Message align={message.role === "user" ? "end" : "start"}>
<MessageAvatar>{/* … */}</MessageAvatar>
<MessageContent>{message.text}</MessageContent>
</Message>
</MessageScrollerItem>
))}
</MessageScrollerContent>
</MessageScrollerViewport>
<MessageScrollerButton />
</MessageScroller>
</MessageScrollerProvider>
)
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Turn anchoring
Set scrollAnchor on the MessageScrollerItem that should settle near the top of the viewport when a new turn arrives, instead of snapping straight to the bottom. In the demo above, the current user's own messages are anchored so a reply starts in view without the disorienting jump of a plain overflow container.
Programmatic scrolling
import { useMessageScroller } from "@/components/ui/message-scroller"
function JumpToLatest() {
const { scrollToEnd, scrollToMessage } = useMessageScroller()
return <button onClick={() => scrollToEnd({ behavior: "smooth" })}>Latest</button>
}2
3
4
5
6
Accessibility
MessageScrollerContentsetsrole="log"andaria-relevant="additions"by default, so screen readers announce new messages as they stream in.MessageScrollerButtonis a real<button>with ansr-onlylabel, and drops out of the tab order once the viewport is already at the bottom — no ghost focus stop.- Keep each
MessageScrollerItemkeyed by a stablemessageIdso scroll-to-message and visibility tracking stay accurate as the list updates.
When to use the Message Scroller
Use Message Scroller for any chat or AI-assistant thread where messages stream in over time. For a static, non-streaming list that doesn't need auto-follow or anchoring, a plain scrollable container is enough — reach for Scroll Area instead.
Used in these blocks
Message Scroller ships with ShadcnStore's upcoming AI-Chat blocks (next release), powering the scrollable thread behind streamed assistant replies. This section will link to the real blocks once they're live.
Related components
Message Scroller wraps a list of Message turns, each typically paired with an Avatar; use a Marker for status lines between turns. For scrollable content that isn't a chat thread, use Scroll Area instead.
FAQ
What is the shadcn message scroller used for?
Message Scroller is a scroll container for chat threads that anchors turns, follows streamed AI responses while the reader is at the bottom, and shows a scroll-to-latest button when new content arrives off-screen.
How is Message Scroller different from a plain overflow container?
A plain overflow container just clips and scrolls. Message Scroller additionally auto-follows the bottom during streaming, anchors a chosen turn near the top instead of snapping to the document end, and only auto-scrolls when the reader hasn't deliberately scrolled away.
Does Message Scroller require an extra dependency?
Yes. Its scroll-anchoring and auto-follow logic ships in the @shadcn/react package; the CLI installs it automatically alongside the styled wrapper.
Can I jump to a specific message?
Yes. Call scrollToMessage(messageId) from the useMessageScroller hook, matching the messageId prop on the target MessageScrollerItem.