Image Lazy Loading — Load the Right Images at the Right Time to Speed Up Your Site
> cd .. / HUB_EDITORIALE
Seo e analitica

Image Lazy Loading — Load the Right Images at the Right Time to Speed Up Your Site

[2026-07-29] Author: Ing. Calogero Bono
> share
Zenithby Meteora Web The operating system for your business. Social, clients, bookings and invoices in one platform. Gyms, barbers, professionals. Discover Zenith Free demo · no card

Your site has heavy images and PageSpeed Insights is punishing you? Every kilobyte loaded too late is a potential lost customer. We at Meteora Web see it every day: sites loading dozens of images all at once, even those the user never sees. The result is slow load times, poor Core Web Vitals, and Google penalties. The solution is lazy loading: loading images only when needed. This guide covers two techniques: native loading='lazy' and Intersection Observer. Let's start with the real problem: your homepage has 12 off-screen images? You are paying to load them all.

Why is Image Lazy Loading Critical for Performance?

Imagine walking into a store and the clerk shows you the entire catalog at once, including items from departments you don't care about. Waste of time. On a website, the browser must download, decode, and render every image in the DOM, even those off-screen. Lazy loading defers off-screen images until the user scrolls near them. This reduces initial load time (FCP, LCP) and data transferred.

Impact on Core Web Vitals is direct: Lower LCP means better rankings. In an e-commerce site with 40 images per page, we cut load time by 35% and improved LCP by over 1 second. Not theory — accounting.

Sponsored Protocol

Difference Between Native Lazy Loading and Intersection Observer

Browsers offer two main ways to defer loading:

  • Native lazy loading (loading='lazy') — HTML attribute supported in all modern browsers. Just add loading="lazy" to <img> or <iframe>. The browser decides when to load based on distance from viewport.
  • Intersection Observer API — More flexible JavaScript API: you observe an element and trigger a callback when it enters the viewport (or a threshold you set). Allows custom behavior like pre-loading, fade-in effects, etc.

Which one? For simplicity and reliability, use native. For advanced needs (animated placeholders, progressive loading, CSS backgrounds), Intersection Observer gives full control.

How Does Native Lazy Loading Work and When to Use It?

Native lazy loading is the easiest: add loading="lazy" to the image tag. No JavaScript, no library. The browser handles everything, starting load when the image is about 1250 pixels from the viewport.

Sponsored Protocol

Practical Implementation of loading='lazy'

<img src="heavy-image.jpg" alt="Description" loading="lazy" width="800" height="600">

Important: always specify width and height to avoid Cumulative Layout Shift (CLS). The browser reserves space even before the image loads.

Example with a product grid:

<div class="grid">
  <img src="product1.jpg" alt="Product 1" loading="lazy" width="300" height="300">
  <img src="product2.jpg" alt="Product 2" loading="lazy" width="300" height="300">
  <!-- … -->
</div>

Browser Support and Fallback

Native lazy loading is supported in Chrome 76+, Firefox 75+, Safari 15.4+, Edge 79+. For older browsers (e.g., Safari <15.4) you can fallback to Intersection Observer. We often combine both: try native first, if unsupported activate Observer.

When to Choose Native vs JavaScript

  • Native is enough for most sites: blogs, landing pages, portfolios.
  • Intersection Observer is needed if you want:
    – Fancy loading effects (placeholder, blur-up)
    – Load images only after a delay (e.g., infinite scroll)
    – Lazy loading CSS background images

How to Implement Lazy Loading with Intersection Observer?

Intersection Observer is a native JavaScript API (available since 2017) that lets you observe when an element enters a certain area (intersection) with the viewport. Perfect for advanced lazy loading.

Sponsored Protocol

Basic Code: Load Images When They Enter the Viewport

Here's a working snippet you can copy into your theme:

document.addEventListener("DOMContentLoaded", function() {
  const lazyImages = document.querySelectorAll("img[data-src]");

  if ("IntersectionObserver" in window) {
    const imageObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(entry) {
        if (entry.isIntersecting) {
          const img = entry.target;
          img.src = img.dataset.src;  // load the real image
          img.removeAttribute("data-src");
          img.classList.add("loaded");
          observer.unobserve(img);
        }
      });
    }, { rootMargin: "200px 0px" }); // load 200px before entering

    lazyImages.forEach(function(img) {
      imageObserver.observe(img);
    });
  } else {
    // Fallback: load all immediately
    lazyImages.forEach(function(img) {
      img.src = img.dataset.src;
    });
  }
});

