Check if an Element is in the Viewport in React.js

avatar
Borislav Hadzhiev

Last updated: Apr 7, 2024
2 min

banner

# Check if an Element is in the Viewport in React.js

To check if an element is in the viewport in React.js:

  1. Set the ref prop on the element.
  2. Use the IntersectionObserver API to track if the element is intersecting.
App.js
import {useEffect, useRef, useState, useMemo} from 'react'; export default function App() { const ref1 = useRef(null); const ref2 = useRef(null); const isInViewport1 = useIsInViewport(ref1); console.log('isInViewport1: ', isInViewport1); const isInViewport2 = useIsInViewport(ref2); console.log('isInViewport2: ', isInViewport2); return ( <div> <div ref={ref1}>Top div {isInViewport1 && '| in viewport โœ…'}</div> <div style={{height: '155rem'}} /> <div ref={ref2}>Bottom div {isInViewport2 && '| in viewport โœ…'}</div> </div> ); } function useIsInViewport(ref) { const [isIntersecting, setIsIntersecting] = useState(false); const observer = useMemo( () => new IntersectionObserver(([entry]) => setIsIntersecting(entry.isIntersecting), ), [], ); useEffect(() => { observer.observe(ref.current); return () => { observer.disconnect(); }; }, [ref, observer]); return isIntersecting; }

react check if element is in viewport

The code for this article is available on GitHub

The example shows how to check if an element is in the viewport.

The IntersectionObserver API enables us to check if a given element is intersecting the document.

The useIsInViewport hook takes a ref object that points to the element we want to track.
App.js
function useIsInViewport(ref) { const [isIntersecting, setIsIntersecting] = useState(false); const observer = useMemo( () => new IntersectionObserver(([entry]) => setIsIntersecting(entry.isIntersecting), ), [], ); useEffect(() => { observer.observe(ref.current); return () => { observer.disconnect(); }; }, [ref, observer]); return isIntersecting; }
The code for this article is available on GitHub

The IntersectionObserver constructor takes a function that gets called with an array of entries.

Entries is an array of all of the observer's targeted elements that have become either more or less visible than one of the intersection observer ratios.

Each entry describes how much of a given element is intersecting with the root element (the document).

We destructured the entry because our IntersectionObserver only tracks a single element (the element on which we set the ref).

We called the observe() method passing it the element we want to track - observer.observe(ref.current).

App.js
useEffect(() => { observer.observe(ref.current); return () => { observer.disconnect(); }; }, [ref, observer]);

Every time the element enters the viewport or exits the viewport, the function we passed to the IntersectionObserver() constructor gets called and we update the state.

App.js
// ๐Ÿ‘‡๏ธ Gets called every time the element enters or leaves the viewport new IntersectionObserver(([entry]) => setIsIntersecting(entry.isIntersecting), )

If the element on which we set the ref object is in the viewport, the useIsInViewport hook will return true.

If the element isn't in the viewport, the hook returns false.

Note that on the initial render, the useIsInViewport hook will return false because that's the initial value we passed to useState.

App.js
const [isIntersecting, setIsIntersecting] = useState(false);

If you want to track changes to the return value of the hook, use the useEffect hook and add the value to the hook's dependencies.

App.js
const isInViewport1 = useIsInViewport(ref1); console.log('isInViewport1: ', isInViewport1); useEffect(() => { // ๐Ÿ‘‡๏ธ listen for changes console.log(isInViewport1); }, [isInViewport1]);
The code for this article is available on GitHub

Any time the isInViewport1 state variable changes, the useEffect hook is rerun.

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.

Copyright ยฉ 2024 Borislav Hadzhiev