useScroll
Tracks the scroll position, scroll direction, arrived state (top/bottom/left/right), and scrolling status of any scrollable target — HTMLElement, Document, or Window — as reactive Observable values.
Scroll inside the box to see x, y, arrivedState, and directions update.
import { const useScroll: (element: MaybeEventTarget<Element | Document | Window>, options?: DeepMaybeObservable<UseScrollOptions>) => UseScrollReturn
useScroll } from "@usels/web";import { const useRef$: { <T = any>(initialValue: null): Ref$<T | null>; <T = any>(initialValue: T): Ref$<T>; <T = any>(): Ref$<T | null>;}
useRef$ } from "@usels/core";
function function Component(): JSX.Element
Component() { const const el$: Ref$<HTMLDivElement | null>
el$ = useRef$<HTMLDivElement>(): Ref$<HTMLDivElement | null> (+2 overloads)
Core (framework-agnostic) version of useRef$.
Creates an observable element ref with opaque wrapping.
- non-null value →
Ref$<T>: current, get(), peek() return T
- null / no arg →
Ref$<T | null>: current, get(), peek() return T | null
Nullability is expressed via the type parameter, mirroring T | null at the call site.
useRef$<interface HTMLDivElement
HTMLDivElement>(); const { const x$: any
x$, const y$: any
y$, const arrivedState$: any
arrivedState$ } = function useScroll(element: MaybeEventTarget<Element | Document | Window>, options?: DeepMaybeObservable<UseScrollOptions>): UseScrollReturn
Framework-agnostic reactive scroll tracker. Monitors scroll events on the
target (Element, Document, Window, Ref$, or Observable) and
exposes reactive position, direction, arrived-state, and scrolling status.
useScroll(const el$: Ref$<HTMLDivElement | null>
el$);
return ( <JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div RefAttributes<HTMLDivElement>.ref?: Ref<HTMLDivElement> | undefined
Allows getting a ref to the component instance.
Once the component unmounts, React will set ref.current to null
(or call the ref with null if you passed a callback ref).
ref={const el$: Ref$<HTMLDivElement | null>
el$} HTMLAttributes<HTMLDivElement>.style?: CSSProperties | undefined
style={{ StandardShorthandProperties<string | number, string & {}>.overflow?: Property.Overflow | undefined
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
Syntax: [ visible | hidden | clip | scroll | auto ]{1,2}
Initial value: visible
| Chrome | Firefox | Safari | Edge | IE |
| :----: | :-----: | :----: | :----: | :---: |
| 1 | 1 | 1 | 12 | 4 |
overflow: "auto", StandardLonghandProperties<string | number, string & {}>.height?: Property.Height<string | number> | undefined
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
Syntax: auto | <length-percentage [0,∞]> | min-content | max-content | fit-content | fit-content(<length-percentage [0,∞]>) | <calc-size()> | <anchor-size()>
Initial value: auto
| Chrome | Firefox | Safari | Edge | IE |
| :----: | :-----: | :----: | :----: | :---: |
| 1 | 1 | 1 | 12 | 4 |
height: 300 }}> <JSX.IntrinsicElements.p: DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p> scrollX: {const x$: any
x$.any
get()}, scrollY: {const y$: any
y$.any
get()} {const arrivedState$: any
arrivedState$.any
bottom.any
get() && " — reached bottom"} </JSX.IntrinsicElements.p: DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p> </JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div> );}import { createScroll } from "@usels/web";import { createRef$ } from "@usels/core";
function Component() { "use scope" const el$ = createRef$<HTMLDivElement>(); const { x$, y$, arrivedState$ } = createScroll(el$);
return ( <div ref={el$} style={{ overflow: "auto", height: 300 }}> <p> scrollX: {x$.get()}, scrollY: {y$.get()} {arrivedState$.bottom.get() && " — reached bottom"} </p> </div> );}Window scroll
Section titled “Window scroll”Use useWindowScroll for the common case, or pass window directly.
import { useScroll } from "@usels/web";
function Component() { const { y$, arrivedState$, isScrolling$ } = useScroll(window);}Arrived state with offset
Section titled “Arrived state with offset”Use offset to declare a threshold (in px) before the edge is considered “arrived”.
const { arrivedState$ } = useScroll(el$, { offset: { bottom: 100 }, // bottom=true when within 100px of the end});isScrolling + onStop
Section titled “isScrolling + onStop”const { isScrolling$ } = useScroll(el$, { idle: 300, // ms to wait before isScrolling becomes false (default: 200) onStop: () => { // called when scrolling stops },});Throttle
Section titled “Throttle”const { x$, y$ } = useScroll(el$, { throttle: 50 }); // handler fires at most once per 50msManual re-measure
Section titled “Manual re-measure”const { y$, measure } = useScroll(el$);
// Call measure() to force-sync scroll state without a scroll eventmeasure();Reactive options
Section titled “Reactive options”Options can be passed as plain values, per-field Observables, or a single Observable<UseScrollOptions>. Changes are picked up reactively.
import { observable } from "@usels/core";
const idle$ = observable(200);const { isScrolling$ } = useScroll(el$, { idle: idle$ });
// Later: update idle time reactivelyidle$.set(500);Reactive observables, not state. All returned values (x$, y$, isScrolling$, arrivedState$, directions$) are Legend-State Observables. Read them with .get() inside a reactive context to avoid unnecessary re-renders.
measure() is synchronous. It immediately reads the current scroll values from the DOM and updates all observables. Useful after programmatic scroll operations.
arrivedState initial values. On mount, top and left default to true, and bottom/right default to false. After the first measure() call (triggered automatically on mount), all values are synced with actual DOM state.
options is DeepMaybeObservable. Each option field can be a plain value or an Observable. Callback options (onScroll, onStop, onError) are passed as plain functions.
Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
element | MaybeEventTarget<Element | Document | Window> | - |
options | UseScrollOptions (optional) | - |
UseScrollOptions
Section titled “UseScrollOptions”Returns
Section titled “Returns”UseScrollReturn
| Name | Type | Description |
|---|---|---|
x$ | ObservablePrimitive<number> | - |
y$ | ObservablePrimitive<number> | - |
isScrolling$ | ObservableBoolean<boolean> | - |
arrivedState$ | Observable<ArrivedState> | - |
directions$ | Observable<ScrollDirections> | - |
measure | () => void | - |