Skip to main content

Lazy Loading: Defer Offscreen Images and Iframes

13 min read

13 min read

Lazy Loading: Defer Offscreen Images and Iframes

Reading time: 13 minutes

Quick Definition: Lazy loading delays loading images and iframes until they’re about to enter the viewport (become visible). Instead of loading all 50 images when the page loads, lazy loading only loads the 3-5 visible images immediately, then loads the rest as you scroll.

One attribute fixes 80% of page weight:

<img src="image.jpg" alt="Description" loading="lazy">

TLDR

Lazy loading defers loading images and iframes until users scroll near them, reducing initial page weight by 30-60%. Add loading="lazy" to image and iframe tags. No JavaScript needed. Critical mistake: don’t lazy load above-the-fold images like your hero or logo, as this delays LCP and hurts performance scores. Always include width and height attributes to prevent layout shift. WordPress automatically adds lazy loading to images. Lazy load YouTube embeds and Google Maps if they’re below the fold.


Why Lazy Loading Matters

The Problem: Loading Everything Upfront

Typical blog post without lazy loading:

  • Page has 20 images
  • Total image weight: 6 MB
  • User only sees first 3 images “above the fold”
  • Browser downloads all 6 MB anyway
  • Initial page load: 8 seconds on 3G

Result:

  • Slow Largest Contentful Paint (LCP)
  • Wasted bandwidth
  • Poor mobile experience
  • Lower rankings

The Solution: Load on Demand

Same page with lazy loading:

  • Browser loads first 3 images immediately (800 KB)
  • Remaining 17 images load as user scrolls (5.2 MB)
  • Initial page load: 2.1 seconds

Benefits:

  • Faster initial load
  • Better Core Web Vitals scores
  • Less bandwidth usage (users who don’t scroll don’t download)
  • Lower server costs

How Lazy Loading Works

Without lazy loading:

Page loads → All images download → User sees page
(8 seconds on slow connection)

With lazy loading:

Page loads → Visible images download → User sees page (fast!)

           User scrolls → More images download

Key concept: Only load resources when they’re about to be needed.


Native Lazy Loading (Modern Standard)

Browser Support

Supported (94%):

  • Chrome 76+ (2019)
  • Firefox 75+ (2020)
  • Edge 79+ (2020)
  • Safari 15.4+ (2022)
  • Opera 64+ (2019)

Not supported:

  • IE 11 (but IE is dead)
  • Older Safari versions (before 2022)

Fallback: Old browsers simply load images normally (no harm, just no benefit)

Implementation (Images)

Before:

<img src="photo.jpg" alt="Description">

After:

<img src="photo.jpg" alt="Description" loading="lazy">

That’s it. No JavaScript, no libraries, just one attribute.

Implementation (Iframes)

YouTube embeds:

<iframe
  src="https://www.youtube.com/embed/VIDEO_ID"
  loading="lazy"
  title="Video description"
></iframe>

Google Maps:

<iframe
  src="https://www.google.com/maps/embed?..."
  loading="lazy"
  title="Map location"
></iframe>

Why lazy load iframes:

  • YouTube embed: about 500 KB
  • Google Maps embed: about 1.2 MB
  • If they’re below the fold, delay loading until user scrolls

Loading Attribute Values

loading=“lazy”

Use for: Images and iframes below the fold

<img src="blog-image.jpg" alt="Screenshot" loading="lazy">

What happens:

  • Browser defers loading until image is about 1-2 viewport heights away
  • Loads just before user scrolls to it (seamless experience)
  • Saves initial page load time

loading=“eager”

Use for: Critical images you want to load immediately

<img src="hero.jpg" alt="Hero image" loading="eager">

What happens:

  • Browser loads image immediately (default behavior)
  • Same as omitting the attribute

When to use:

  • Hero images
  • Logos
  • Above-the-fold content

Auto (Default)

If you omit the attribute:

<img src="photo.jpg" alt="Description">
<!-- No loading attribute = browser decides -->

Browser default: Eager (loads immediately)


Best Practices

DO: Lazy Load Below-the-Fold Images

<!-- Hero image (above the fold) - load immediately -->
<img src="hero.jpg" alt="Hero" loading="eager" width="1200" height="630">

<!-- Blog post images (below the fold) - lazy load -->
<img src="screenshot-1.jpg" alt="Dashboard" loading="lazy" width="800" height="450">
<img src="screenshot-2.jpg" alt="Settings" loading="lazy" width="800" height="450">

