Fix LCP Lazy Loaded for Better LCP

Why lazy-loading your LCP image destroys performance and how to fix it. Remove loading="lazy" from above-the-fold images.
Harlan WiltonHarlan Wilton5 min read Published

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:

  1. Browser parses HTML, sees loading="lazy", skips the image
  2. Browser downloads CSS, JavaScript, other resources
  3. Browser calculates layout
  4. Browser realizes the image is in viewport
  5. Now browser starts downloading the image
  6. 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

  1. Open Elements panel (F12)
  2. Find your LCP image (usually the largest visible image)
  3. 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

  1. Record a page load
  2. Find the LCP marker
  3. Look at when the LCP image request started
  4. 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":

  1. The browser delays the fetch until layout is calculated
  2. It checks if the image is within a "distance threshold" of the viewport
  3. 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.jsnext/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 />
The priority prop:
  • Sets loading="eager"
  • Adds fetchpriority="high"
  • Creates a preload hint
Only use on the LCP image. One image per page should have priority.
NuxtNuxtImg 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
/>
The preload prop adds a link rel="preload" for even faster loading.
ReactWatch for lazy-loading libraries and HOCs:
// 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" />
WordPressWordPress adds 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:

  1. Elements panel — LCP image should NOT have loading="lazy"
  2. Network tab — LCP image should start downloading early (not waiting for layout)
  3. Lighthouse — "Largest Contentful Paint image was lazily loaded" warning should be gone
  4. 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>

Often appears alongside:

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.