10% of pages lazy-load their LCP image. This single mistake can add 500ms+ to your LCP.
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:
loading="lazy", skips the imageThe "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.
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.
loading="lazy"The audit is explicit: "Largest Contentful Paint image was lazily loaded"
It tells you:
loading="lazy" (or equivalent JavaScript lazy-loading)View page source, search for your hero image URL, check for 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.
While you're at it, tell the browser this image is critical:
<img
src="hero.webp"
fetchpriority="high"
alt="Hero"
>
<!-- 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>
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)
}
})
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'
}
})
Native lazy-loading uses the browser's own heuristics to determine when to load images. When you add loading="lazy":
For images already visible, this adds latency:
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.
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: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);
After implementing:
loading="lazy"Expected improvement: 100-500ms depending on image size and page complexity.
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">
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'
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"
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>
Often appears alongside:
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.