Web Fundamentals

Import on Visibility: Lazy Loading with IntersectionObserver

mediumWeb Fundamentals

Import on Visibility: Lazy Loading with IntersectionObserver

Learn the interview-ready mental model, practical trade-offs, and production patterns for this web fundamentals topic.

Topic content

TL;DRUse IntersectionObserver + dynamic import() to load heavy components only when they enter the viewport
High Signal
Google
Meta
Agoda
Meesho
30-Second Answerstart every interview with this

Import on Visibility combines IntersectionObserver with dynamic import() to load JavaScript-heavy components only when they approach the user's viewport. This pattern further reduces initial bundle size for below-the-fold content while providing smooth perceived performance through rootMargin prefetching.

Static imports = cooking everything before opening the restaurant. Import on Interaction = cooking after they order. Import on Visibility = starting prep when you see them walking toward the table (via IntersectionObserver). You save resources on startup and still deliver smoothly when they arrive.

Placeholder renders (lightweight)
IntersectionObserver detects viewport approach
dynamic import() + rootMargin prefetch
Component mounts with loading UI

1IntersectionObserver Basics

The modern, efficient way to detect when an element enters the viewport without expensive scroll listeners. Use rootMargin to start loading slightly before the element is visible.

observer.jsjs
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      loadHeavyComponent();
      observer.disconnect();
    }
  });
}, { rootMargin: '150px' });

observer.observe(placeholderElement);

2Basic Implementation

Combine observer with dynamic import() to load and mount the real component only when needed.

3React Hook Pattern

Encapsulate logic into a reusable hook for clean, production-ready usage with proper cleanup and state management.

useLoadOnVisibility.jsjs
function useLoadOnVisibility(loader, rootMargin = '150px') {
  const ref = useRef(null);
  const [Component, setComponent] = useState(null);
  // ... loading & error states
}

4Reusable Component & Advanced Tips

Create <LazyLoadOnVisible /> wrapper. Support parallel imports, meaningful placeholders, error handling, and cleanup to prevent memory leaks.

Key Takeaways
  • IntersectionObserver + dynamic import() = efficient below-the-fold lazy loading
  • Use positive rootMargin to prefetch before visibility
  • Always disconnect observer and guard against duplicate loads
  • Best for comments, embeds, charts, recommendations — not core UI
  • Provide excellent placeholders and error handling for great UX
  • Combine with import on interaction for complete lazy loading coverage