Intersection Observer In React

Intersection Observer In React

Introduction

Have you ever wondered how those slick slide-in animations and dynamic content loading happen as you scroll through modern websites? You could probably use some sort of mathematical calculations, along with multiple scroll listeners on the body. But that's too complicated, and unnecessary. There is an API for this exact use case known as the Intersection Observer API. Let's explore what it is, what it can do, and how can we integrate it easily into React projects.

Intersection Observer API

From the Mozilla docs,

The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.

That sounds kind of complicated. In simple words, we can efficiently, and easily tell when an element gets visible within our viewport. But why do we care about that you ask? The scroll animations we discussed earlier, is just the other side of the same coin. When we scroll the page, the elements that were not visible before become visible, and because of that the Intersection Observer will trigger some interaction. This interaction can be some animation, or a fetch call etc. The possibilities are endless.

Thats enough about the theory though. Let's see some code.

let options = {
  root: document.querySelector("#scrollArea"),
  threshold: 1.0,
};
 
let observer = new IntersectionObserver(callback, options);
observer.observe(myElement);

Options

The IntersectionObserver constructor accepts an options object to customize its behavior.

  • root : The element that is used as the viewport for checking visibility of the target element. Defaults to the browser viewport if not specified or if null.
  • threshold : The ratio of the area of the target element that needs to be visible to trigger the callback. A threshold of 1.0 signifies, the element needs to be completely intersecting with the viewport(full visible).

Callback

The callback for the IntersectionObserver is triggered every time a target element becomes visible.

It receives a list of IntersectionObserverEntry, and the observer object. This list will be composed of all the targets that are being observed. We can specify an descendent element of the root node to be tracked/observed by using the observe method of the observer

let callback = (entries, observer) => {
  entries.forEach((entry) => {
    // Each entry describes an intersection change for one observed
    // target element:
    //   entry.boundingClientRect
    //   entry.intersectionRatio
    //   entry.intersectionRect
    //   entry.isIntersecting
    //   entry.rootBounds
    //   entry.target
    //   entry.time
  });
};

Example

Let's assume our targets are transparent (opacity of 0), with a transition property on it. We want to transition it to appear (opacity 0 → 1) when we scroll.

.transparent-appear{
    width: 100px;
    height: 100px;
    background-color: blue;
    margin: 20px;
    opacity: 0;
    transition: opacity 0.5s;
}
const callback = (entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
	    // When the observed element enters the viewport
	    entry.target.style.opacity = 1;
    }
	});
};
 
const options = {
  threshold: 1.0
};
 
// Create an Intersection Observer instance with the callback and options
const observer = new IntersectionObserver(callback, options);
 
// Element to observe
const observedElement = document.querySelector('.transparent-appear');
 
// Start observing the element
observer.observe(observedElement);

Implementation in React

Lets encapsulate the Intersection Observer logic in a react component that slides in horizontally or vertically when scroll to it.

Slide On Show Component

Lets establish what are the requirements for the component, and how we can customize its behavior with props.

  • The direction for the slide can be vertical or horizontal.
  • We can provide an offset for the slide.
  • We can specify if the slide should be triggered on revisiting the target.
  • We can specify a delay for the slide-in.
  • We need a state to represent whether our component is visible or not.
function ScrollShow({ delay = 200, direction = 'horizontal', repeat = true, offset = 100, className, ...props }) {
  const [visible, setVisible] = useState(false);
  const ref = useRef(null);
 
  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            setVisible(true);
          } else {
            if (repeat) setVisible(false);
          }
        });
      },
      { threshold: 0.1 } // Adjust the threshold as needed
    );
 
    if (ref?.current) {
      observer.observe(ref.current);
    }
 
    return () => {
      observer.disconnect();
    };
  }, [repeat]);
 
  const inlineStyles = {
    position: 'relative',
    transition: `transform ${delay}ms, opacity ${delay}ms`,
    transform: visible ? 'translateX(0)' : 
			direction === 'vertical' ? `translateY(${offset}px)` 
														   : `translateX(${offset}px)`,
    opacity: visible ? 1 : 0,
  };
 
  return (
    <div
        ref={ref}
        style={inlineStyles}
        className={className}
        {...props}
    />
  );
}
  • ref is used to reference the DOM element that you want to observe for visibility changes. In this case, it's the element that you want to apply the slide-in animation to when it becomes visible.
  • useEffect is used to set up and manage the Intersection Observer. It initializes the observer when the component is mounted, observes changes in the visibility of the referenced element (controlled by ref), and disconnects the observer when the component is unmounted to ensure proper cleanup.

Usage

Now you can use the ScrollShow component in your React application to create elements that slide in as you scroll down the page. For example:

<ScrollShow delay={500} direction="vertical" repeat={false}>
  <div className="your-content">Your content here</div>
</ScrollShow>

This will slide in the content vertically with a 500ms delay and no repeat when it becomes visible in the viewport. You can customize it further based on your needs.

The ScrollShow component has been used on this website as well. Check out the tags page!

Conclusion

In this article, we've explored the Intersection Observer API, which allows us to efficiently detect when elements become visible in the viewport. We've also created a custom React component, ScrollShow, that makes it easy to add scroll-triggered animations to your web applications. By combining the power of the Intersection Observer API with React, you can create engaging and dynamic user experiences without the need for complex scroll event listeners and calculations.