Intersection Observer API for visibility tracking

Maya Patel Jan 2026
2 tabs
import { useEffect, useState, useRef } from 'react'

interface UseInViewOptions {
  threshold?: number | number[]
  rootMargin?: string
  triggerOnce?: boolean
}

export function useInView(options: UseInViewOptions = {}) {
  const { threshold = 0, rootMargin = '0px', triggerOnce = false } = options
  const [inView, setInView] = useState(false)
  const [hasTriggered, setHasTriggered] = useState(false)
  const ref = useRef<HTMLElement>(null)

  useEffect(() => {
    const element = ref.current
    if (!element || (triggerOnce && hasTriggered)) return

    const observer = new IntersectionObserver(
      ([entry]) => {
        const isInView = entry.isIntersecting
        setInView(isInView)

        if (isInView && triggerOnce) {
          setHasTriggered(true)
        }
      },
      { threshold, rootMargin }
    )

    observer.observe(element)

    return () => observer.disconnect()
  }, [threshold, rootMargin, triggerOnce, hasTriggered])

  return { ref, inView }
}
2 files · typescript Explain with highlit

The Intersection Observer API efficiently detects when elements enter/exit the viewport, enabling lazy loading, infinite scroll, and analytics without expensive scroll listeners. I create observers with thresholds defining when callbacks trigger—0.0 means any pixel visible, 1.0 means fully visible. Observers watch multiple elements simultaneously with minimal performance cost. For lazy images, I load when they're about to enter viewport with a root margin buffer. Analytics track which content users actually view. Infinite scroll triggers fetches when sentinel elements become visible. This modern API replaces polling and improves performance significantly. All major browsers support it.