30% offEnding soon
useScrollDirectionLoading saved progress…

useScrollDirection

useScrollDirection reports whether the user is currently scrolling up or down as React state, returning null until they have scrolled. The browser's scroll event only tells you the current position, so direction is a comparison against where you were a moment ago — and because raw scrolling jitters by a pixel or two, you ignore movements below a small threshold so the reported value stays stable. The classic use is a sticky header that hides on scroll-down and reappears on scroll-up.

Signature

type ScrollDirection = 'up' | 'down' | null;

function useScrollDirection(options?: { threshold?: number }): ScrollDirection;
// null before the first real scroll; then 'up' or 'down'.
// threshold: pixels of movement to ignore as jitter (default 6).

Examples

// Hide a sticky header on the way down, show it on the way up.
const direction = useScrollDirection();
const hidden = direction === 'down';
return <header className={hidden ? 'header header--hidden' : 'header'}>...</header>;
// Momentum scrolling on a trackpad is noisy — widen the dead zone.
const direction = useScrollDirection({ threshold: 12 });

Notes

  • Source — read window.scrollY (the vertical offset). This hook reports vertical direction only.
  • Compare against the latest — each scroll must be compared to the most recent position, not to a value captured once when you first subscribed.
  • Threshold — ignore moves smaller than threshold pixels so trackpad wobble and momentum drift do not flip the value. Below the threshold, keep the last reported direction.
  • Change only on a flip — the returned value should change (and re-render) only when the direction actually reverses, not on every scroll event.
  • Passive + cleanup — attach the scroll listener as { passive: true } (you never call preventDefault) and remove it on unmount.
  • Do not worry about — horizontal direction or server rendering beyond returning null on the first render.