useScrollPosition
A hook to track scroll position, direction, progress, and boundary detection.
The useScrollPosition hook tracks the scroll position of the window or a specific element, providing direction, progress percentage, and boundary detection — all throttled for performance.
Demo
Scroll Position
Scroll Y0px
Progress0%
Direction— Idle
Position🔝 Top
Scroll this page to see values update in real-time.
Source Code
Copy this code into src/hooks/useScrollPosition.ts:
import { useCallback, useEffect, useRef, useState } from 'react';
export interface ScrollPosition {
x: number;
y: number;
directionY: 'up' | 'down' | null;
directionX: 'left' | 'right' | null;
isAtTop: boolean;
isAtBottom: boolean;
progress: number; // 0–100
}
export const useScrollPosition = (options?: {
throttle?: number;
element?: React.RefObject<HTMLElement | null>;
}): ScrollPosition => {
const { throttle: throttleMs = 50, element } = options ?? {};
const [position, setPosition] = useState<ScrollPosition>({
x: 0,
y: 0,
directionY: null,
directionX: null,
isAtTop: true,
isAtBottom: false,
progress: 0,
});
// ... throttled scroll handler with rAF
// See full source for implementation
return position;
};Usage
import { useScrollPosition } from '@/hooks/useScrollPosition';
const ScrollIndicator = () => {
const { progress, directionY, isAtBottom } = useScrollPosition();
return (
<div>
<div style={{ width: `${progress}%` }} className="h-1 bg-blue-500" />
{isAtBottom && <p>You've reached the bottom!</p>}
</div>
);
};API Reference
Options
| Property | Type | Default | Description |
|---|---|---|---|
throttle | number | 50 | Throttle interval in milliseconds |
element | RefObject<HTMLElement> | window | Element to track scroll on |
Return Value
| Property | Type | Description |
|---|---|---|
x | number | Horizontal scroll position in pixels |
y | number | Vertical scroll position in pixels |
directionY | "up" | "down" | null | Vertical scroll direction |
directionX | "left" | "right" | null | Horizontal scroll direction |
isAtTop | boolean | Whether scrolled to the top |
isAtBottom | boolean | Whether scrolled to the bottom |
progress | number | Scroll progress from 0 to 100 |