useScrollProgress

@sushindustries/ui

How far one element has travelled up the viewport, 0 to 1, once per frame. Gated by an observer so an off-screen element costs nothing.

6 min read
View as MarkdownEdit on GitHub

A hook that reports how far one element has climbed the viewport, from 0 to 1, once per frame - not how far the page has scrolled. Reach for it to drive anything that should animate in as an element arrives, like a progress bar or a reveal, gated by an IntersectionObserver so off-screen elements cost nothing.

Viewport width
Phone22remthe floor, the narrowest this promises to work at
tanstacktanstack add https://adamjurek.com/r/tanstack/use-scroll-progress.jsonshadcnpnpm dlx shadcn@latest add https://adamjurek.com/r/shadcn/use-scroll-progress.json

Why it is built this way

An IntersectionObserver gates the scroll listener rather than driving the value, because observers report crossings, not positions - they can't give a smooth progress on their own, but they're the cheapest way to stop measuring an element nobody can see. The callback is expected to write to a ref directly rather than call setState, for the same reason useScrollTurnuseScrollTurnScroll position as a rotation, delivered once per frame. Drives a CSS transform or a three.js object, never React state.@sushindustries/ui · motion does: sixty updates a second would re-render a subtree sixty times a second.

Install commands are not written here

Anything in packages/ui/registry.ts gets its TanStack and shadcn commands attached automatically, so there is nothing to keep in sync.

Install

tanstack add https://adamjurek.com/r/tanstack/use-scroll-progress.json
shell
pnpm dlx shadcn@latest add https://adamjurek.com/r/shadcn/use-scroll-progress.json
shell
pnpm add @sushindustries/ui @sushindustries/atoms
shell

What you get

Version0.1.0
Categorymotion · Scroll effects
Filesuse-scroll-progress.ts
DependenciesNone
Tagsscroll, hook, observer, no-deps

No runtime dependencies

It brings nothing with it beyond the stylesheet.