
Native lazy loading can tank LCP. Use fetchpriority, the hero exception, and one eager-loading rule to keep deferred images without hurting Core Web Vitals.
Lazy-loading images without breaking Largest Contentful Paint means one rule above all others: never defer the image that becomes your LCP element. Load that hero eagerly, give it high fetch priority, and only lazy-load everything below the fold.
That sentence is the entire strategy. Teams still ship loading="lazy" on every <img> because it feels like free performance, then watch LCP climb from roughly 1.8 seconds to 3.5 seconds or worse on mobile. Google's Core Web Vitals treat LCP as a field metric with a "good" threshold at 2.5 seconds for 75% of visits; a single mis-tagged hero routinely pushes sites into "needs improvement." At Atlas Photo, gallery and portfolio pages live or die on that first full-bleed frame, so lazy-loading images without breaking LCP is not an optimization footnote. It is the difference between a page that feels instant and one that paints a blank box while the rest of the grid politely waits.
This guide covers how LCP chooses an image, why blanket lazy loading breaks it, how fetchpriority="high" recovers the hero, and the eager-loading rule you can apply in templates without hand-tuning every URL.
Largest Contentful Paint is the time from navigation start until the largest visible text block or image finishes rendering in the viewport.
On photography and product sites, that "largest" node is almost always an <img> or CSS background-backed media element in the first screen: hero banner, featured shot, or first grid tile. Chrome's documentation on optimizing LCP breaks the metric into four sub-parts: time to first byte, resource load delay, resource load duration, and element render delay. For images, load delay and load duration dominate. If the browser does not discover the hero URL early in HTML, or discovers it but deprioritizes the request, you pay hundreds of milliseconds before a single pixel of the LCP image arrives.
Real numbers help. A 180 KB WebP hero on a mid-tier 4G connection might download in about 400 to 700 ms once the request starts. Add 800 ms of discovery delay because the image was lazy and sat behind JavaScript, and you have already burned a large slice of the 2.5-second budget before paint. Prefetch and CDN edges cut transfer time; they cannot invent a request the browser refused to start.
Key terms, stated plainly: LCP is the moment the biggest above-the-fold content finishes painting. Lazy loading is instructing the browser to skip fetching an image until it nears the viewport. Fetch priority is a hint (fetchpriority="high" or "low") that nudges the browser's network scheduler. Eager loading is the default for images: fetch as soon as the parser sees the tag.
Lazy loading breaks LCP when the LCP image itself is marked loading="lazy", because the browser delays the request until layout proves the image is near the viewport, often after CSS and fonts have already competed for bandwidth.
Native lazy loading (the HTML loading attribute) landed widely around 2019 to 2020 and is now supported across Chromium, Safari, and Firefox. MDN's guidance on the img loading attribute is clear: use lazy for offscreen images. The failure mode is cultural, not technical. Component libraries, CMS image helpers, and "performance" snippets apply loading="lazy" globally. On a homepage where the first <img> is a 1200×800 hero, that attribute tells the browser the resource is optional. Chromium may still fetch it, but later and at a lower priority than scripts, stylesheets, and other early requests.
Field data patterns look like this:
fetchpriority="high" on that eager hero: another 100 to 400 ms improvement on contention-heavy pages (many third-party scripts, large CSS).Lazy loading is still correct for galleries. A page with 40 thumbnails should not open 40 connections on first paint. The bug is treating the LCP candidate like thumbnail number 27.
Fetch priority recovers LCP by marking the hero request as more important than competing subresources, and the hero exception is the policy that the LCP image must be eager, discoverable in initial HTML, and usually fetchpriority="high".
Practical markup for a typical Atlas-style hero:
<img
src="/media/hero-coast.webp"
width="1600"
height="900"
alt="Coastline at blue hour"
fetchpriority="high"
decoding="async"
/>
Notice what is absent: no loading="lazy". Width and height reserve layout space so you do not trade a fixed LCP for a layout shift (CLS). decoding="async" keeps decode work off the main thread without delaying the network fetch.
If you use <picture> or srcset, put fetchpriority="high" on the <img> inside the picture, and keep the same eager rule. Preload can help when the hero URL is known but not in the first HTML chunk:
<link rel="preload" as="image" href="/media/hero-coast.webp" fetchpriority="high">
Use preload sparingly: one hero, not five. Over-preloading fights the same contention problem you are trying to solve.
Use this rule in templates and CMS renderers so the first viewport image is protected while the rest of the gallery stays deferred:
loading omitted or eager, plus fetchpriority="high".loading="lazy", optional fetchpriority="low".Comparison of approaches:
| Approach | Hero request start | Typical mobile LCP impact | Safe for long galleries? |
|---|---|---|---|
| Lazy on every image | Late / deprioritized | Often +0.5s to +2.0s | Yes for grid, no for hero |
| Eager hero only | Early, normal priority | Baseline recovery | Yes |
Eager hero + fetchpriority="high" |
Early, elevated priority | Best common case | Yes |
| Preload + eager high-priority hero | Earliest possible | Strong when HTML is delayed | Yes if limited to one image |
| JS-only image injection | After JS execute | Frequently worst | Avoid for LCP media |
The middle rows are the production default for Atlas Photo and similar image-first sites: defer the grid, protect the hero.
You should still lazy-load aggressively for offscreen galleries, infinite scroll, and any image that cannot become the LCP element on first paint.
A portfolio index with 60 shots benefits from native lazy loading plus modern formats (WebP/AVIF), responsive srcset, and a CDN cache hit rate above 90%. Those techniques shrink bytes; lazy loading shrinks concurrent bytes. Together they keep Time to Interactive and main-thread work sane while the protected hero satisfies LCP.
Watch for these traps in 2024 to 2026 stacks:
background-image heroes that cannot use loading or fetchpriority. Prefer a real <img> for LCP candidates so the browser can prioritize correctly.Budget guidance that teams can enforce in code review: hero file under about 200 to 300 KB in the primary format, dimensions matching display size × device pixel ratio (often 2x, not 3x for every asset), and only one high-priority image preload per document.
Lazy-loading images without breaking LCP is therefore not "lazy loading bad." It is scoped lazy loading: defer what the user cannot see yet, and accelerate the one frame that defines perceived speed.
Usually no. Logos are small and rarely the LCP element, so high priority wastes contention budget better spent on the hero photograph.
Omitting loading already means eager for images in HTML; set loading="eager" only when a parent system forces lazy by default and you need an explicit override.
No. fetchpriority ranks a discovered request, while preload can discover the URL earlier. Use preload for late-discovered heroes and fetchpriority on the actual image tag either way.
Find any asset in seconds. Atlas Photo is digital asset management for creative and brand teams, with early-access founder pricing for the first users. Get early access
Optimize images with SSIM-based compression. 10 free conversions per day, no credit card required.