Last updated: Apr 7, 2024
Reading timeยท2 min
To check if an element is in the viewport in React.js:
ref
prop on the element.IntersectionObserver
API to track if the element is intersecting.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; }
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.
useIsInViewport
hook takes a ref object that points to the element we want to track.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 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).
ref
).We called the observe()
method passing it the element we want to track -
observer.observe(ref.current)
.
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.
// ๐๏ธ 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
.
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.
const isInViewport1 = useIsInViewport(ref1); console.log('isInViewport1: ', isInViewport1); useEffect(() => { // ๐๏ธ listen for changes console.log(isInViewport1); }, [isInViewport1]);
Any time the isInViewport1
state variable changes, the useEffect
hook is
rerun.