Shadcn.io is not affiliated with official shadcn/ui
Shadcn Message Scroller
React chat scroller for AI threads and support transcripts. Built with TypeScript and Tailwind CSS for Next.js using Base UI — anchors turns, follows streams.
A message scroller is a height-constrained transcript. New turns pin near the top, streamed replies follow only while you sit at the live edge, and older history can load above without moving the row you are reading. Use it for AI chats, support inboxes, and any thread that should not yank the reader around.
Usage
import { Message } from "@/components/ui/message"
import {
MessageScroller,
MessageScrollerButton,
MessageScrollerContent,
MessageScrollerItem,
MessageScrollerProvider,
MessageScrollerViewport,
} from "@/components/ui/message-scroller"<MessageScrollerProvider>
<MessageScroller>
<MessageScrollerViewport>
<MessageScrollerContent>
{messages.map((message) => (
<MessageScrollerItem
key={message.id}
messageId={message.id}
scrollAnchor={message.role === "user"}
>
<Message />
</MessageScrollerItem>
))}
</MessageScrollerContent>
</MessageScrollerViewport>
<MessageScrollerButton />
</MessageScroller>
</MessageScrollerProvider>MessageScroller fills its parent — give that parent a height. scrollAnchor marks the start of a turn.
Composition
Anchoring
scrollAnchor pins that row near the top and leaves a peek of the previous turn.
Group Chat
Anchor a marker the same way — a join event can start the turn, not just a user message.
Previous Context
scrollPreviousItemPeek keeps pixels of the last row visible above the new anchor.
AI SDK
A scripted conversation through @shadcn/helpers/ai-sdk and useChat. No model, no API key — press send to stream the next turn.
TanStack AI
Same transcript through @shadcn/helpers/tanstack-ai. Same scroller, different chat hook.
Streaming
autoScroll follows tokens only while the reader sits at the live edge.
Opening Position
defaultScrollPosition="last-anchor" reopens a saved thread on the last meaningful turn.
Load History
Prepend older rows; preserveScrollOnPrepend keeps the visible message still.
Animation
Animate transform and opacity on MessageScrollerItem — not height, margin, or padding.
Commands
useMessageScroller jumps from a menu or permalink, even outside the frame.
Visibility
useMessageScrollerVisibility reports the current anchor and what is on screen.
Scrollable
useMessageScrollerScrollable tells you which edges still have overflow.
API
Prop
Type
These props live on MessageScrollerProvider. MessageScroller is the styled frame and takes className.
MessageScrollerItem
Prop
Type
MessageScrollerViewport
Prop
Type
MessageScrollerButton
Prop
Type
MessageScrollerContent is a role="log" live region. Pass aria-busy while a turn streams. Hooks — useMessageScroller, useMessageScrollerVisibility, useMessageScrollerScrollable — read from the provider, so they work outside the frame.
Keyboard
| Key | Action |
|---|---|
Tab | Focus the viewport |
Arrow / Page / Home / End / Space | Scroll; also releases auto-follow |
Enter / Space on the button | Jump to the latest message |
Notes
The frame fills its parent
Give the parent a height — h-[350px], flex-1 in a column, or h-dvh. An unconstrained parent grows the page.
Wrap every direct child of MessageScrollerContent in MessageScrollerItem. Parts throw without the provider.
With other components
Rows in the transcript
BubbleSurfaces in each row
MarkerJoin event as an anchor
AttachmentFiles inside a turn
ButtonJump to latest
CommandJump menu from visibility
Questions
Related
Was this page helpful?
Sign in to leave feedback.
Message
React message for chat rows in a thread or assistant. Built with TypeScript and Tailwind CSS for Next.js using Base UI — avatar, header, footer, start or end.
Native Select
React native select for mobile pickers and HTML forms. Built with TypeScript and Tailwind CSS for Next.js using Base UI — option groups and validation states.