ImagePDF.Tools
Performance & SEO

Lazy Loading Images: The One-Line Fix That Backfires on LCP

N
NikolaLast updated on June 20, 2026 · 9 min read

Summary

loading="lazy" on your hero image is one of the most common LCP killers. Here's exactly why it happens, how to find it, and how to fix the damage.

The snippet is everywhere in blog posts and performance checklists: "add loading="lazy" to all your images." It's presented as a one-line performance win with no downside. In practice, applying it to every image, including the hero banner, is one of the fastest ways to fail Largest Contentful Paint (LCP) and get a red score in PageSpeed Insights.

The browser delay introduced by lazy loading a hero image is typically 200–800ms on a real page load. That's not a rounding error, it's often the difference between a good LCP score (under 2.5s) and a needs-improvement score (2.5–4s). The fix is understanding which images to lazy-load and which to not, not blanket application of the attribute.

Lazy loading: above-fold hero image loads immediately, entering-viewport image starts loading on scroll, below-fold image stays unloaded | ImagePDF.Tools
The hero image must load immediately, lazy loading is only beneficial below the fold.

What loading="lazy" Actually Does to the Network Request

Native lazy loading (loading="lazy") instructs the browser to defer the image's network request until the image is within a threshold distance from the viewport. The threshold varies by connection speed: roughly 1000–1500px on fast connections, less on slow ones. The browser's preload scanner, which reads ahead of HTML parsing to discover and start downloading resources early, skips lazy-loaded images entirely.

For genuinely off-screen images, this is the correct behavior. The browser only fetches what the user is about to see, reducing the initial network payload and freeing bandwidth for critical resources. For any image that is visible on page load, however, lazy loading is actively harmful.

The LCP Trap: How a One-Line Attribute Breaks Your Score

LCP is the time when the largest visible content element becomes fully rendered. For most pages, that's the hero image. LCP is measured from navigation start to when that element is painted. Here's what happens when the hero image has loading="lazy":

  1. 1.Browser parses HTML → sees hero image with loading="lazy" → does NOT start fetching it
  2. 2.Browser continues parsing, discovers and fetches CSS and JavaScript
  3. 3.JavaScript executes, layout is calculated
  4. 4.Browser now has a viewport and calculates that the hero image is in view
  5. 5.Browser starts fetching the hero image
  6. 6.Image downloads and paints

The hero image request starts after steps 2–4, adding 200–800ms of avoidable delay. Without lazy loading, the preload scanner would have started the request during step 1, overlapping with CSS and JavaScript downloads.

⚠️

Never add loading="lazy" to your LCP image. PageSpeed Insights will flag this with the message "Largest Contentful Paint image was lazily loaded." The hero banner, above-fold product photo, or any image visible without scrolling should use eager loading (the default) or be explicitly excluded from any lazy-loading scripts.

Correct Attribute Configuration by Image Position

Image positionloadingfetchprioritydecodingWhy
LCP / hero (above fold)eager (omit attribute)highsyncFetch immediately, high priority, sync decode for guaranteed paint timing
Above fold, not LCPeager (omit attribute)auto (omit)asyncLoad eagerly; don't delay with lazy
First scroll (partially visible)eager (omit attribute)auto (omit)asyncPreload scanner discovers naturally; lazy adds unnecessary delay
Well below foldlazyauto (omit)asyncDefinite win, user hasn't scrolled there yet
Carousel / hidden tablazylowasyncMay never be viewed; lowest priority
loading, fetchpriority, and decoding attributes by image position
html
<!-- ✅ LCP hero: eager (default), high priority, sync decode -->
<img
  src="hero.webp"
  alt="Product hero"
  width="1200"
  height="630"
  fetchpriority="high"
  decoding="sync"
>
<!-- Omitting loading= is the same as loading="eager", the default -->

<!-- ✅ Below-fold gallery images: lazy, async decode -->
<img
  src="product-detail.webp"
  alt="Product detail"
  width="800"
  height="800"
  loading="lazy"
  decoding="async"
  width="800"
  height="800"
>

<!-- ❌ LCP image with lazy loading, adds 200–800ms to LCP -->
<img src="hero.webp" loading="lazy" alt="Hero">

<!-- ❌ Multiple images with fetchpriority="high", defeats the purpose -->
<img src="hero.webp" fetchpriority="high" alt="">
<img src="banner.webp" fetchpriority="high" alt="">

How to Find Lazy-Loaded LCP Images on Your Site

Two quick diagnostic methods:

  1. 1.PageSpeed Insights: Run your URL at pagespeed.web.dev. If the LCP image is lazy-loaded, you'll see a specific warning: "Largest Contentful Paint image was lazily loaded." The recommendation will tell you the element and its loading attribute.
  2. 2.Chrome DevTools: Open DevTools → Performance tab → record a page load → look for the LCP marker. Click it to identify the LCP element. Then inspect that element's HTML to check whether loading="lazy" is present.

