Custom React hook that detects the scroll direction (up or down) with a configurable threshold to prevent jitter.
Import
import useScrollDirection from "../hooks/useScrollDirection"
Signature
function useScrollDirection(
initialDirection?: 'up' | 'down',
threshold: number
): string
Parameters
initialDirection
'up' | 'down'
default:"'up'"
The initial scroll direction before any scrolling occurs.
Minimum scroll distance (in pixels) required to trigger a direction change. Prevents jitter from small scroll movements.
Returns
The current scroll direction: 'up' or 'down'.
Usage
const Header = () => {
const direction = useScrollDirection('up', 10)
return (
<header
className={`
fixed top-0 transition-transform
${direction === 'down' ? '-translate-y-full' : 'translate-y-0'}
`}
>
<nav>Navigation content</nav>
</header>
)
}
Implementation Details
- Performance: Uses
requestAnimationFrame to throttle scroll event updates
- Threshold: Only updates direction when scroll distance exceeds threshold
- State Management: Tracks last scroll position to calculate direction
- Cleanup: Removes scroll event listener on component unmount
- Edge Case: Handles negative scroll positions (sets to 0)
Examples
const AutoHideHeader = () => {
const scrollDirection = useScrollDirection('up', 5)
return (
<header
style={{
transform: scrollDirection === 'down'
? 'translateY(-100%)'
: 'translateY(0)',
transition: 'transform 0.3s ease'
}}
>
<h1>My App</h1>
</header>
)
}
const ScrollToTop = () => {
const direction = useScrollDirection('up', 20)
const [showButton, setShowButton] = useState(false)
useEffect(() => {
// Show button when scrolling up and past a certain point
const handleScroll = () => {
setShowButton(window.scrollY > 300 && direction === 'up')
}
window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll)
}, [direction])
return showButton ? (
<button onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })}>
↑ Back to top
</button>
) : null
}
Dynamic Navbar Style
const Navbar = () => {
const direction = useScrollDirection('up', 15)
const isScrollingDown = direction === 'down'
return (
<nav className={isScrollingDown ? 'navbar-compact' : 'navbar-full'}>
{/* Navbar content */}
</nav>
)
}
Threshold Guidelines
- Small threshold (5-10px): More responsive, may trigger on small movements
- Medium threshold (15-25px): Balanced responsiveness and stability
- Large threshold (30-50px): Only triggers on intentional scroll gestures
Use a larger threshold (20-30px) for mobile devices to avoid triggering on scroll bounce effects.
Common Use Cases
- Auto-hiding headers/navigation bars
- Showing/hiding floating action buttons
- Triggering animations based on scroll direction
- Optimizing scroll-based UI interactions
- Conditional content loading
The hook uses requestAnimationFrame for performance optimization, ensuring direction updates don’t block the main thread.