← Back to all snippets
JAVASCRIPT

Tracking Scroll Position with `useScrollPosition`

A custom React hook to track the current vertical and horizontal scroll position of the window or a specified element, useful for scroll-based UI effects.

import { useState, useEffect, useRef } from 'react';

const getScrollPosition = (element) => {
  if (!element || element === window) {
    return { x: window.scrollX, y: window.scrollY };
  }
  return { x: element.scrollLeft, y: element.scrollTop };
};

export const useScrollPosition = (
  elementRef = null, // Can be window or a ref to a DOM element
  wait = 100 // Throttling delay
) => {
  const [scrollPosition, setScrollPosition] = useState(getScrollPosition(elementRef ? elementRef.current : window));
  const timeoutRef = useRef(null);
  const lastScrollRef = useRef(0);

  useEffect(() => {
    const currentElement = elementRef ? elementRef.current : window;
    if (!currentElement) return;

    const handleScroll = () => {
      const now = Date.now();
      const lastExecution = lastScrollRef.current;

      if (now - lastExecution > wait) {
        setScrollPosition(getScrollPosition(currentElement));
        lastScrollRef.current = now;
      } else {
        if (timeoutRef.current) {
          clearTimeout(timeoutRef.current);
        }
        timeoutRef.current = setTimeout(() => {
          setScrollPosition(getScrollPosition(currentElement));
          lastScrollRef.current = Date.now();
          timeoutRef.current = null;
        }, wait - (now - lastExecution));
      }
    };

    currentElement.addEventListener('scroll', handleScroll);

    return () => {
      currentElement.removeEventListener('scroll', handleScroll);
      if (timeoutRef.current) {
        clearTimeout(timeoutRef.current);
      }
    };
  }, [elementRef, wait]);

  return scrollPosition;
};
How it works: The `useScrollPosition` hook tracks the scroll position (x, y coordinates) of the window or a specific DOM element. It takes an optional `elementRef` (for a DOM element) and a `wait` time for throttling. This hook efficiently updates the scroll position state by ensuring the update function is called at most once within the specified `wait` period, preventing excessive re-renders during scrolling. It's ideal for dynamic headers, scroll-to-top buttons, or lazy loading components.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs