Hooks or component
useInView for state, useOnInView for effects, and <InView> for render props or a wrapper.
A tiny, fully-typed React adapter for the Intersection Observer API. Reveal on scroll, lazy-load, track impressions, and build infinite lists with one hook and about a kilobyte.
$ npm i react-intersection-observerEvery card below waited off-screen and animated in as it crossed the threshold, with the same useInView you would ship. It fires once and then leaves the DOM alone.
useInView for state, useOnInView for effects, and <InView> for render props or a wrapper.
Around 1 kB gzipped per API. Tree-shakeable, so you ship only what you import.
The same threshold, rootMargin, and root options you already know from IntersectionObserver.
Elements with identical options reuse one observer, so thousands of targets stay cheap.
Written in TypeScript. Options, return values, and entries are typed, with no extra @types.
A drop-in mock for the Intersection Observer keeps Vitest and Jest suites deterministic.
useInView({ triggerOnce: true })drives every reveal on this page.Pick the shape that fits your component. The panel follows whichever you are reading. That is useInView doing scrollspy, right here. Tap a card to pin one.
const { ref, inView } = useInView({ threshold: 0.5,});<section ref={ref}> {inView ? "In view" : "Waiting"}</section>const { ref, inView } = useInView({ threshold: 0.5,});<section ref={ref}> {inView ? "In view" : "Waiting"}</section>const ref = useOnInView( (inView, entry) => { track("seen", entry.target); }, { threshold: 1, triggerOnce: true },);<InView as="div" threshold={0.2} triggerOnce> {({ ref, inView }) => ( <div ref={ref}> {inView ? "Loaded" : "Placeholder"} </div> )}</InView>useOnInView runs your callback without a hook-owned re-render. It is the right tool for impressions, prefetching, and logging. Each tile below logs itself the first time it is fully visible.
Change the threshold and scroll the custom root. Same hook, wired to live controls and a live readout.
Install, attach a ref, and read inView. Thresholds, roots, and margins are there when you need them.
$ npm i react-intersection-observerTesting? The package ships a mock for the Intersection Observer so your suites stay deterministic.