scroll
Track the scroll offset, edges, and direction of an element or window.
| Package | @vizejs/composable/scroll |
| Own the source | vize lib pull composable:scroll |
| Runtime exports | useScroll, useWindowScroll |
| Gzip budget | 3072 B |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/composable
Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.
import { useScroll, useWindowScroll } from "@vizejs/composable/scroll";
Try it
Navigate a scrollable preparation guide
Connect native scrolling, writable vertical offset, edge state, and keyboard controls to one bounded guide preview.
What to observe: Scroll the read-only preparation preview, or activate Next guide section. Its actual vertical offset changes and Previous becomes available. Continue to the end: Next disables when the element reaches its bottom. Return to the beginning resets the offset and disables Previous. The surrounding document is not the observed target.
useScroll receives the real HTMLTextAreaElement template ref. Assigning y and calling scrollTo move that element; its real client and scroll dimensions determine arrivedState. Native scroll events update activity, which settles on scrollend or the 200 ms idle fallback. Read-only text still supports ordinary selection and keyboard scrolling. SSR starts at zero with the top/left edges arrived, no bottom/right arrival, and no browser listeners. Client attachment follows mounted state; scope disposal removes listeners and the idle timer. These controls navigate local content and do not request or save data.
Compare the initial and interacted states


Copy the example
Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.
<script setup lang="ts">
import { useId, useTemplateRef } from "vue";
import { useScroll } from "@vizejs/composable/scroll";
const previewId = useId();
const preview = useTemplateRef<HTMLTextAreaElement>("preview");
const guide = [
"Workshop preparation guide",
"",
"1. Install the Vue tooling in your existing project.",
"Keep the dependency changes separate from application changes.",
"",
"2. Compile a representative component.",
"Check its emitted behavior and its diagnostics.",
"",
"3. Review keyboard interaction.",
"Use the native controls before adding custom shortcuts.",
"",
"4. Check a narrow viewport.",
"Verify that content wraps and controls stay reachable.",
"",
"5. Publish the verified result.",
"Keep the source and the demonstrated behavior aligned.",
].join("\n");
const { y, arrivedState, isScrolling, scrollTo } = useScroll(preview);
</script>
<template>
<div class="composable-example">
<label :for="previewId">Preparation guide preview</label>
<textarea :id="previewId" ref="preview" class="guide-preview" :value="guide" readonly />
<div class="example-actions">
<button type="button" :disabled="arrivedState.top" @click="() => (y = y - 120)">
Previous guide section
</button>
<button type="button" :disabled="arrivedState.bottom" @click="() => (y = y + 120)">
Next guide section
</button>
<button type="button" @click="() => scrollTo({ top: 0 })">Return to the beginning</button>
</div>
<output aria-live="polite">
Vertical offset: {{ Math.round(y) }} pixels · At beginning:
{{ arrivedState.top ? "yes" : "no" }} · At end: {{ arrivedState.bottom ? "yes" : "no" }}
</output>
<p>Scrolling activity: {{ isScrolling ? "active" : "idle" }}</p>
<p>
Scroll the native read-only preview or use the buttons. Assigning the writable y ref scrolls
this element; edge flags follow its real dimensions and disable the corresponding controls.
This preview does not scroll the surrounding documentation page.
</p>
</div>
</template>
<style scoped>
.guide-preview {
box-sizing: border-box;
inline-size: 100%;
block-size: 140px;
resize: none;
padding: 12px;
line-height: 1.6;
}
</style>
Runtime contract
| Utility | Category | Stability | SSR | Hydration | Cleanup | Targets | Host globals | Uses |
|---|---|---|---|---|---|---|---|---|
useScroll |
dom | experimental | deterministic-fallback | stable | caller, reactive-scope | web, desktop | globalThis |
isElementNode, resolveElement, tryOnScopeDispose |
useWindowScroll |
dom | experimental | deterministic-fallback | stable | caller, reactive-scope | web, desktop | globalThis, window |
useScroll |
API
useScroll
Track the scroll offset, edges, and direction of an element or window. During server rendering (or while the target is unresolved) offsets are 0, the top/left edges report arrived, and the bottom/right edges do not — matching an unscrolled page, so hydration is stable. The scroll listener is passive and removed together with the idle timer when the owning reactive scope stops. Inside a component the host is read after mounting, so hydration renders the server fallback first and never mismatches.
function useScroll( target: MaybeRefOrGetter<ScrollTargetValue>, options: UseScrollOptions = {}, ): ScrollControls
useWindowScroll
Track the document scroll position. Equivalent to useScroll with the window as target, so it shares its SSR-stable defaults and scope-bound cleanup.
function useWindowScroll(options: UseWindowScrollOptions = {}): ScrollControls
Types
ScrollMetrics
Scroll extents shared by elements and the document root element.
| Member | Type | Description |
|---|---|---|
scrollWidth |
number |
Total scrollable width. |
scrollHeight |
number |
Total scrollable height. |
clientWidth |
number |
Visible width without scrollbars. |
clientHeight |
number |
Visible height without scrollbars. |
ScrollWindowHost
Window-like scroll container accepted by useScroll.
| Member | Type | Description |
|---|---|---|
scrollX |
number |
Horizontal document scroll offset. |
scrollY |
number |
Vertical document scroll offset. |
scrollTo |
(options: ScrollToOptions) => void |
Scroll the document. |
document |
{ readonly documentElement: ScrollMetrics } |
Document whose root element reports the scroll extents. |
ScrollEdges
Per-edge boolean state.
| Member | Type | Description |
|---|---|---|
left |
boolean |
Left edge (or inline start in LTR). |
right |
boolean |
Right edge. |
top |
boolean |
Top edge. |
bottom |
boolean |
Bottom edge. |
ScrollOffset
Per-edge distance thresholds in CSS pixels.
| Member | Type | Description |
|---|---|---|
left? |
number |
Distance from the left edge that still counts as arrived. |
right? |
number |
Distance from the right edge that still counts as arrived. |
top? |
number |
Distance from the top edge that still counts as arrived. |
bottom? |
number |
Distance from the bottom edge that still counts as arrived. |
UseScrollOptions
Options for useScroll.
| Member | Type | Description |
|---|---|---|
offset? |
ScrollOffset |
Edge tolerances used to compute arrivedState. |
idle? |
number |
Milliseconds without scroll events before isScrolling becomes false (used when the engine does not fire scrollend). |
behavior? |
MaybeRefOrGetter<ScrollBehavior> |
Scroll behavior used by scrollTo and the writable x/y refs. |
onScroll? |
(event: Event) => void |
Called for every scroll event after the state has been updated. |
onStop? |
(event: Event) => void |
Called once scrolling settles. |
scheduler? |
TimeoutScheduler |
Owns the idle timer. |
flush? |
"pre" | "post" | "sync" |
Target resolution timing. "post" observes template refs after mount. |
ScrollControls
Reactive scroll state returned by useScroll.
| Member | Type | Description |
|---|---|---|
x |
WritableComputedRef<number> |
Horizontal offset. Assigning scrolls the target with the configured behavior. |
y |
WritableComputedRef<number> |
Vertical offset. Assigning scrolls the target with the configured behavior. |
isScrolling |
Readonly<Ref<boolean>> |
Whether scroll events arrived within the idle window. |
arrivedState |
Readonly<ScrollEdges> |
Whether each edge has been reached (within offset). |
directions |
Readonly<ScrollEdges> |
Direction of the most recent scroll movement; reset when scrolling stops. |
measure |
() => void |
Re-read offsets and edges without waiting for a scroll event. |
scrollTo |
(options: { readonly left?: number; readonly top?: number }) => void |
Scroll the target. Missing axes keep their current offset. |
UseWindowScrollOptions
Options for useWindowScroll.
| Member | Type | Description |
|---|---|---|
offset? |
ScrollOffset |
Edge tolerances used to compute arrivedState. |
idle? |
number |
Milliseconds without scroll events before isScrolling becomes false (used when the engine does not fire scrollend). |
behavior? |
MaybeRefOrGetter<ScrollBehavior> |
Scroll behavior used by scrollTo and the writable x/y refs. |
onScroll? |
(event: Event) => void |
Called for every scroll event after the state has been updated. |
onStop? |
(event: Event) => void |
Called once scrolling settles. |
scheduler? |
TimeoutScheduler |
Owns the idle timer. |
flush? |
"pre" | "post" | "sync" |
Target resolution timing. "post" observes template refs after mount. |
host? |
MaybeRefOrGetter<ScrollWindowHost | null | undefined> |
Reactive window capability for alternate runtimes and tests. |