Skip to main content
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

completion
number
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).

Performance Considerations

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.

Build docs developers (and LLMs) love