useScrollShadow
Track whether a scroll container has hidden content past its start/end edges, so you can fade in shadows on the overflowing sides only.
Installation
useScrollShadow watches a scroll container and reports whether content is hidden past its start and end edges. Use it to fade in edge shadows (or arrows) only on the sides that actually overflow. It re-reads scroll geometry on scroll and whenever the container or its children resize, and only re-renders when an edge actually flips — not on every scroll frame.
For vertical orientation, start/end map to top/bottom; for horizontal, to left/right.
Horizontal mode assumes LTR layout (scrollLeft grows from 0 at the start edge). RTL is not handled.
Usage
import { useRef } from "react";
import { useScrollShadow } from "@/hooks/use-scroll-shadow";
import { cn } from "@/lib/utils";
function ShadowedList() {
const ref = useRef<HTMLDivElement>(null);
const { start, end } = useScrollShadow(ref);
return (
<div className="relative">
<div
className={cn(
"pointer-events-none absolute inset-x-0 top-0 h-4 bg-gradient-to-b from-background to-transparent transition-opacity",
start ? "opacity-100" : "opacity-0"
)}
/>
<div ref={ref} className="max-h-64 overflow-y-auto">
{/* long content */}
</div>
<div
className={cn(
"pointer-events-none absolute inset-x-0 bottom-0 h-4 bg-gradient-to-t from-background to-transparent transition-opacity",
end ? "opacity-100" : "opacity-0"
)}
/>
</div>
);
}API
useScrollShadow<T>(ref, enabled?, orientation?)
| Parameter | Type | Default | Description |
|---|---|---|---|
ref | RefObject<T | null> | — | Ref to the scroll container element. |
enabled | boolean | true | When false, listeners are not attached and edges stay false. |
orientation | 'vertical' | 'horizontal' | 'vertical' | Axis to observe. |
Returns { start: boolean; end: boolean } — whether content is hidden past the start/end edge.