Message Scroller

A scroll container for chat transcripts that stays pinned to the newest streamed text only while the reader sits near the bottom, stops following once they scroll up, and can add a rail for jumping between messages.

Preview

Message Scroller preview width

Install

Add it with the shadcn CLI, or copy the source manually.

$ bunx --bun shadcn add @karaui/message-scroller
@karaui not found? Add it to components.json
{
  "registries": {
    "@karaui": "https://karaui.kamisama-v0.my.id/r/{name}.json"
  }
}

@karaui is in shadcn's public registry directory, so the latest CLI needs no setup. On an older CLI, merge this into the existing file once and every @karaui/… install resolves.

API Reference

followOutput?
boolean

Keep streamed output pinned while the reader remains near the end.

true
followThreshold?
number

Distance from the end that still counts as following the output.

56
smooth?
boolean

Smoothly follow growing content.

true
onFollowChange?
((following: boolean) => void)

Reports when the reader leaves or returns to the live edge.

—
label?
string

Accessible label for the scrollable transcript.

Conversation
busy?
boolean

Marks the transcript as waiting for more streamed content.

—
navigation?
"rail"

Adds a compact rail for navigating between rendered Message rows.

—
navigationLabel?
string

Accessible label for the optional message navigation rail.

Message navigation
viewportClassName?
string
—
contentClassName?
string
—
railClassName?
string
—
viewportRef?
Ref<HTMLElement>
—
viewportProps?
Omit<DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>, "ref" | "children" | "className">
—
contentProps?
Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref" | "children" | "className">
—
className?
string
—

Composition

Wrap the message rows with the scroller instead of building scrolling into the rows.

MessageScroller
└── MessageGroup
    └── Message
        └── MessageContent

Note: Message gives the transcript consistent, labelled rows. Prompt Input starts the next turn but leaves scrolling to the scroller. Streaming Response produces the growing text that the view keeps in sight.

How it works

Streaming chat breaks the usual rules of overflow. While someone is reading the newest line, the view should keep up with it; once they scroll back to check something, it must stop pulling them down.

Updated