react-intersection-observer

React implementation of the Intersection Observer API to tell you when an e...

README

react-intersection-observer

[![Version Badge][npm-version-svg]][package-url] [![GZipped size][npm-minzip-svg]][bundlephobia-url] [![Test][test-image]][test-url] [![License][license-image]][license-url] [![Downloads][downloads-image]][downloads-url]

React implementation of the
to tell you when an element enters or leaves the viewport. Contains both a
plain children implementation.

Storybook Demo:

Features


- 🪝 Hooks or Component API - With useInView it's easier than ever to
  monitor elements
- ⚡️ Optimized performance - Reuses Intersection Observer instances where
  possible
- ⚙️ Matches native API - Intuitive to use
- 🛠 Written in TypeScript - It'll fit right into your existing TypeScript
  project
- 🧪 Ready to test - Mocks the Intersection Observer for easy testing with
  Jest or Vitest
- 🌳 Tree-shakeable - Only include the parts you use
- 💥 Tiny bundle - Around ~1.15kB for useInView and ~1.6kB for
``

Installation


Install using Yarn:

  1. ```sh
  2. yarn add react-intersection-observer
  3. ```

or NPM:

  1. ```sh
  2. npm install react-intersection-observer --save
  3. ```

Usage


useInView hook


  1. ``` js
  2. // Use object destructing, so you don't need to remember the exact order
  3. const { ref, inView, entry } = useInView(options);

  4. // Or array destructing, making it easy to customize the field names
  5. const [ref, inView, entry] = useInView(options);
  6. ```

The useInView hook makes it easy to monitor the inView state of your
components. Call the useInView hook with the (optional) options
you need. It will return an array containing a ref, the inView status and
the current
[entry](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry).
Assign the ref to the DOM element you want to monitor, and the hook will
report the status.

  1. ``` js
  2. import React from 'react';
  3. import { useInView } from 'react-intersection-observer';

  4. const Component = () => {
  5.   const { ref, inView, entry } = useInView({
  6.     /* Optional options */
  7.     threshold: 0,
  8.   });

  9.   return (
  10.     <div ref={ref}>
  11.       <h2>{`Header inside viewport ${inView}.`}</h2>
  12.     </div>
  13.   );
  14. };
  15. ```
Edit useInView

Render props


To use the `` component, you pass it a function. It will be called
whenever the state changes, with the new value of inView. In addition to the
inView prop, children also receive a ref that should be set on the
containing DOM element. This is the element that the Intersection Observer will
monitor.

If you need it, you can also access the
[IntersectionObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry)
on entry, giving you access to all the details about the current intersection
state.

  1. ``` js
  2. import { InView } from 'react-intersection-observer';

  3. const Component = () => (
  4.   <InView>
  5.     {({ inView, ref, entry }) => (
  6.       <div ref={ref}>
  7.         <h2>{`Header inside viewport ${inView}.`}</h2>
  8.       </div>
  9.     )}
  10.   </InView>
  11. );

  12. export default Component;
  13. ```
Edit InView render props

Plain children


You can pass any element to the ``, and it will handle creating the
wrapping DOM element. Add a handler to the onChange method, and control the
state in your own component. Any extra props you add to `` will be
passed to the HTML element, allowing you set the className, style, etc.

  1. ``` js
  2. import { InView } from 'react-intersection-observer';

  3. const Component = () => (
  4.   <InView as="div" onChange={(inView, entry) => console.log('Inview:', inView)}>
  5.     <h2>Plain children are always rendered. Use onChange to monitor state.</h2>
  6.   </InView>
  7. );

  8. export default Component;
  9. ```
Edit InView plain children

Note<br>

When rendering a plain child, make sure you keep your HTML output semantic.

Change the as to match the context, and add a className to style the

<InView />. The component does not support Ref Forwarding, so if you need a

ref to the HTML element, use the Render Props version instead.


API


Options


Provide these as the options argument in the useInView hook or as props on the
**``** component.

NameTypeDefaultDescription
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
**root**`Element``document`The
**rootMargin**`string``'0px'`Margin
**threshold**`number``0`Number
**onChange**`(inView,`undefined`Call
**trackVisibility**`boolean``false`A
**delay**`number``undefined`A
**skip**`boolean``false`Skip
**triggerOnce**`boolean``false`Only
**initialInView**`boolean``false`Set
**fallbackInView**`boolean``undefined`If

InView Props


The **``** component also accepts the following props:

NameTypeDefaultDescription
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
**as**`string``'div'`Render
**children**`({ref,`undefined`Children

Intersection Observer v2 🧪


The new
extends the original API, so you can track if the element is covered by another
element or has filters applied to it. Useful for blocking clickjacking attempts
or tracking ad exposure.

To use it, you'll need to add the new trackVisibility and delay options.
When you get the entry back, you can then monitor if isVisible is true.

  1. ``` js
  2. const TrackVisible = () => {
  3.   const { ref, entry } = useInView({ trackVisibility: true, delay: 100 });
  4.   return <div ref={ref}>{entry?.isVisible}</div>;
  5. };
  6. ```

This is still a very new addition, so check
caniuse for current browser
support. If trackVisibility has been set, and the current browser doesn't
support it, a fallback has been added to always report isVisible as true.

It's not added to the TypeScript lib.d.ts file yet, so you will also have to
extend the IntersectionObserverEntry with the isVisible boolean.

Recipes


The IntersectionObserver itself is just a simple but powerful tool. Here's a
few ideas for how you can use it.

- Track impressions _(Google Analytics, Tag
  Manager, etc)_

FAQ


How can I assign multiple refs to a component?


You can wrap multiple ref assignments in a single useCallback:

  1. ``` js
  2. import React, { useRef, useCallback } from 'react';
  3. import { useInView } from 'react-intersection-observer';

  4. function Component(props) {
  5.   const ref = useRef();
  6.   const { ref: inViewRef, inView } = useInView();

  7.   // Use `useCallback` so we don't recreate the function on each render
  8.   const setRefs = useCallback(
  9.     (node) => {
  10.       // Ref's from useRef needs to have the node assigned to `current`
  11.       ref.current = node;
  12.       // Callback refs, like the one from `useInView`, is a function that takes the node as an argument
  13.       inViewRef(node);
  14.     },
  15.     [inViewRef],
  16.   );

  17.   return <div ref={setRefs}>Shared ref is visible: {inView}</div>;
  18. }
  19. ```

rootMargin isn't working as expected


When using rootMargin, the margin gets added to the current root - If your
application is running inside a `