Message Scroller

A reader-aware conversation viewport that follows streamed output at the live edge and releases control when the reader moves away.

Preview

What should the first release include?
Start with the smallest workflow that still feels complete.
Include streaming and recovery states too.
Yes. Those states make the first version feel dependable.
How should we present tool results?
Keep results close to the action that produced them.
What about actions that need confirmation?
Pause the run, explain the impact, and ask before continuing.
Can the transcript stay easy to navigate?
Use the rail to jump between turns without losing your place.

Install

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

$ bunx --bun shadcn add @beui/message-scroller

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>, "className" | "children" | "ref">
contentProps?
Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "className" | "children" | "ref">
className?
string

Composition

Keep the scrolling viewport outside the message primitives it follows.

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

Note: Message provides stable, semantic rows for the transcript. Prompt Input starts new turns without owning transcript movement. Streaming Response supplies the growing content the viewport follows.

How it works

A streaming transcript is not ordinary overflow. It must follow new output while the reader stays at the live edge, then stop moving the moment they choose to inspect earlier work.

Updated