Lazy Loading: Defer Offscreen Images and Iframes
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:
- View page source
- Search for
loading="lazy" - Should be present on most
<img>tags
Disable for Specific Images
Yoast SEO / Rank Math:
- Edit image in Media Library
- Alt text field → Add custom attribute
- 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
- Open DevTools (F12)
- Network tab
- Filter: Images
- Scroll down the page
- 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:
- Open Chrome DevTools
- Lighthouse tab
- Run audit
- Note “Total Page Size” and “LCP”
After lazy loading:
- Add
loading="lazy"to images - Run Lighthouse again
- 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:
- Visit PageSpeed Insights
- Enter your URL
- 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:
- Run PageSpeed Insights
- Look for “Largest Contentful Paint element”
- Note which image it is
- 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, notdata-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
Was this helpful?
Thanks for your feedback!
Have suggestions for improvement?
Tell us moreHelp 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