DO: Lazy Load Iframes

<!-- YouTube video at bottom of article -->
<iframe
  src="https://www.youtube.com/embed/dQw4w9WgXcQ"
  loading="lazy"
  width="560"
  height="315"
  title="Tutorial video"
></iframe>

DO: Include Width and Height

Problem:

<img src="photo.jpg" alt="Photo" loading="lazy">
<!-- Missing dimensions -->

Result: Cumulative Layout Shift (CLS) as image loads and page jumps

Fix:

<img src="photo.jpg" alt="Photo" loading="lazy" width="800" height="600">

DON’T: Lazy Load Above-the-Fold Images

Bad:

<!-- Hero image at top of page -->
<img src="hero.jpg" alt="Hero" loading="lazy">

Result:

  • Delays LCP (Largest Contentful Paint)
  • Hurts Core Web Vitals scores
  • Slower perceived load time

Fix:

<img src="hero.jpg" alt="Hero" loading="eager">
<!-- Or omit loading attribute entirely -->

DON’T: Lazy Load Logo or Navigation Images

Bad:

<img src="logo.svg" alt="Company Logo" loading="lazy">

Why: Logo is always visible. No benefit from lazy loading.

DON’T: Lazy Load First 3-5 Images on Page

Rule of thumb: First about 600px of content should not be lazy loaded.

Check “above the fold” for your site:

  • Desktop: First about 800px
  • Mobile: First about 600px

JavaScript Lazy Loading (Legacy/Fallback)

For browsers without native loading="lazy" support (or advanced use cases), use JavaScript libraries.

Intersection Observer API (Modern)

HTML:

<img data-src="image.jpg" alt="Description" class="lazy">

JavaScript:

document.addEventListener("DOMContentLoaded", function() {
  const lazyImages = document.querySelectorAll('img.lazy');

  if ("IntersectionObserver" in window) {
    const imageObserver = new IntersectionObserver((entries, observer) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const img = entry.target;
          img.src = img.dataset.src;
          img.classList.remove('lazy');
          imageObserver.unobserve(img);
        }
      });
    });

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

Browser support: 95% (IE not supported)

Libraries (Easiest)

1. lazysizes (Most popular)

<!-- Include library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.2/lazysizes.min.js" async></script>

<!-- Use class="lazyload" -->
<img data-src="image.jpg" class="lazyload" alt="Description">

Features:

  • Auto-detects when to load
  • SEO-friendly
  • about 3 KB gzipped

2. lozad.js (Smallest)

<script src="https://cdn.jsdelivr.net/npm/lozad/dist/lozad.min.js"></script>
<script>
  const observer = lozad();
  observer.observe();
</script>

<img class="lozad" data-src="image.jpg" alt="Description">

Size: 1.9 KB (smallest option)


WordPress Implementation

Native Lazy Loading (Built-in)

Since WordPress 5.5 (2020):

<!-- WordPress automatically adds loading="lazy" to images -->
<img src="photo.jpg" alt="Description" loading="lazy">

No action needed if using WordPress 5.5+.

Check if it’s working:

  1. View page source
  2. Search for loading="lazy"
  3. Should be present on most <img> tags

Disable for Specific Images

Yoast SEO / Rank Math:

  1. Edit image in Media Library
  2. Alt text field → Add custom attribute
  3. Or use filter in theme:
<?php
// Disable lazy loading for specific images
add_filter('wp_lazy_loading_enabled', function($default, $tag_name, $context) {
  if ('img' === $tag_name && 'the_content' === $context) {
    // Check if it's a hero image class
    if (strpos($context, 'hero') !== false) {
      return false; // Disable lazy loading
    }
  }
  return $default;
}, 10, 3);
?>

Plugins (Advanced)

1. WP Rocket

  • Lazy loads images, iframes, videos
  • Defers YouTube embeds
  • Includes fallback for old browsers

2. Lazy Load by WP Rocket (Free)

  • Standalone lazy load plugin
  • No caching features (lightweight)

3. a3 Lazy Load

  • Free, lightweight
  • Works with images, videos, iframes

Testing Lazy Loading

Check Implementation

Method 1: View Source

<!-- Look for loading="lazy" attribute -->
<img src="photo.jpg" alt="Description" loading="lazy">

Method 2: Chrome DevTools

  1. Open DevTools (F12)
  2. Network tab
  3. Filter: Images
  4. Scroll down the page
  5. Watch images load as you scroll

