A React hook that manages a custom horizontal scrollbar with thumb sizing, position syncing, drag interaction, edge-fade indicators, and RAF-throttled scroll handling — all driven directly against the DOM for performance. ## Key Components **`useHorizontalScrollbar()`** — the single export; returns everything needed to wire up a scrollable container and its custom scrollbar UI. ### Returned Values | Name | Type | Description | |---|---|---| | `scrollRef` | callback ref | Attach to the scrollable container; sets up `ResizeObserver` + `MutationObserver` automatically | | `trackRef` | `RefObject` | Attach to the scrollbar track element | | `thumbRef` | `RefObject` | Attach to the scrollbar thumb element | | `thumbRatio` | `number` | Thumb width as a fraction of track width (`0` when no overflow) | | `canScrollLeft` | `boolean` | `true` when content is scrolled past the left edge | | `canScrollRight` | `boolean` | `true` when more content exists to the right | | `onScroll` | handler | RAF-throttled; attach to the container's `onScroll` | | `onTrackClick` | handler | Scrolls to clicked position on the track | | `onTrackWheel` | handler | Forwards wheel events from the track to the scroll container | | `onThumbPointerDown/Move/Up` | handlers | Full pointer-capture drag implementation | ## Usage Example ```typescript import { useHorizontalScrollbar } from './use-horizontal-scrollbar' function ScrollableTable() { const { scrollRef, trackRef, thumbRef, thumbRatio, canScrollLeft, canScrollRight, onScroll, onTrackClick, onTrackWheel, onThumbPointerDown, onThumbPointerMove, onThumbPointerUp, } = useHorizontalScrollbar() return (
{canScrollLeft &&
}
{/* wide content */}
{canScrollRight &&
} {thumbRatio > 0 && thumbRatio < 1 && (
)}
) } ``` ## Notes - `scrollRef` is a **callback ref**, so observer setup runs correctly even when the container mounts asynchronously (e.g. after data loads). - Thumb position is updated **directly on the DOM** (`thumb.style.left`) during scroll and drag to avoid React re-render overhead. - `ResizeObserver` watches both the container and its children; a `MutationObserver` re-observes when children change, keeping thumb size accurate after dynamic content updates.