How it works: Put the real URL in data-src and a placeholder in src (e.g., transparent gif). The observer triggers loading when the image enters the viewport, with a 200px margin to anticipate scroll.

Sponsored Protocol

Handling Placeholders and Visual Effects

You can add a small SVG placeholder as initial src, then apply a CSS transition when the real image arrives:

img[data-src] {
  opacity: 0.3;
  transition: opacity 0.3s ease-in-out;
}
img.loaded {
  opacity: 1;
}

Or use a blur-up technique: load a tiny pixelated version as src, then replace with full-size. We used this for an e-commerce client: images went from 1 MB to 400 KB with fade-in, and LCP dropped by 40%.

Lazy Loading CSS Background Images

Intersection Observer can also handle images loaded via background-image. Example:

<div class="hero lazy-bg" data-bg="url('background.jpg')">…</div>
const lazyBgs = document.querySelectorAll(".lazy-bg");
const bgObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const el = entry.target;
      el.style.backgroundImage = el.dataset.bg;
      el.classList.add("bg-loaded");
      observer.unobserve(el);
    }
  });
}, { rootMargin: "100px" });
lazyBgs.forEach(el => bgObserver.observe(el));

How to Test and Validate Your Lazy Loading?

Don't just implement: verify it works and doesn't cause regressions.

  • Google PageSpeed Insights — if offscreen images are not loaded, you won't see “defer offscreen images” suggestion.
  • Lighthouse (Performance section) — check the “Defer offscreen images” audit.
  • Chrome DevTools > Network — filter by “img” and scroll: you should see images loading as you scroll down.
  • Visual check — ensure images don't appear empty or with broken placeholders on older browsers (test on Safari 14, Chrome 70).

Common Mistakes to Avoid

  • Missing explicit dimensions: without width/height, the browser reserves zero space then reflows when the image loads, causing CLS. Always set width/height.
  • Above-the-fold image with lazy loading: don't apply lazy loading to images visible at initial load – it hurts LCP. Use loading="eager" or omit the attribute.
  • Too small rootMargin: if you load exactly when the image enters, user may see a flash of placeholder. Add margin (200-300px) to preload.
  • Forgetting fallbacks: for browsers without Intersection Observer (IE11) or native lazy, fallback to immediate loading.

Which Technique to Choose for Your Project?

No one-size-fits-all. Here's how we decide at Meteora Web:

  • WordPress site with Elementor: native lazy loading (often enabled by default, but check conflicts with caching plugins).
  • WooCommerce e-commerce with many images: Intersection Observer with placeholder and fade-in. User experience control is worth the complexity.
  • Single-page applications (Vue, React): they have built-in lazy loading, but adding Intersection Observer for images in yet-to-mount components is beneficial.
  • Custom CMS projects: we use a unified script that tries native, falls back to Observer, and finally loads all if nothing works.

Remember: the cost of a late-loaded image is low; the cost of an early-loaded image is high. Every millisecond saved on initial load is a competitive edge.

What to Do Now

  1. Analyze your site with PageSpeed Insights. Identify offscreen images that aren't lazy loaded.
  2. Add loading='lazy' to all images below the fold. For WordPress, many themes already do it; otherwise edit functions.php or use a plugin.
  3. Implement Intersection Observer if you need advanced control. Start with the snippet above, then customize placeholders and margins.
  4. Test on real browsers: Safari mobile, Chrome, Firefox. Check for white images or layout breaks.
  5. Measure the impact on Core Web Vitals (LCP, CLS). A good implementation can cut LCP by 0.5-2 seconds.

For a deeper dive into the entire performance ecosystem, read our pillar guide: Core Web Vitals and PageSpeed — The Pillar Guide (English version available).

> share
Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere informatico, fondatore di Meteora Web e Zenith OS. System administrator e progettista di piattaforme, app e CMS proprietari, con esperienza in sviluppo full-stack, marketing digitale ed ecosistema Google.
[ Read Full Dossier ]

> METEORA_WEB // DIGITAL AGENCY

We build the digital presence your business deserves.

Websites, social media, online advertising, e-commerce and high-performance hosting, engineered with method by computer engineers in Sciacca, for all of Italy.

> MW_JOURNAL

> READ_ALL()