La Ventana, Baja California Sur, México. [email protected]
←Journal
·6 min read

Lazy-Load Images Without Hurting LCP

Native lazy loading saves bandwidth on service pages—but web.dev and HTTP Archive data show that applying loading="lazy" to the hero or LCP image delays discovery and slows the paint visitors notice first.

Windware Studio· Design & Development Team

A service homepage often ships a large hero photograph, a logo mark, a trust-badge strip, and a gallery of project shots further down the page. Asking the browser to defer every image with loading="lazy" feels like a free win. It is not. Browser guidance from web.dev and real-world measurements from the HTTP Archive’s Web Almanac show the same pattern: lazy loading below the fold cuts unused bytes, but lazy loading the image that becomes Largest Contentful Paint (LCP)—the largest visible element in the initial viewport—delays the paint visitors use to judge whether the page has arrived.

web.dev’s caution is explicit: do not lazy-load images that are likely to be in the viewport when the page loads, especially LCP images. The 2024 Web Almanac performance chapter (published November 11, 2024; last updated February 4, 2025) still finds that 16% of mobile pages with an image-based LCP apply native or custom lazy loading to that image—down only modestly from 18% in 2022.


Why blanket lazy loading slows the first paint

Largest Contentful Paint measures when the largest content element in the viewport finishes rendering. On service sites that element is often the hero photograph, a featured project image, or a large service-band graphic. MDN’s January 13, 2025 guide on fixing image LCP lists lazy loading as one of three common causes of resource load delay: the time between receiving the HTML and starting the image request.

The mechanism is straightforward. When an <img> carries loading="lazy", Chromium’s preload scanner skips it during the early HTML parse. The browser waits until layout shows the image is near the viewport, then queues the download—often at a lower priority. For a hero that is already on screen, that wait is pure delay. web.dev’s article on browser-level lazy loading (authors including Addy Osmani, Houssein Djirdeh, Mathias Bynens, and Barry Pollard; last updated August 13, 2024) states the rule in plain terms: use loading="lazy" only for images outside the initial viewport, because the browser cannot lazy-load an image until it knows where that image sits on the page.

A short analogy: lazy loading is a loading dock that opens only when a truck is almost at the gate. That saves space for shipments that may never arrive. Opening the dock late for the one delivery everyone is waiting for is the wrong optimization.


What the attribute does—and what it does not

The HTML loading attribute on <img> (and on iframes) accepts two practical values, as documented by MDN and web.dev:

ValueBehaviorUse on a service page
lazyDefer fetch until the image nears a browser-defined distance from the viewportGallery shots, case-study thumbnails, footer logos below the fold
eager (or omit)Load as usual during parse; default behaviorHero / LCP candidate, first in-viewport service photo

eager does not make an image download faster than an unmarked image; it only avoids an extra delay. To raise relative priority for the LCP image, MDN’s fetchpriority attribute documentation and web.dev both recommend fetchpriority="high" on that image (or on a matching <link rel="preload" as="image">). Combining loading="lazy" with fetchpriority="high" still delays the fetch while the image is off-screen—web.dev notes the pair is rarely useful.

Dimensions matter either way. Both sources recommend width and height (or equivalent CSS aspect-ratio reservation) so the browser can reserve space before the file arrives. Lazy loading without dimensions increases Cumulative Layout Shift risk if the image pops in after the visitor has started reading.


How the failure modes show up on service sites

Three patterns cause most of the damage in practice.

CMS and plugin defaults. Many content systems and optimization plugins attach loading="lazy" to every <img>. WordPress historically drove much of native lazy-loading adoption; web.dev’s earlier study, The performance effects of too much lazy loading (Felix Arntz and Rick Viscomi; last updated March 31, 2022), found that WordPress’s then-default approach of lazy-loading above-the-fold images cost LCP on archive-style pages while still saving bytes. Their experimental fix—eager-loading the first featured or main-content image, lazy-loading the rest—recovered LCP while keeping the byte savings. The lesson travels: exclude the first one or two images, or any selector that marks the hero, from automatic lazy loading.

JavaScript-injected heroes. MDN warns that inserting the LCP image with client-side JavaScript creates a request chain: HTML → script download → script execution → image request. Even inline scripts force the look-ahead parser to wait. Prefer a real <img> (or <picture>) in the initial HTML for the hero.

Everything eager “just to be safe.” The opposite mistake also hurts. If every below-the-fold project photo downloads immediately, those requests compete with the hero for bandwidth. MDN’s LCP guide notes that reducing that contention can shorten download time for the critical image. Selective lazy loading below the fold is still the right tool—just not for the LCP candidate.

The 2024 Almanac adds context: images remain the LCP content type on about 73% of mobile pages and 83% of desktop pages in their sample, and 15% of mobile pages now use fetchpriority="high" on the LCP image (up from near-zero in 2022). Prioritization is catching on; excluding the LCP image from lazy loading has further to go.


Limits and tradeoffs

Viewport size, anchor links, and early copy length can move a “first image” in or out of the fold. Server-side heuristics that skip lazy loading for the first featured image are good defaults, not guarantees on every device. Field monitoring still helps: web.dev demonstrates a PerformanceObserver on largest-contentful-paint that warns when the reported LCP element carries loading="lazy".

Lazy loading also only defers when JavaScript is enabled—an intentional anti-tracking constraint noted by MDN. Browsers without support ignore the attribute harmlessly. Neither web.dev nor the Almanac claims a universal millisecond gain for every niche; they establish that above-the-fold lazy loading is a predictable anti-pattern, not a rare edge case.


Practical takeaways for your next website

  1. Audit the LCP image first. In Lighthouse or Chrome DevTools, confirm which element is LCP on mobile and desktop; remove loading="lazy" from it.
  2. Mark the hero explicitly. Omit loading, or set loading="eager", and add fetchpriority="high" when the hero competes with other early requests.
  3. Lazy-load below the fold only. Apply loading="lazy" to galleries, secondary case studies, and offscreen embeds—not to the first viewport.
  4. Reserve space. Keep width and height (or aspect-ratio CSS) on every image so lazy or eager loads do not shove copy around.
  5. Override CMS defaults. If a plugin lazy-loads everything, add an exclusion for the hero selector or the first content image.

Ready to Discuss Your Website?

If you are planning a new website, a service redesign, or custom development for your business, we’re here to help shape the work—including image-loading patterns that keep the first paint honest without wasting bandwidth on offscreen photos.