Fix LCP Lazy Loaded for Better LCP
10% of pages lazy-load their LCP image. This single mistake can add 500ms+ to your LCP.
What's the Problem?
loading="lazy" is a performance optimization—for images below the fold. It tells browsers to defer downloading images until they're near the viewport. This saves bandwidth and speeds up initial load.
But your LCP image is already in the viewport. It's the first thing users see. Lazy-loading it creates this sequence:
- Browser parses HTML, sees
loading="lazy", skips the image - Browser downloads CSS, JavaScript, other resources
- Browser calculates layout
- Browser realizes the image is in viewport
- Now browser starts downloading the image
- LCP fires after unnecessary delay
The "Largest Contentful Paint image was lazily loaded" audit catches this anti-pattern. It's one of the easiest LCP issues to fix—just remove an attribute.
The Anti-Pattern
This happens constantly with hero images:
<!-- The classic mistake: lazy-loading the hero -->
<section class="hero">
<img
src="hero.webp"
loading="lazy" <!-- This destroys your LCP -->
alt="Welcome to our site"
>
</section>
Developers add loading="lazy" to all images as a "best practice." It's not. It's a targeted optimization for specific images.
How to Identify This Issue
Chrome DevTools
- Open Elements panel (F12)
- Find your LCP image (usually the largest visible image)
- Check if it has
loading="lazy"
Lighthouse
The audit is explicit: "Largest Contentful Paint image was lazily loaded"
It tells you:
- Which element is your LCP
- That it has
loading="lazy"(or equivalent JavaScript lazy-loading)
Performance Panel
- Record a page load
- Find the LCP marker
- Look at when the LCP image request started
- Large gap after HTML = likely lazy-load issue
Quick Check
View page source, search for your hero image URL, check for loading="lazy".
The Fix
Primary Solution: Remove loading="lazy"
<!-- Before: lazy-loaded LCP image -->
<img src="hero.webp" loading="lazy" alt="Hero">
<!-- After: default eager loading -->
<img src="hero.webp" alt="Hero">
That's it. loading="eager" is the default. You don't need to specify it.
Add fetchpriority for Maximum Impact
While you're at it, tell the browser this image is critical:
<img
src="hero.webp"
fetchpriority="high"
alt="Hero"
>
Complete Pattern: Above vs Below Fold
<!-- Above the fold: LCP image -->
<header>
<img
src="hero.webp"
fetchpriority="high"
decoding="async"
width="1200"
height="600"
alt="Hero"
>
</header>
<!-- Below the fold: lazy load these -->
<section class="products">
<img src="product-1.webp" loading="lazy" alt="Product 1">
<img src="product-2.webp" loading="lazy" alt="Product 2">
<img src="product-3.webp" loading="lazy" alt="Product 3">
</section>
Fix JavaScript Lazy-Loading Libraries
If you use a library like lazysizes, vanilla-lazyload, or lozad, exclude above-fold images.
<!-- Before: library lazy-loads everything -->
<img data-src="hero.webp" class="lazyload" alt="Hero">
<!-- After: real src, no lazy class -->
<img src="hero.webp" fetchpriority="high" alt="Hero">
// If using Intersection Observer directly
const observer = new IntersectionObserver(loadImage)
// Don't observe the hero image
document.querySelectorAll('img[data-src]').forEach((img) => {
if (!img.closest('.hero')) {
observer.observe(img)
}
})
Fix Native Lazy-Loading Applied Globally
Common mistake: CSS or JavaScript that adds lazy loading to all images.
// Wrong: blanket lazy-loading
document.querySelectorAll('img').forEach((img) => {
img.loading = 'lazy'
})
// Right: skip above-fold images
document.querySelectorAll('img').forEach((img) => {
if (!img.hasAttribute('fetchpriority')) {
img.loading = 'lazy'
}
})
Why This Works
Native lazy-loading uses the browser's own heuristics to determine when to load images. When you add loading="lazy":
- The browser delays the fetch until layout is calculated
- It checks if the image is within a "distance threshold" of the viewport
- Only then does it start the download
For images already visible, this adds latency:
- Layout calculation time (50-200ms)
- Intersection Observer overhead (10-50ms)
- Network request queuing
Removing lazy-loading means the browser starts downloading immediately during HTML parsing—exactly what you want for critical content.
The combination of removing lazy-load and adding fetchpriority can improve LCP by 200-800ms on image-heavy pages.
Framework-Specific Solutions
next/image lazy-loads by default. Use priority to override:// Wrong: lazy by default
<Image src="/hero.jpg" alt="Hero" width={1200} height={600} />
// Right: priority disables lazy loading
<Image src="/hero.jpg" alt="Hero" width={1200} height={600} priority />
priority prop:- Sets loading="eager"
- Adds fetchpriority="high"
- Creates a preload hint
priority.NuxtImg and NuxtPicture lazy-load by default. Override with loading="eager":<!-- Wrong: lazy by default -->
<NuxtImg src="/hero.jpg" alt="Hero" />
<!-- Right: eager loading for LCP -->
<NuxtImg
src="/hero.jpg"
alt="Hero"
loading="eager"
preload
/>
preload prop adds a link rel="preload" for even faster loading.// Wrong: react-lazy-load-image-component on LCP
import { LazyLoadImage } from 'react-lazy-load-image-component'
<LazyLoadImage src="/hero.jpg" />
// Right: regular img for LCP
<img src="/hero.jpg" fetchPriority="high" alt="Hero" />
// Use lazy-loading library only for below-fold
<LazyLoadImage src="/product.jpg" />
loading="lazy" to all images by default since 5.5. For LCP images:// Option 1: Remove lazy from specific images
add_filter('wp_img_tag_add_loading_attr', function($value, $image, $context) {
if (strpos($image, 'hero') !== false) {
return false;
}
return $value;
}, 10, 3);
// Option 2: Disable on featured images
add_filter('wp_img_tag_add_loading_attr', function($value, $image, $context) {
if ($context === 'the_content' && is_singular()) {
return false; // Let theme control loading
}
return $value;
}, 10, 3);
Verify the Fix
After implementing:
- Elements panel — LCP image should NOT have
loading="lazy" - Network tab — LCP image should start downloading early (not waiting for layout)
- Lighthouse — "Largest Contentful Paint image was lazily loaded" warning should be gone
- Performance panel — LCP image request should start sooner in the waterfall
Expected improvement: 100-500ms depending on image size and page complexity.
Common Mistakes
Only Removing the Attribute
Removing loading="lazy" helps, but combine with fetchpriority="high" for maximum impact.
<!-- Okay: removed lazy -->
<img src="hero.webp" alt="Hero">
<!-- Better: eager + priority -->
<img src="hero.webp" fetchpriority="high" alt="Hero">
CMS or Theme Overriding Your Fix
Your CMS or theme might re-add lazy loading. Test the rendered HTML, not just your source.
curl -s https://yoursite.com | grep -A2 'hero.webp'
Build Tools Adding Lazy Loading
Some build tools or plugins add lazy loading automatically. Check your build config.
// webpack.config.js or similar
// Look for image optimization plugins that add loading="lazy"
Forgetting Mobile
Mobile layout might have a different LCP image than desktop. Test both.
<!-- Desktop LCP might be hero-desktop.webp -->
<!-- Mobile LCP might be hero-mobile.webp -->
<!-- Both need eager loading -->
<picture>
<source media="(max-width: 768px)" srcset="hero-mobile.webp">
<img src="hero-desktop.webp" fetchpriority="high" alt="Hero">
</picture>
Related Issues
Often appears alongside:
- Prioritize LCP Image — The positive version of this fix
- Large Images — An eagerly-loaded 2MB image is still slow
- Client-Side Rendering — JavaScript-rendered images have similar discovery delays
Test Your Entire Site
Different page templates have different lazy-loading behavior. Your blog posts might be fine while product pages lazy-load heroes. Unlighthouse scans your entire site and identifies every page with lazy-loaded LCP images.