Skip to content
usefy

ScrollProgress

ComponentsReading progress

Drop-in React reading-progress bar

Live demo

Install

$ npm install @usefy/scroll-progress

Quick start

ScrollProgress.tsx
import { ScrollProgress } from "@usefy/scroll-progress";

function App() {
  return (
    <>
      {/* Mount once — a 3px blue bar fixed to the top of the viewport */}
      <ScrollProgress />
      <YourApp />
    </>
  );
}

API reference

<ScrollProgress /> Props

PropTypeDefaultDescription
position"top" | "bottom""top"Which viewport edge the fixed bar is pinned to.
colorstring"#3b82f6"Bar fill color (any CSS color).
heightnumber | string3Bar thickness — a number is pixels, a string is any CSS length.
zIndexnumber9999Stacking order of the fixed bar.
targetRefObject<HTMLElement | null>— (window)Ref to the scrollable container to measure. Omit to track the window/document scroll.
throttleMsnumber0Throttle interval (ms) for scroll updates, forwarded to useScrollPosition. 0 updates on every scroll event. The trailing edge always fires, so the resting position is never dropped.
aria-labelstring"Scroll progress"Accessible name of the progressbar.
render(progress: number) => ReactNodeEscape hatch: replaces the default bar entirely; called on every render with the current progress (01).
classNamestringClass applied to the default bar element.
styleCSSPropertiesInline styles merged over the defaults (yours win).

Other exports

ExportDescription
ScrollProgressPropsProps type.
ScrollProgressPosition"top" | "bottom".
DEFAULT_BAR_COLOR / DEFAULT_BAR_HEIGHT / DEFAULT_Z_INDEX / DEFAULT_ARIA_LABELThe defaults, for reuse in custom render UIs.

The default bar also exposes a data-position="top" | "bottom" attribute for styling and testing.

Progress model

progress = clamp(scrollTop / (scrollHeight - clientHeight), 0, 1)

When scrollHeight - clientHeight <= 0 (the content fits in the viewport, so there is nothing to scroll), progress is 0 — the bar renders but stays empty, so layout and stacking never jump when content grows.


Go deeper

This page is the quick reference. For every example, prop, and edge case, read the full README — or open Storybook to change props live.

More in components