What you should see:

  • Initial page load: 3-5 images
  • As you scroll: More images appear in Network tab

Measure Performance Impact

Before lazy loading:

  1. Open Chrome DevTools
  2. Lighthouse tab
  3. Run audit
  4. Note “Total Page Size” and “LCP”

After lazy loading:

  1. Add loading="lazy" to images
  2. Run Lighthouse again
  3. Compare scores:
    • Total Page Size: Should decrease
    • LCP: Should improve (if not lazy loading hero)

Expected improvement:

  • Page size: ⬇️ 30-60% (depending on image count)
  • LCP: ⬆️ 10-30% faster (if hero isn’t lazy loaded)

Validate Core Web Vitals

Google PageSpeed Insights:

  1. Visit PageSpeed Insights
  2. Enter your URL
  3. Check “Field Data” section:
    • LCP should be under 2.5s
    • CLS should be under 0.1

Common Mistakes

Lazy Loading the LCP Image

Problem:

<!-- Hero image (LCP element) with lazy loading -->
<img src="hero.jpg" alt="Hero" loading="lazy">

Result:

  • LCP delayed by 500-1000ms
  • Lower PageSpeed score
  • “Largest Contentful Paint element was lazy-loaded” warning

Fix:

<img src="hero.jpg" alt="Hero" loading="eager">

How to identify LCP image:

  1. Run PageSpeed Insights
  2. Look for “Largest Contentful Paint element”
  3. Note which image it is
  4. Don’t lazy load that image

Missing Dimensions (Layout Shift)

Problem:

<img src="photo.jpg" alt="Photo" loading="lazy">
<!-- Missing width/height -->

Result:

  • Page jumps as images load
  • High CLS (Cumulative Layout Shift)
  • Poor user experience

Fix:

<img src="photo.jpg" alt="Photo" loading="lazy" width="800" height="600">

Or use CSS:

img.lazy {
  aspect-ratio: 16 / 9; /* Reserves space */
}

Lazy Loading in Unsupported Browsers Without Fallback

Problem: Using native lazy loading with old Safari (before 15.4)

Result: Images load normally (no harm, but no benefit)

Better: Use JavaScript library for maximum browser support


Impact on SEO

Google’s Stance

From Google’s documentation:

“Lazy loading is recommended for images and iframes.”

SEO benefits:

  • Faster page speed = ranking boost
  • Better Core Web Vitals = ranking boost
  • Lower bounce rate (faster = more engagement)

No penalties:

  • Google can crawl lazy-loaded images (uses src, not data-src)
  • No “hidden content” penalty

Googlebot Behavior

Native lazy loading:

<img src="photo.jpg" alt="Description" loading="lazy">

Googlebot sees src attribute, crawls image normally

JavaScript lazy loading (proper implementation):

<img src="photo.jpg" data-src="photo-large.jpg" class="lazy">
<noscript>
  <img src="photo-large.jpg" alt="Description">
</noscript>

Googlebot sees noscript fallback, crawls image

Bad implementation:

<img data-src="photo.jpg" class="lazy">
<!-- No src, no noscript fallback -->

Googlebot may not discover image


What Surmado Checks

Surmado Scan looks for:

  • Lazy loading implemented on below-the-fold images
  • LCP image NOT lazy-loaded
  • Width and height attributes present
  • Proper fallback for unsupported browsers
  • Iframes (YouTube, Maps) using lazy loading

Quick Reference

Standard implementation:

<!-- Above-the-fold (hero) -->
<img src="hero.jpg" alt="Hero" width="1200" height="630">

<!-- Below-the-fold (lazy load) -->
<img src="image.jpg" alt="Description" loading="lazy" width="800" height="600">

<!-- YouTube embed -->
<iframe src="https://youtube.com/embed/..." loading="lazy" width="560" height="315"></iframe>

What to lazy load:

  • Images below the fold
  • YouTube/Vimeo embeds
  • Google Maps iframes
  • Product thumbnails (e-commerce)

What NOT to lazy load:

  • Hero images
  • Logos
  • Above-the-fold content
  • First 3-5 images on page

Related: Image Formats: WebP & AVIF | Core Web Vitals Explained | Mobile Performance

Help Us Improve This Article

Know a better way to explain this? Have a real-world example or tip to share?

Contribute and earn credits:

  • Submit: Get $25 credit (Signal, Scan, or Solutions)
  • If accepted: Get an additional $25 credit ($50 total)
  • Plus: Byline credit on this article
Contribute to This Article