Fluxo UIFluxo UIv0.4.93

Sticky Scroll

A scroll container that auto-follows new content while the user is at the bottom, but holds its position the moment they scroll up to read history — chat logs, activity feeds, build output, terminals.

Basic Usage

Live activity feed

New rows stream in every 1.8s. While you are at the bottom the view auto-follows. Scroll up to read an older row and it stays put — a 'jump to latest' button appears so you can re-pin.

#1Agent connected to the bound tab.
#2Reading page info — title, URL, ready state.
#3Clicking the "New invoice" button.
#4Filling customer name with "Acme Corp".
import { StickyScroll } from 'fluxo-ui';

<StickyScroll maxHeight={320}>
    {messages.map((m) => (
        <div key={m.id}>{m.text}</div>
    ))}
</StickyScroll>

Labelled Button

Labelled jump button

Pass jumpButtonLabel to render a pill with text instead of the icon-only chevron. It only shows while the user has scrolled away from the bottom.

line 1: compiled module successfully
line 2: compiled module successfully
line 3: compiled module successfully
line 4: compiled module successfully
line 5: compiled module successfully
line 6: compiled module successfully
<StickyScroll maxHeight={300} jumpButtonLabel="Jump to latest">
    {rows}
</StickyScroll>

Imperative & State

Imperative handle & pinned state

Call scrollToBottom() via a ref, and observe pinned state through onPinnedChange. Useful for a custom 'send' button that always re-pins.

Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Item 7
Item 8
Item 9
Item 10
Item 11
Item 12
Item 13
Item 14
Item 15
Item 16
Item 17
Item 18
Item 19
Item 20
pinned: true
const ref = useRef<StickyScrollHandle>(null);

<StickyScroll ref={ref} maxHeight={300} onPinnedChange={setPinned}>
    {rows}
</StickyScroll>

<Button label="Scroll to bottom" onClick={() => ref.current?.scrollToBottom()} />

Import

import { StickyScroll } from 'fluxo-ui';
import type { StickyScrollProps, StickyScrollHandle, StickyScrollBehavior } from 'fluxo-ui';

Props

childrenreq
ReactNode

Scrollable content. New items appended at the end trigger auto-scroll when pinned

threshold
number"48"

Distance in px from the bottom within which the view is considered pinned

initialPinned
boolean"true"

Start scrolled to the bottom and pinned on mount

behavior
'smooth' | 'auto'"'smooth'"

Scroll behavior for the jump button; auto-falls back under prefers-reduced-motion

showJumpButton
boolean"true"

Show a 'jump to latest' button while the user is scrolled up

jumpButtonLabel
string

Optional visible text on the jump button (icon-only when omitted)

maxHeight
number | string

Max height of the scroll viewport (number is px)

height
number | string

Fixed height of the scroll viewport (number is px)

onPinnedChange
(pinned: boolean) => void

Fires when the pinned-to-bottom state changes

className
string

Additional CSS classes on the outer wrapper

contentClassName
string

Additional CSS classes on the inner content element

id
string

HTML id attribute on the scroll viewport

ariaLabel
string

Accessible label for the scroll viewport

role
string

ARIA role for the scroll viewport (e.g. 'log' for live activity feeds)

Features

Smart Auto-Follow

Appends auto-scroll the view only while the user is already at the bottom — never yanks them back when reading older content.

Jump To Latest

A floating button appears the moment the user scrolls up, letting them re-pin to the newest content with one click.

Resize-Aware

A ResizeObserver keeps the view pinned even when items grow in height, not just when new ones are added.

Imperative Handle

scrollToBottom(), isPinned(), and getElement() via ref for custom controls and integrations.

Reduced Motion

Falls back to instant scroll under prefers-reduced-motion and dampens the button animation.

Accessible

Keyboard-focusable jump button with aria-label, an aria-hidden state when pinned, and an optional role like log for live regions.