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
- Observer Map: Archibald uses an internal map to share a single
IntersectionObserverinstance between all components that use the same configuration (e.g., the samerootMarginandthreshold). - Ref Management: You attach the returned
setRefcallback to any DOM node you wish to observe. - State Updates: The hook updates its
isVisibleboolean state as the element enters or exits the viewport. - 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: falsefor 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
rootMarginfor 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
isVisibleflag to conditionally render light components. For heavy operations, consider combininguseIsVisiblewithReact.lazyorLoadable. - Use
multiple: truefor scroll-triggered animations: Only use this if you need an animation to restart every time the user scrolls back to the element. - Prefer
setRefoverelement: While you can pass a direct DOM element, using thesetRefcallback is generally more robust as it handles dynamic mounting/unmounting automatically.