Lazy loading images and a fast Largest Contentful Paint (LCP) pull in opposite directions when the same rule is applied to every <img>. Deferring offscreen bytes is usually right, but deferring the hero that PageSpeed Insights later names as LCP is usually wrong. The loading="lazy" attribute is only a browser hint for that trade-off. It is not a global “make the site faster” switch that themes and optimisation plugins can sprinkle without review.
What follows is a decision guide for agencies and product teams: when lazy loading images helps, when it hurts LCP or Cumulative Layout Shift (CLS), and how to prove the change with scheduled lab checks rather than one green Lighthouse run. For the wider image pipeline (formats, sizing, CDN), keep Image Optimisation Strategies for Better LCP Scores open beside this piece. When text paint competes with the hero, Font Subsetting for Web Performance: 4 Tools to Reduce Font File Size and Improve LCP still belongs in the same sprint.
What the loading="lazy" attribute does
The HTML loading attribute on <img> and <iframe> tells supporting browsers whether to fetch the resource immediately or wait until it is near the viewport. Per MDN’s lazy loading guide and web.dev’s browser-level image lazy loading article, loading="lazy" is the deferred path and loading="eager" (or omitting the attribute for images that default to eager) is the immediate path. Native lazy loading replaced most custom Intersection Observer snippets for ordinary image galleries, yet the browser still decides how far ahead of the viewport it starts the fetch.
That nuance matters for agency audits. “We enabled lazy loading” can mean native attributes, a WordPress filter that injects loading="lazy" on every attachment, a Shopify theme setting, or a JavaScript library that swaps data-src after scroll. All of those can defer the LCP candidate if the first screen image is included, so the mechanism belongs in the ticket before anyone blames hosting.
Native lazy loading is progressive enhancement: older browsers ignore unknown attributes and load images normally. That is fine for bandwidth on modern Chrome and Safari, yet it does not excuse shipping a hero with loading="lazy" on the browsers that dominate your CrUX population. Progressive enhancement is not permission to defer the element field users actually paint as LCP.
Why lazy-loading the LCP image hurts Largest Contentful Paint
LCP marks when the largest above-the-fold content element becomes visible. On marketing and commerce pages that element is often a hero image, product photo, or poster frame. If that resource is marked loading="lazy", the browser may delay the request until layout work suggests the image is “near” the viewport, even though it already is. The result shows up as higher resource load delay in Lighthouse and PageSpeed Insights, and as a worse lab LCP, which Google’s common LCP misconceptions post still flags as a frequent own-goal.
Search threads for “lazy load LCP” are full of Elementor, WordPress, and theme support tickets for the same pattern: a plugin or theme applied lazy loading sitewide, the logo or banner became LCP, and scores collapsed until someone excluded the first image. Weston Ruter’s comparison demos of lazy-loaded versus eager LCP images make the timing difference visible without arguing theory. Disabling lazy load on the LCP image is a first triage step in those threads, not a last resort after a CDN rewrite.
The fix is usually boring. Once PageSpeed Insights or Chrome Performance names the LCP element, remove loading="lazy" from that image (and any identical mobile/desktop sources in <picture>). fetchpriority="high" fits when you know the LCP URL early; a preload fits only when the markup path cannot put that URL in the initial HTML. Preloading and lazy-loading the same URL fights itself.
When lazy loading images is the right default
Lazy loading images is the right default for content that starts offscreen: article body photos after the fold, related-product grids, footer badges, long category galleries, and comment avatars. Those bytes should not compete with the critical path on first paint. On a twenty-image blog post, native lazy loading can cut early bandwidth sharply on mobile without changing the story the user sees first.
On retainer sites we treat eager versus lazy as a short policy, not a plugin toggle. Eager (no lazy, or explicit eager) covers the LCP candidate, a logo when it is LCP, the primary above-the-fold product image, and any image inside the first viewport on common breakpoints. Lazy covers everything that requires scroll or a tab change before it matters.
That policy needs a mobile-width check as well as desktop. An image that looks “below the fold” on a large monitor can sit in the first screen on a phone, and that phone traffic often dominates CrUX. Audits that only run at 1440px tend to lazy-load the wrong assets for the field population.
Above-the-fold images, fetchpriority, and eager loading
“Above the fold” is not a CSS property; it is a judgement about what appears in the initial viewport for your primary breakpoints. When that judgement is wrong, lazy loading above the fold recreates the LCP delay problem even if the CMS labelled the block “content” rather than “hero.” Breakpoints disagree more often than teams expect, especially between a design mockup and a phone-width lab run.
For the confirmed LCP image, loading="lazy" comes off (or loading="eager" overrides a stack that injects lazy by default), accurate width and height or aspect-ratio CSS reserve layout space, and fetchpriority="high" fits when the image is the clear LCP candidate and other resources are not already marked high. An appropriately sized file matters so eager does not mean downloading a 4000px PNG; the image optimisation post linked above covers formats and srcset. Theme and page-builder defaults often invert that list: every attachment gets lazy, none get fetch priority, and the hero is still a huge JPEG. Attribute policy belongs ahead of another CDN plan. A smaller eager hero usually beats a huge lazy hero on LCP, even when the waterfall looks “optimised” in a plugin dashboard.
Iframes, video, and other lazy media caveats
The loading attribute also applies to <iframe>, so below-the-fold embeds (maps, secondary YouTube players, chat widgets) are usually sensible lazy candidates. A first-screen video iframe that is also your LCP or a large layout box can delay paint and still risk CLS if dimensions are missing. A facade (poster image + click-to-load) fits heavy third-party players when the embed is not required for the first view. That pattern pairs with Cross-Origin YouTube Embeds and CLS.
Background images in CSS cannot use loading="lazy". If the LCP element is a CSS background, attribute fixes will not help; you need a real <img> or <picture> in the document, or a different LCP strategy. JavaScript lazy libraries that leave src empty until hydration can also create late discovery worse than native lazy, especially when the library bundle itself is deferred. Video posters and <video> elements have their own preload behaviour, so “lazy images are on” does not automatically cover posters and iframes. The LCP node type in the trace matters, because a poster frame can win LCP while the checklist only mentions <img loading>.
Lazy loading and CLS: reserve space first
Lazy loading does not cause CLS by itself; missing dimensions do. When a lazy image finally loads into a box with no reserved height, the page shifts and CLS rises. That failure mode is easy to miss in a lab run that never scrolls to the image, then appears in field data when users do scroll.
Lazy loading images still needs intrinsic size hints: width and height attributes, width/height in CMS media metadata, or CSS aspect-ratio on the container. Responsive srcset still needs a consistent aspect ratio across candidates. If a theme wraps images in a collapsing container until load, the container belongs in the ticket before anyone argues about Core Web Vitals thresholds. Hero images that are eager still need reserved space: eager versus lazy is an LCP timing decision, while dimensioning is a CLS decision, so agencies should treat them as two checklist rows rather than one vague “images” ticket.
Lab vs field when lazy loading hides LCP regressions
A single PageSpeed Insights run on the homepage can look fine after you “enable lazy loading,” especially if the lab viewport and your CMS preview disagree about what is on screen. Field LCP in CrUX or the PageSpeed Insights field section can still worsen when real devices, slower networks, and cookie banners change which element wins LCP. Mobile users may also hit a lazy hero that desktop lab never marked as LCP, so one desktop screenshot is not proof.
Scheduled lab checks on money URLs (home, category, product, key landing pages) before and after attribute changes, on both mobile and desktop strategies, keep the claim honest. LCP element identity matters more than the score alone. If the LCP node switches from a heading to an image after a deploy, that image’s lazy attribute is the next place to look. For portfolio monitoring across many client sites, Apogee Watcher keeps those lab histories in one place so an attribute change does not vanish into a one-off PDF.
Lab green and field amber can both be honest, because lab may scroll less, block third parties differently, or use a colder cache. When lazy loading is in the diff, read resource load delay for the LCP URL in the lab diagnostics before you close the ticket. If load delay stays high after “optimisation,” suspect the attribute before you rewrite the media query set.
Agency checklist before you ship “lazy everything”
Before a theme update or “speed plugin” goes to production, a short pass usually pays for itself. The current LCP element and time on mobile and desktop for each money URL set the baseline, then templates and plugin settings get a search for global loading="lazy" injection. The LCP candidate (and logo if needed) stays off the lazy list, and width/height or aspect-ratio get confirmed on lazy and eager images alike. Lab checks run again until the LCP element and load delay move the right way; field LCP is watched for the 28-day window after release rather than declared fixed on day one; and the exclusion list is written down so the next plugin install does not re-lazy the hero. That pass is cheaper than a month of “we improved PageSpeed” conversations while CrUX stays amber, and it gives the next developer a written exclusion list instead of tribal knowledge about “the hero must stay eager.” The list belongs in the repository or the retainer runbook so plugin reinstalls do not undo it quietly.
FAQ
Should every image use loading="lazy"?
No: lazy loading images is for offscreen content, while the LCP image and other critical above-the-fold images should load eagerly. Sitewide lazy defaults from CMS plugins are a common source of LCP regressions on retainers.
Does loading="lazy" on the LCP image always break field data?
Not always, because browsers and viewports differ, but it is a high-risk pattern that Lighthouse and many case studies associate with longer LCP. The LCP candidate usually stays eager unless you have measured no harm on mobile and desktop for that template.
Is native lazy loading better than a JavaScript lazy library?
For ordinary images, native lazy loading is usually enough and avoids waiting on a script. Libraries still appear for older patterns or fancy placeholders; they must not leave the LCP src undiscovered until late JavaScript runs.
Can lazy loading improve CLS?
Indirectly, only if it reduces contention and you already reserve space. Lazy loading without dimensions often worsens CLS when images pop in during scroll, so sizing belongs ahead of another lazy-loading toggle.
What about logos?
If the logo is the LCP element (common on simple brochure sites), it should stay eager. If a large hero is LCP and the logo is small, the logo can follow normal theme defaults, but still reserve space to avoid header shift.
Ship eager heroes, lazy galleries
Lazy loading images remains one of the strongest bandwidth defaults on long pages, as long as the LCP image stays eager, dimensions protect CLS, and iframes or video facades are handled on purpose. The loading="lazy" attribute is a scalpel for offscreen media, not a blanket for every attachment ID in the CMS. The exclusion list travels with the theme update so the next speed plugin cannot quietly reverse it.
For format and sizing work around the same LCP element, continue with Image Optimisation Strategies for Better LCP Scores. When font files compete with the hero, use Font Subsetting for Web Performance: 4 Tools to Reduce Font File Size and Improve LCP. To keep attribute changes honest across a client roster, start a free trial or run a check on a money URL before and after you remove lazy from the hero.
References
- Browser-level image lazy loading for the web (web.dev)
- Lazy loading (MDN)
- Common misconceptions about how to optimize LCP (web.dev)
- HTML attribute: loading (MDN)
- Image Optimisation Strategies for Better LCP Scores (Apogee Watcher)
- Font Subsetting for Web Performance: 4 Tools to Reduce Font File Size and Improve LCP (Apogee Watcher)
- Lighthouse: Don't lazy load Largest Contentful Paint image (GTmetrix / Lighthouse audit explanation)
- Cross-Origin YouTube Embeds and CLS: What Publishers Can Actually Fix (Apogee Watcher)
Top comments (1)
tr.ee/dev-to