StorybookGitHub

Message Scroller

Auto-scrolling container for chat message lists with jump-to controls.

Assistant: message 1 — spacing, tokens, and chat layout notes.
You: message 2 — continue with the summary, please.
Assistant: message 3 — spacing, tokens, and chat layout notes.
You: message 4 — continue with the summary, please.
Assistant: message 5 — spacing, tokens, and chat layout notes.
You: message 6 — continue with the summary, please.
Assistant: message 7 — spacing, tokens, and chat layout notes.
You: message 8 — continue with the summary, please.
Assistant: message 9 — spacing, tokens, and chat layout notes.
You: message 10 — continue with the summary, please.
Assistant: message 11 — spacing, tokens, and chat layout notes.
You: message 12 — continue with the summary, please.
Assistant: message 13 — spacing, tokens, and chat layout notes.
You: message 14 — continue with the summary, please.
Assistant: message 15 — spacing, tokens, and chat layout notes.
You: message 16 — continue with the summary, please.
Assistant: message 17 — spacing, tokens, and chat layout notes.
You: message 18 — continue with the summary, please.
Assistant: message 19 — spacing, tokens, and chat layout notes.
You: message 20 — continue with the summary, please.
Assistant: message 21 — spacing, tokens, and chat layout notes.
You: message 22 — continue with the summary, please.
Assistant: message 23 — spacing, tokens, and chat layout notes.
You: message 24 — continue with the summary, please.

Installation

npx shadcn@latest add https://reactdocs.canceydejean.dev/r/message-scroller.json

Usage

Use for conversation threads that stay pinned to the latest message and offer a control to jump back. Built on @shadcn/react/message-scroller. Viewport uses scroll-fade-b from the scroll-fade utility.

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

export function Example({
  messages,
}: {
  messages: { id: string; text: string }[];
}) {
  return (
    <MessageScrollerProvider>
      <MessageScroller className="h-96">
        <MessageScrollerViewport>
          <MessageScrollerContent>
            {messages.map((message, index) => (
              <MessageScrollerItem
                key={message.id}
                scrollAnchor={index === messages.length - 1}
              >
                {message.text}
              </MessageScrollerItem>
            ))}
          </MessageScrollerContent>
        </MessageScrollerViewport>
        <MessageScrollerButton />
      </MessageScroller>
    </MessageScrollerProvider>
  );
}

Mark the latest row with scrollAnchor so new content sticks to the end. Use hooks useMessageScroller, useMessageScrollerScrollable, and useMessageScrollerVisibility for custom controls.