Skip to main content

useIsVisible: Performance-Friendly Intersection Tracking

Best Practices Guide for Intersection Observer Integration


Introduction

Tracking whether an element is visible in the viewport is a common requirement for lazy loading, animations, and analytics. The useIsVisible hook simplifies this by wrapping the Intersection Observer API and providing an efficient, deduplicated tracking system.

How it works

  1. Observer Map: Archibald uses an internal map to share a single IntersectionObserver instance between all components that use the same configuration (e.g., the same rootMargin and threshold).
  2. Ref Management: You attach the returned setRef callback to any DOM node you wish to observe.
  3. State Updates: The hook updates its isVisible boolean state as the element enters or exits the viewport.
  4. Automatic Cleanup: The observer is automatically disconnected when the component unmounts or the target element is removed.

Why use useIsVisible?

  • Optimized Performance: Deduplicates observer instances to minimize browser overhead.
  • Declarative Logic: Replaces complex imperative intersection logic with a simple boolean flag.
  • SSR Compatibility: Gracefully handles server-side rendering environments where the DOM is not yet available.

See Also

For a detailed technical breakdown and additional implementation patterns, refer to the following resources:


Key Takeaways

  • Rely on the default multiple: false for lazy loading: The default already stops observing after the first intersection — ideal when you only need to know when an element becomes visible once (e.g., to load an image), since the observer is disconnected immediately, saving resources.
  • Tune rootMargin for proactive loading: The default is already '100px 0px 0px 0px', triggering visibility slightly before the element enters the viewport for a smoother user experience. Increase it for heavier content, or set '0px' if you need exact viewport intersection.
  • Avoid expensive logic in render: Use the isVisible flag to conditionally render light components. For heavy operations, consider combining useIsVisible with React.lazy or Loadable.
  • Use multiple: true for scroll-triggered animations: Only use this if you need an animation to restart every time the user scrolls back to the element.
  • Prefer setRef over element: While you can pass a direct DOM element, using the setRef callback is generally more robust as it handles dynamic mounting/unmounting automatically.