Vize

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

Connect native scrolling, writable vertical offset, edge state, and keyboard controls to one bounded guide preview.

Open the interactive example

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
Navigate a scrollable preparation guide: initial state
Initial state
Navigate a scrollable preparation guide: state after the verified interactions
After the verified interactions

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.