JAVASCRIPT

React to Scroll Position with useScrollPosition Hook

Implement a custom React hook to track and react to the user's scroll position within a component or the window, enabling scroll-based UI effects and logic.

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

/**
 * Custom React hook to get the current scroll position of the window or a ref element.
 *
 * @param {HTMLElement | null} elementRef - Optional ref to an HTML element to track its scroll.
 *                                          If null, tracks window scroll.
 * @returns {Object} An object containing { x, y } scroll coordinates.
 */
function useScrollPosition(elementRef = null) {
  const [scrollPosition, setScrollPosition] = useState({ x: 0, y: 0 });
  const ticking = useRef(false);

  const handleScroll = useCallback(() => {
    if (!ticking.current) {
      requestAnimationFrame(() => {
        let currentX, currentY;
        if (elementRef && elementRef.current) {
          currentX = elementRef.current.scrollLeft;
          currentY = elementRef.current.scrollTop;
        } else {
          currentX = window.scrollX;
          currentY = window.scrollY;
        }

        setScrollPosition({ x: currentX, y: currentY });
        ticking.current = false;
      });
      ticking.current = true;
    }
  }, [elementRef]);

  useEffect(() => {
    const target = elementRef && elementRef.current ? elementRef.current : window;
    target.addEventListener('scroll', handleScroll, { passive: true });

    // Initial read
    handleScroll();

    return () => {
      target.removeEventListener('scroll', handleScroll);
    };
  }, [elementRef, handleScroll]);

  return scrollPosition;
}

export default useScrollPosition;
How it works: The `useScrollPosition` hook provides an efficient and performant way to track scroll coordinates. It can monitor either the entire window's scroll or the scroll of a specific DOM element passed via a `ref`. To optimize performance, it debounces scroll events using `requestAnimationFrame`, ensuring that updates only happen once per frame. This hook is perfect for implementing scroll-triggered animations, sticky headers, or progress indicators that react to user scrolling.

Need help integrating this into your project?

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

Hire DigitalCodeLabs