dotUI
dotUI
beta
  1. Hooks
  2. Elements hooks
  3. useIntersectionObserver

useIntersectionObserver

Track and manage the visibility of your DOM elements within the viewport with useIntersectionObserver.

"use client";

import * as React from "react";
import { ScrollArea } from "@/lib/components/core/default/scroll-area";
import { useIntersectionObserver } from "@/lib/hooks/use-intersection-observer";
import { cn } from "@/lib/utils/classes";

function Demo() {
  const [ref, entry] = useIntersectionObserver({
    threshold: 0,
    root: null,
    rootMargin: "0px",
  });

  return (
    <div className="w-full max-w-sm">
      <p>
        Element{" "}
        <span className={cn(entry?.isIntersecting ? "text-green-700" : "text-yellow-600")}>
          {entry?.isIntersecting ? "inside" : "outside"}
        </span>{" "}
        the viewport
      </p>
      <ScrollArea type="always" className="mt-4 h-32 border">
        <div className="flex h-96 flex-col items-center justify-between py-8">
          <p className="text-fg-muted">Scroll me down!</p>
          <p ref={ref} className="border-4 border-dashed p-4">
            Hello world!
          </p>
        </div>
      </ScrollArea>
    </div>
  );
}

Installation

Copy and paste the following code into your project.

import React from "react";

export const useIntersectionObserver = <T extends Element>(
  options: IntersectionObserverInit = {}
): [React.RefCallback<T>, IntersectionObserverEntry | null] => {
  const { threshold = 1, root = null, rootMargin = "0px" } = options;
  const [entry, setEntry] = React.useState<IntersectionObserverEntry | null>(null);

  const previousObserver = React.useRef<IntersectionObserver | null>(null);

  const customRef = React.useCallback(
    (node: Element | null) => {
      if (previousObserver.current) {
        previousObserver.current.disconnect();
        previousObserver.current = null;
      }

      if (node?.nodeType === Node.ELEMENT_NODE) {
        const observer = new IntersectionObserver(
          ([entry]) => {
            setEntry(entry);
          },
          { threshold, root, rootMargin }
        );

        observer.observe(node);
        previousObserver.current = observer;
      }
    },
    [threshold, root, rootMargin]
  );

  return [customRef, entry];
};

Update the import paths to match your project setup.

PARAMETERS

NameTypeDescription
threshold
number
Either a single number or an array of numbers between 0 and 1, indicating at what percentage of the target’s visibility the observer’s callback should be executed.
root
element
The Element that is used as the viewport for checking visibility of the target. Defaults to the browser viewport if not specified or if null.
rootMargin
string
Margin around the root. Can have values similar to the CSS margin property. The values can be percentages. This set of values serves to grow or shrink each side of the root element’s bounding box before computing intersections. Defaults to all zeros.

Return values

NameTypeDescription
ref
React.ref.object
A React reference that can be attached to a DOM element to observe.
entry
object
An object containing information about the intersection. This object is similar to IntersectionObserverEntry.

Built by mehdibha. The source code is available on GitHub.