DevPrep
  • Interview Prep
  • Projects
  • Resources
  • Pricing
  • About Us
Submit Question
DevPrep
  • Pricing
  • About Us
Submit Question
  1. Home
  2. Articles
  3. Frontend Engineering
  4. Intersection Observer API: Lazy Loading and Scroll Effects
XLinkedInReddit
MediumFrontend Engineering

Intersection Observer API: Lazy Loading and Scroll Effects

D
DevPrep Team
February 10, 2026·2 min read·0
Table of Contents
  • Basic Usage
  • Lazy Loading Images
  • Infinite Scroll
  • Scroll-Triggered Animations
  • Performance Advantage

Stop using scroll events for visibility detection. Intersection Observer is performant, non-blocking, and runs on a separate thread.

Basic Usage

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add("visible");
      observer.unobserve(entry.target); // Stop observing after first intersection
    }
  });
}, {
  threshold: 0.1, // Trigger when 10% visible
  rootMargin: "50px" // Start loading 50px before entering viewport
});

document.querySelectorAll(".lazy").forEach(el => observer.observe(el));

Lazy Loading Images

function LazyImage({ src, alt, ...props }) {
  const imgRef = useRef();
  const [loaded, setLoaded] = useState(false);

  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) {
        setLoaded(true);
        observer.disconnect();
      }
    }, { rootMargin: "200px" });

    if (imgRef.current) observer.observe(imgRef.current);
    return () => observer.disconnect();
  }, []);

  return (
    <div ref={imgRef}>
      {loaded ? (
        <img src={src} alt={alt} {...props} />
      ) : (
        <div className="placeholder" />
      )}
    </div>
  );
}

Infinite Scroll

function InfiniteList({ fetchMore }) {
  const sentinelRef = useRef();

  useEffect(() => {
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) fetchMore();
    });

    if (sentinelRef.current) observer.observe(sentinelRef.current);
    return () => observer.disconnect();
  }, [fetchMore]);

  return (
    <div>
      {items.map(renderItem)}
      <div ref={sentinelRef} style={{ height: 1 }} />
    </div>
  );
}

Scroll-Triggered Animations

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    entry.target.style.opacity = entry.intersectionRatio;
    entry.target.style.transform = `translateY(${(1 - entry.intersectionRatio) * 20}px)`;
  });
}, { threshold: Array.from({ length: 20 }, (_, i) => i / 20) }); // 20 thresholds for smooth animation

Performance Advantage

Unlike scroll event listeners, Intersection Observer runs asynchronously and doesn't block the main thread. No need for throttle/debounce.

Related Articles

MediumFrontend Engineering

System Design #12: Design a Multi-Step Form Wizard

7 min read
MediumFrontend Engineering

Mastering Senior-Level JavaScript Interview Concepts

2 min read
MediumFrontend Engineering

System Design #9: Design a Collaborative Text Editor

9 min read

Comments (0)

Sign in to leave a comment.

No comments yet. Be the first to comment.

Table of Contents

  • Basic Usage
  • Lazy Loading Images
  • Infinite Scroll
  • Scroll-Triggered Animations
  • Performance Advantage

Series

View all Frontend Engineering articles →

Practice

  • JavaScript
  • DSA
  • Machine Coding
  • System Design

Resources

  • Learning Tracks
  • Articles
  • Roadmaps
  • Compare Concepts
  • Glossary
  • Developer Tools
  • All Questions

Company

  • About
  • Pricing

Legal

  • Privacy Policy
  • Terms of Service
DevPrep

© 2026 DevPrep. All rights reserved.