useContainerScroll

Layout/Viewport

A React hook that tracks scroll position and dimensions of a container element, perfect for custom scrollbars and scroll-based interactions.

Demo

Try scrolling within the container below to see the scroll metrics update in real-time:

Scroll Position
Top: 0px
Left: 0px
Container Size
Client Width: 0px
Client Height: 0px
Content Size
Scroll Width: 0px
Scroll Height: 0px
Scrollable Content
Section 1
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 2
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 3
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 4
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 5
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 6
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 7
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 8
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 9
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 10
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 11
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 12
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 13
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 14
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 15
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 16
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 17
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 18
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 19
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Section 20
Scroll around to see the metrics update. This content is intentionally large to demonstrate both vertical and horizontal scrolling.
Scroll State
Scroll ended

Installation

npm install @thibault.sh/hooks

Usage

import { useContainerScroll } from '@thibault.sh/hooks/useContainerScroll';
import { useRef } from 'react';

function CustomScrollbar() {
  const containerRef = useRef<HTMLDivElement>(null);
  const {
    scrollTop,
    scrollHeight,
    clientHeight,
    isScrolling
  } = useContainerScroll(containerRef);

  const scrollbarHeight = (clientHeight / scrollHeight) * 100;
  const scrollbarTop = (scrollTop / (scrollHeight - clientHeight)) * 100;

  return (
    <div className="relative">
      <div
        ref={containerRef}
        className="h-[400px] overflow-y-auto pr-4"
      >
        {/* Content */}
      </div>
      
      <div className="absolute top-0 right-0 w-2 h-full bg-gray-100">
        <div
          className="absolute bg-gray-400 w-full rounded transition-all"
          style={{
            height: `${scrollbarHeight}%`,
            top: `${scrollbarTop}%`,
            opacity: isScrolling ? 0.8 : 0.4
          }}
        />
      </div>
    </div>
  );
}

API

Returns

Object containing scroll position and dimension information

Features

  • Real-time Updates

    Automatically updates when container scroll position or dimensions change

  • Scroll State Detection

    Tracks when scrolling starts and ends with configurable delay

  • Comprehensive Metrics

    Provides scroll position, content size, and viewport dimensions

  • Performance Optimized

    Uses efficient scroll event handling and cleanup

Custom Scrollbar Example

import { useContainerScroll } from '@thibault.sh/hooks/useContainerScroll';
import { useRef } from 'react';

function CustomScrollbar() {
  const containerRef = useRef<HTMLDivElement>(null);
  const {
    scrollTop,
    scrollHeight,
    clientHeight,
    isScrolling
  } = useContainerScroll(containerRef, 500);

  return (
    <div className="relative">
      <div
        ref={containerRef}
        className="h-[400px] overflow-y-auto hide-scrollbar"
      >
        {/* Content */}
      </div>
      
      <div
        className="absolute right-2 top-1/2 -translate-y-1/2
                   h-1/2 w-1 bg-gray-200 rounded-full"
      >
        <div
          className="absolute w-full rounded-full bg-gray-600
                     transition-all duration-200"
          style={{
            height: `${(clientHeight / scrollHeight) * 100}%`,
            top: `${(scrollTop / (scrollHeight - clientHeight)) * 100}%`,
            opacity: isScrolling ? 0.8 : 0.4
          }}
        />
      </div>
    </div>
  );
}

Scroll Progress Example

import { useContainerScroll } from '@thibault.sh/hooks/useContainerScroll';
import { useRef } from 'react';

function ScrollProgress() {
  const containerRef = useRef<HTMLDivElement>(null);
  const { scrollTop, scrollHeight, clientHeight } = useContainerScroll(containerRef);

  const progress = Math.min(
    (scrollTop / (scrollHeight - clientHeight)) * 100,
    100
  );

  return (
    <div className="relative">
      <div className="absolute top-0 left-0 w-full h-1 bg-gray-100">
        <div
          className="h-full bg-orange-500 transition-all"
          style={{ width: `${progress}%` }}
        />
      </div>

      <div ref={containerRef} className="h-[500px] overflow-auto pt-4">
        {/* Content */}
      </div>
    </div>
  );
}

Infinite Scroll Example

import { useContainerScroll } from '@thibault.sh/hooks/useContainerScroll';
import { useRef, useEffect } from 'react';

function InfiniteScroll({ onLoadMore }: { onLoadMore: () => void }) {
  const containerRef = useRef<HTMLDivElement>(null);
  const {
    scrollTop,
    scrollHeight,
    clientHeight,
    isScrolling
  } = useContainerScroll(containerRef);

  useEffect(() => {
    const remainingScroll = scrollHeight - clientHeight - scrollTop;
    
    // Load more when user is near bottom
    if (remainingScroll < 200) {
      onLoadMore();
    }
  }, [scrollTop, scrollHeight, clientHeight, onLoadMore]);

  return (
    <div ref={containerRef} className="h-[600px] overflow-auto">
      {/* Content */}
      {isScrolling && (
        <div className="sticky bottom-0 p-2 text-center bg-white/80">
          Scrolling...
        </div>
      )}
    </div>
  );
}