Lazy Loading Mistakes That Hurt LCP
Lazy loading saves bandwidth below the fold. It breaks LCP when it is applied to the hero, to fonts, or to every image. These are the patterns that delay largest contentful paint, and how to undo them.
By Rehan Baqir
Lazy loading saves bandwidth on images the visitor may never scroll to. It delays Largest Contentful Paint when loading="lazy" is on the hero or on any other element that paints largest in the first screen. The LCP image needs loading="eager" (or no lazy attribute) and, where the browser supports it, fetchpriority="high". Switching that one image to eager removes the fetch delay. It does not shrink a 4 MB file or fix a slow origin TTFB.
Someone added loading="lazy" to every <img> in a template, including the hero. Lighthouse LCP regressed 1.2 seconds. Deleting that attribute from that one tag was the fix.
Below the fold, lazy loading is the reasonable default. On whatever paints largest first, it is the wrong default.
How native lazy load works
loading="lazy" tells the browser to defer the fetch until the image approaches the viewport. The margin is implementation-defined. Images a visitor never scrolls to never use that bandwidth.
The side effect: an LCP image inside that lazy threshold starts late. The LCP score gets worse, and both SEO and the experience take the hit.
Mistake 1: Lazy hero / featured image
Wrong:
<img src="hero.webp" loading="lazy" alt="..." class="hero">
Right:
<img src="hero.webp" loading="eager" fetchpriority="high" alt="..." class="hero">
Hero Image Weight Budget covers size, which still matters after the attribute is correct.
Mistake 2: Blanket CMS lazy plugin
WordPress and similar plugins add lazy to every content image. The first image in the post is often the LCP element. Exclude the first content image, or the featured thumbnail, from that plugin.
Mistake 3: Lazy background heroes
A CSS background-image on a hero div has no native lazy attribute. A JavaScript lazy background delays LCP further. Prefer an <img> with object-fit, or put the background in critical CSS so it is not waiting on a script.
Mistake 4: Lazy plus a low-quality placeholder swap
LQIP and blur-up techniques can shift LCP timing if the final image loads late. Put the high-resolution LCP src in the initial HTML. Do not inject it after a consent or analytics script.
Mistake 5: Carousel first slide lazy
Slider libraries that lazy-load slide 1 delay the thing the visitor can already see. The first visible slide should be eager.
Mistake 6: Incorrect sizes/srcset
An oversized srcset candidate plus lazy loading delays the image twice. Fix dimensions with Resize Images for Web and make the sizes attribute match the layout.
What to lazy load
- Blog comment avatars
- Footer badges
- Related-post thumbnails below the fold
- Gallery images after the first row
- Embed placeholders that load on click
fetchpriority hint
<img fetchpriority="high" ...> <img loading="lazy" fetchpriority="low" ...> ```
Chromium support is broad. Other browsers can ignore the hint.
## Picture element
The first `<source>` that matches the LCP image should not sit behind a lazy parent that blocks it. Check the network waterfall along with the markup.
## Framework pitfalls
- Next.js `Image`: set the `priority` prop on the LCP image
- A React lazy component that wraps the hero
- A custom Intersection Observer that treats the top of the viewport as lazy
## Measure the fix
Compare Lighthouse LCP before and after, on mobile throttling. Field data in CrUX lags about 28 days. A lab test is the immediate feedback.
[Image Compression for Web](/blog/image-compression-for-web) belongs in the same pass as the lazy-loading change.
## Native lazy load distance
Browsers preload lazy images within an implementation-defined margin around the viewport. Chrome starts the fetch slightly before the image is visible. That margin is still too late for the LCP element. The LCP image has to be eager.
## Intersection Observer custom lazy
Libraries that use a 200px rootMargin fetch a bit early. You can tune rootMargin. A generic lazy script should still skip any selector that is above the fold.
## WordPress native lazy
WordPress 5.5 and later adds loading=lazy to content images. Filter `wp_img_tag` so the first image, or the featured image, does not get the attribute. Put the snippet in the theme's functions.php and document it in the theme README.
## Worked example: fixing a 1.1-second LCP penalty on a product page
1. Before: the main product gallery rendered `<img src="shoe-hero-1200w.webp" loading="lazy" width="600" height="600">`. Because `loading="lazy"` waits until layout is computed before the image fetch starts, Chrome delayed the request by 420 ms on a throttled 4G test. LCP landed at 3.4 s.
2. After: remove `loading="lazy"` from the first slide only, add `fetchpriority="high"` and `decoding="async"`, and keep `loading="lazy"` on thumbnails 2 through 8 (sized in [Resize Image](/image/resize-image) and compressed in [Compress Image](/image/compress-image)).
3. Result: the primary image starts downloading with the document head. LCP dropped to 2.1 s, and the below-the-fold gallery shots still skip their bytes until they are needed.
## Limitations
Switching an above-the-fold hero from `lazy` to `eager` removes the browser's fetch delay. It cannot compensate for a 4 MB uncompressed image or a slow origin TTFB. Pair eager LCP loading with proper dimensions ([Resize Image](/image/resize-image)) and modern compression ([WebP Converter](/image/webp-converter)).
## Conclusion
Leave lazy loading on everything except the LCP element. One hero with `loading="lazy"` can cost about a second of LCP.
Audit the first screen. Set eager and fetchpriority high on the image that wins, and lazy-load the rest. Deleting that one attribute often moves the score more than a CDN header tweak.
Frequently Asked Questions
Common questions answered to help you get the most from this tool.