Lazy Loading in JavaScript-Heavy Frameworks

Many frameworks and libraries apply lazy loading automatically to all images. React libraries like react-lazyload, Next.js's default Image component, and WordPress lazy load plugins can incorrectly apply lazy loading to above-fold content.

  • Next.js: The built-in <Image> component applies lazy loading by default. Set priority={true} on your LCP image to disable lazy loading and add fetchPriority="high" automatically.
  • WordPress: WordPress 5.5+ adds loading="lazy" to all images by default. To disable for a specific image: add_filter('wp_lazy_loading_enabled', fn($def, $tag) => $tag === 'img' ? false : $def, 10, 2); Or use the loading="eager" attribute explicitly.
  • Gatsby, Astro, SvelteKit: Each has its own image component defaults, check documentation for how to mark priority images.

The preload Link Tag: Proactive LCP Help

For maximum LCP performance, use a <link rel="preload"> in the <head> to start fetching the LCP image immediately, before the browser even parses the body. Combined with fetchpriority="high", this is the most effective combination for improving LCP.

html
<head>
  <!-- Preload the LCP image, starts fetching before body parsing -->
  <link
    rel="preload"
    as="image"
    href="hero.webp"
    fetchpriority="high"
  >

  <!-- For responsive images with srcset, include imagesrcset and imagesizes -->
  <link
    rel="preload"
    as="image"
    imagesrcset="hero-400.webp 400w, hero-800.webp 800w, hero-1200.webp 1200w"
    imagesizes="(max-width: 600px) 100vw, 1200px"
    fetchpriority="high"
  >
</head>

What lazy loading Won't Fix

Correct lazy loading is necessary but not sufficient for a good LCP score. Even with perfect lazy loading strategy, an oversized or poorly compressed LCP image will still fail Core Web Vitals. The full LCP optimisation stack:

  • Step 1: Compress the hero image, target under 150–200 KB for any above-fold image
  • Step 2: Serve in WebP or AVIF format via <picture> srcset
  • Step 3: Add fetchpriority="high" to the hero image
  • Step 4: Add <link rel="preload"> in the head for the hero image
  • Step 5: Apply loading="lazy" only to genuinely below-fold images
  • Step 6: Set explicit width and height on every image to eliminate layout shift (CLS)

Bottom Line

Lazy loading is a powerful optimisation when applied selectively. Applied uniformly, it actively harms the performance of above-fold content. The rule is simple: the LCP image and any other image visible without scrolling must never have loading="lazy". Everything at least one viewport height below the fold benefits from lazy loading. Start with your hero image: make sure it's well-compressed, eagerly loaded, and has fetchpriority="high". Those three changes together are often worth 500ms or more of LCP improvement.

Frequently asked questions

Does loading="lazy" hurt LCP?
Yes, if applied to the LCP image. The browser's preload scanner skips lazy-loaded images, meaning the hero image request doesn't start until after HTML parsing, CSS processing, and layout are complete. This adds 200–800ms to LCP on typical page loads. Never lazy-load an image that is visible without scrolling.
How do I find out which image is my LCP element?
Run your URL through PageSpeed Insights (pagespeed.web.dev), it identifies the LCP element and shows its attributes. Alternatively, open Chrome DevTools → Performance tab → record a page load → look for the LCP timing marker, and click it to identify the element.
Should I use fetchpriority="high" on all images?
No, only on the single LCP image. fetchpriority="high" signals that this image should be prioritised above other resources in the download queue. Applying it to multiple images dilutes the signal and can make all of them slower by competing for the same prioritised slots. Use it on one image per page: the hero or main above-fold image.
How do I disable lazy loading on the WordPress hero image?
WordPress 5.5+ adds loading="lazy" to all images by default. The most targeted fix is to add the loading="eager" attribute explicitly to your hero image in your template. Alternatively, use a filter: add_filter('wp_lazy_loading_enabled', function($default, $tag_name) { return ($tag_name === 'img') ? false : $default; }, 10, 2);, though this disables lazy loading for all images, which has its own trade-offs.
Is lazy loading different in Next.js?
Yes. Next.js's built-in component applies lazy loading by default to all images. To disable lazy loading for the LCP image, set the priority prop: . This tells Next.js to treat the image as high-priority, removing lazy loading and adding fetchpriority="high" and a preload link in the head automatically.

Sources & references

This article was researched and written by Nikola, drawing on the following primary sources and documentation:

Ready to try it?

All tools run entirely in your browser, no uploads, no account required.

Compress Image
You're offline, cached tools still work