Custom React hook that calculates and returns the current scroll progress as a percentage of the total scrollable height.
Import
import useScrollProgess from "../hooks/useScrollProgess"
Signature
function useScrollProgess(): number
Parameters
This hook takes no parameters.
Returns
The scroll progress as a percentage (0-100), rounded to 2 decimal places.
Usage
const ScrollProgressBar = () => {
const progress = useScrollProgess()
return (
<div
style={{
position: 'fixed',
top: 0,
left: 0,
width: `${progress}%`,
height: '3px',
backgroundColor: '#3b82f6',
transition: 'width 0.1s ease'
}}
/>
)
}
Implementation Details
- Calculation:
(scrollY / (scrollHeight - viewportHeight)) * 100
- Precision: Returns percentage with 2 decimal places
- Performance: Updates on scroll events
- Edge Cases: Handles pages with no scrollable content (returns 0)
- Cleanup: Removes scroll event listener on component unmount
Examples
Progress Bar Indicator
const ReadingProgress = () => {
const progress = useScrollProgess()
return (
<div className="fixed top-0 left-0 w-full h-1 bg-gray-200 z-50">
<div
className="h-full bg-blue-500 transition-all duration-150"
style={{ width: `${progress}%` }}
/>
</div>
)
}
Percentage Display
const ScrollPercentage = () => {
const completion = useScrollProgess()
return (
<div className="fixed bottom-4 right-4 bg-white rounded-full p-3 shadow-lg">
<span className="text-sm font-medium">
{Math.round(completion)}%
</span>
</div>
)
}
Conditional Content
const ScrollBasedContent = () => {
const progress = useScrollProgess()
const showElement = progress > 50
return (
<>
<article>
{/* Long article content */}
</article>
{showElement && (
<aside className="sticky top-20">
<h3>Related Articles</h3>
{/* Show after 50% scroll */}
</aside>
)}
</>
)
}
Multi-Threshold Tracking
const ScrollMilestones = () => {
const progress = useScrollProgess()
useEffect(() => {
if (progress >= 25 && progress < 26) {
console.log('Quarter way through!')
}
if (progress >= 50 && progress < 51) {
console.log('Halfway there!')
}
if (progress >= 75 && progress < 76) {
console.log('Almost done!')
}
if (progress >= 99) {
console.log('Completed!')
}
}, [progress])
return <div>Scroll to see milestones in console</div>
}
Combined with Animations
const AnimatedContent = () => {
const scrollProgress = useScrollProgess()
const opacity = Math.min(scrollProgress / 20, 1) // Fade in first 20%
return (
<div style={{ opacity, transition: 'opacity 0.3s' }}>
<h1>Content fades in as you scroll</h1>
</div>
)
}
Common Use Cases
- Reading progress indicators for articles/documentation
- Scroll-based progress bars
- Triggering events at specific scroll milestones
- Analytics tracking (how far users scroll)
- Scroll-based animations and effects
- Unlocking content based on scroll depth
Combine with useScrollDirection for more sophisticated scroll-based UI patterns, like showing a progress bar only when scrolling up.
The hook returns 0 if the page has no scrollable content (when content height ≤ viewport height).
The hook attaches a scroll event listener. For performance-sensitive applications, consider:
- Debouncing or throttling the scroll event
- Using CSS-based progress indicators when possible
- Memoizing components that consume this hook’s value
Browser Compatibility
This hook uses standard DOM APIs:
window.scrollY
document.body.scrollHeight
window.innerHeight
These are supported in all modern browsers.