Message Scroller
Follows new messages until the reader scrolls up.
Installation
pnpm dlx shadcn@latest add @paneui/message-scrollerUsage
Examples
Keyboard
The viewport is focusable. ArrowUp, PageUp, Home and Shift+Space release it from the live edge; End jumps back to the latest message.
Motion
Under reduced motion the viewport jumps instead of gliding to the bottom, and the pill fades instead of blurring and lifting.
MessageScroller
A conversation viewport pinned to the live edge while output streams. Also accepts every prop of div.
- threshold= 48
- number
- Distance from the bottom (px) still treated as at the live edge.
- startAtEnd= true
- boolean
- Start scrolled to the bottom on mount.
- follow= true
- boolean
- Follow new content while at the live edge.
- showJumpButton= true
- boolean
- Show the floating jump-to-latest pill when released.
- jumpButton= "pill"
- "pill" | "icon"
iconrenders a round arrow-only button (label kept for screen readers), as in desktop chat apps.- jumpLabel= "Jump to latest"
- string
- Pill label when nothing new is below.
- newLabel= "New messages"
- string
- Pill label once content arrived below the reader.
- viewportClassName
- string
- Classes for the scrolling viewport.
- contentClassName
- string
- Classes for the content column inside the viewport (default
flex flex-col p-4). - onLiveEdgeChange
- (atLiveEdge: boolean) => void
- Called when the viewport attaches to or releases the live edge.
- footer
- ReactNode
- Rendered below the viewport, outside the scrolling content (a composer, a jump bar). Can call
useMessageScroller(). - overlay
- ReactNode
- Rendered over the viewport, outside the scrolling content, in a
relativeframe; position it yourself. Can calluseMessageScroller().
| Prop | Type | Default | Description |
|---|---|---|---|
| threshold | number | 48 | Distance from the bottom (px) still treated as at the live edge. |
| startAtEnd | boolean | true | Start scrolled to the bottom on mount. |
| follow | boolean | true | Follow new content while at the live edge. |
| showJumpButton | boolean | true | Show the floating jump-to-latest pill when released. |
| jumpButton | "pill" | "icon" | "pill" | icon renders a round arrow-only button (label kept for screen readers), as in desktop chat apps. |
| jumpLabel | string | "Jump to latest" | Pill label when nothing new is below. |
| newLabel | string | "New messages" | Pill label once content arrived below the reader. |
| viewportClassName | string | — | Classes for the scrolling viewport. |
| contentClassName | string | — | Classes for the content column inside the viewport (default flex flex-col p-4). |
| onLiveEdgeChange | (atLiveEdge: boolean) => void | — | Called when the viewport attaches to or releases the live edge. |
| footer | ReactNode | — | Rendered below the viewport, outside the scrolling content (a composer, a jump bar). Can call useMessageScroller(). |
| overlay | ReactNode | — | Rendered over the viewport, outside the scrolling content, in a relative frame; position it yourself. Can call useMessageScroller(). |
- data-live-edge
- On the root while pinned to the newest content.
- data-new
- On the jump pill once new content arrived below.
| Data attribute | Description |
|---|---|
| data-live-edge | On the root while pinned to the newest content. |
| data-new | On the jump pill once new content arrived below. |
useMessageScroller
Reads the nearest MessageScroller from its children, footer or overlay. Throws outside one.
- atLiveEdge
- boolean
- Returned. True while pinned to the newest content.
- hasNew
- boolean
- Returned. True once content arrived below a reader who scrolled up.
- scrollToLatest
- (behavior?: ScrollBehavior) => void
- Returned. Scroll to the newest content and re-attach;
"smooth"by default.
| Prop | Type | Default | Description |
|---|---|---|---|
| atLiveEdge | boolean | — | Returned. True while pinned to the newest content. |
| hasNew | boolean | — | Returned. True once content arrived below a reader who scrolled up. |
| scrollToLatest | (behavior?: ScrollBehavior) => void | — | Returned. Scroll to the newest content and re-attach; "smooth" by default. |