Lazy Loading Guide: Improve Page Speed Without Breaking SEO
Learn how to implement lazy loading correctly for images, videos, and content. Improve page speed while avoiding common SEO pitfalls.
Lazy loading defers the loading of resources until they’re needed, dramatically improving initial page load times. But implement it wrong, and you can hurt your SEO and user experience.
Here’s how to do lazy loading right.
What Is Lazy Loading?
Lazy loading is a technique that delays loading non-critical resources until they’re needed. Instead of loading everything at once, resources load as the user scrolls or interacts.
Commonly lazy loaded:
- Images below the fold
- Videos and embeds
- Heavy JavaScript modules
- Comments sections
- Related content
Benefits of Lazy Loading
Performance Benefits
- Faster initial load: Less to download upfront
- Reduced bandwidth: Only load what’s viewed
- Better LCP: Prioritizes above-fold content
- Lower bounce rate: Page appears ready faster
SEO Benefits
- Better Core Web Vitals: Especially LCP
- Improved page experience: Users don’t wait
- Reduced server load: Fewer requests initially
Native Lazy Loading
Modern browsers support native lazy loading—no JavaScript required.
For Images
<!-- Below-fold images: lazy load -->
<img src="photo.jpg" loading="lazy" alt="Description" width="800" height="600">
<!-- Above-fold images: eager load (default) -->
<img src="hero.jpg" loading="eager" alt="Hero" width="1200" height="600">
For Iframes
<!-- Lazy load embedded content -->
<iframe src="https://youtube.com/embed/xyz" loading="lazy"></iframe>
Browser Support
Native lazy loading is supported by:
- Chrome 77+
- Firefox 75+
- Safari 15.4+
- Edge 79+
For older browsers, content loads normally (graceful degradation).
What NOT to Lazy Load
The LCP Element
Never lazy load your LCP element. This is typically:
- Hero images
- Header images
- First product image
- Featured article image
Lazy loading the LCP element delays the largest content, directly hurting your LCP score.
<!-- WRONG: Lazy loading the hero -->
<img src="hero.jpg" loading="lazy" alt="Hero">
<!-- CORRECT: Eager load the hero -->
<img src="hero.jpg" loading="eager" alt="Hero">
<!-- Or simply omit loading attribute (defaults to eager) -->
<img src="hero.jpg" alt="Hero">
Content Google Needs to See
If lazy loading prevents content from being in the initial HTML, Google may not see it:
- Critical text content should not require scrolling to load
- Internal links should be in initial HTML
- Structured data references should be immediately available
JavaScript Lazy Loading
For more control, use JavaScript with Intersection Observer.
Basic Implementation
// Create observer
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
}, {
rootMargin: '200px' // Load 200px before entering viewport
});
// Observe all lazy images
document.querySelectorAll('img[data-src]').forEach(img => {
observer.observe(img);
});
HTML for JavaScript Lazy Loading
<!-- Use data-src instead of src -->
<img data-src="photo.jpg" alt="Description" width="800" height="600">
When to Use JavaScript vs Native
Use native lazy loading when:
- Simple image/iframe lazy loading
- No need for custom threshold
- Want simplest implementation
Use JavaScript lazy loading when:
- Need custom loading threshold
- Want loading animations
- Require fallback for older browsers
- Need more control over timing
Preventing CLS with Lazy Loading
Lazy loaded images can cause layout shift if space isn’t reserved.
Always Include Dimensions
<!-- Good: Dimensions prevent CLS -->
<img
src="photo.jpg"
loading="lazy"
width="800"
height="600"
alt="Description"
>
Use Aspect Ratio
<img
src="photo.jpg"
loading="lazy"
alt="Description"
style="aspect-ratio: 4/3; width: 100%;"
>
Use Placeholder Techniques
Low Quality Image Placeholder (LQIP):
<img
src="photo-tiny.jpg"
data-src="photo-full.jpg"
alt="Description"
class="lazyload blur-up"
>
CSS Blur-up effect:
.lazyload {
filter: blur(10px);
transition: filter 0.3s;
}
.lazyload.loaded {
filter: blur(0);
}
Solid color placeholder:
<div style="background: #f0f0f0; aspect-ratio: 4/3;">
<img
src="photo.jpg"
loading="lazy"
alt="Description"
onload="this.parentElement.style.background='none'"
>
</div>
Lazy Loading Videos
YouTube Embeds
Don’t embed YouTube directly—it loads megabytes of scripts.
Better: Load facade, embed on click:
<div class="youtube-facade" data-video-id="abc123">
<img src="youtube-thumbnail.jpg" alt="Video title">
<button class="play-button">Play</button>
</div>
<script>
document.querySelectorAll('.youtube-facade').forEach(el => {
el.addEventListener('click', () => {
const iframe = document.createElement('iframe');
iframe.src = `https://youtube.com/embed/${el.dataset.videoId}?autoplay=1`;
iframe.allow = 'autoplay';
el.replaceWith(iframe);
});
});
</script>
Native Video
<video
poster="video-poster.jpg"
preload="none"
controls
>
<source src="video.mp4" type="video/mp4">
</video>
preload="none" prevents video from loading until played.
Lazy Loading Third-Party Scripts
Heavy scripts can delay interactivity.
Delay Until Interaction
let loaded = false;
function loadChatWidget() {
if (loaded) return;
loaded = true;
const script = document.createElement('script');
script.src = 'https://chat-widget.com/embed.js';
document.body.appendChild(script);
}
// Load on scroll or after delay
window.addEventListener('scroll', loadChatWidget, { once: true });
setTimeout(loadChatWidget, 5000);
Common Scripts to Defer
- Chat widgets
- Analytics (non-essential)
- Social sharing buttons
- Comment systems
- Email signup popups
SEO Considerations
What Google Sees
Google renders JavaScript, but:
- Rendering is delayed (second wave of indexing)
- Some lazy-loaded content may be missed
- Content in initial HTML is always seen
Best Practices for SEO
- Critical content in initial HTML: Don’t lazy load main content
- Use native lazy loading: Google understands
loading="lazy" - Provide fallbacks: Content should work without JavaScript
- Test in Search Console: URL Inspection shows rendered page
Testing What Google Sees
- Google Search Console > URL Inspection > View tested page
- Compare crawled HTML vs rendered HTML
- Check if lazy-loaded content appears
Lazy Loading Checklist
Implementation
- Native
loading="lazy"on below-fold images - LCP element NOT lazy loaded
- Iframes lazy loaded where appropriate
- Third-party scripts deferred
Preventing CLS
- All lazy images have width/height
- Placeholders in place during load
- Aspect ratios defined
SEO Safety
- Critical content in initial HTML
- Internal links not lazy loaded
- Tested in Search Console URL inspection
User Experience
- Loading appears smooth
- No flash of empty space
- Works with JavaScript disabled
Related Resources:
Tags:
Share this article:
Ready to improve your SEO?
Get a free SEO audit and see exactly what needs fixing on your site
Start Free Audit