Adding loading="lazy" to the image that becomes your Largest Contentful Paint element delays it. On a bare test page the cost was about 20ms. With a 600ms render-blocking stylesheet in front, the lazy hero finished at 3,524ms against 2,912ms for the same image without it. Lazy-load images below the fold; leave the first, biggest image alone.
Key takeaways
- Lazy-loading the LCP image delays LCP. web.dev's data across all pages in the Chrome UX Report shows a median 75th-percentile LCP of 2,922ms without lazy loading and 3,546ms with it, though it notes this is correlation, not proof.
- On a bare page the cost was small: about 20ms (2,924ms against 2,904ms median, five runs each, emulated slow connection).
- Behind a slow stylesheet it was large: +612ms (3,524ms against 2,912ms), almost exactly the stylesheet's delay.
- fetchpriority="high" matched the default in both tests. It did not make the image faster here; its benefit shows when other requests compete for the connection.
- The page's load event does not wait for lazy images. Measure at load and you will miss the image entirely, which is how the first run of this test went wrong.
- The rule: lazy-load below-the-fold images, never the first one, and check that no plugin or template adds the attribute to everything.
What the published data says first
Before the test, the evidence from people with far more traffic than a test page. web.dev's article on lazy-loading and LCP reports both observational and experimental results:
| Measurement | Without lazy loading | With lazy loading |
|---|---|---|
| Median 75th-percentile LCP, all pages (CrUX) | 2,922ms | 3,546ms |
| Same, WordPress pages | 3,495ms | 3,768ms |
| A/B test, archive pages, desktop | 1,759ms (disabled) | 2,029ms |
| A/B test, archive pages, mobile | 1,403ms (disabled) | 1,657ms |
The same article found single pages differed by only 2 to 4 percent, so the effect is not uniform, and that lazy loading roughly halved image bytes on archive pages (577 KB against 1,173 KB). The trade is real: fewer bytes for pages where images are off screen, a later LCP where an image is the first thing people see.
The test
One page: a header, a single 527 KB image sized to the full width, and a line of text.
Three variants of the image tag (default, loading="lazy",
fetchpriority="high"), each loaded five times in a fresh browser context, with
LCP read from a PerformanceObserver. Network conditions were emulated through
the Chrome DevTools protocol: no throttling, then roughly 150ms round-trip and 1.6 Mbps.
| Condition | Default | loading="lazy" | fetchpriority="high" |
|---|---|---|---|
| No throttling | 36 | 48 | 44 |
| Slow connection | 2,904 | 2,924 | 2,904 |
| Slow connection, plus a 600ms render-blocking stylesheet | 2,912 | 3,524 | 2,916 |
On a bare page, lazy loading cost about 20ms, which is within what you would shrug off. Add
a stylesheet that takes 600ms to arrive and the lazy image finished 612ms later than the
default, while the default barely moved. One run-level detail worth knowing: the page's
load event fires without waiting for a lazy image. The first attempt at this
test stopped measuring at load, recorded an LCP of 180ms for the lazy page,
and was wrong; the image arrived about 2.7 seconds later.
Why it happens
| Claim | Status | Basis |
|---|---|---|
| Lazy-loading an above-the-fold image can delay LCP | Documented | web.dev's data and A/B tests above, plus the measurements here |
| The delay in the slow-stylesheet case matches the stylesheet's delay | Measured | +612ms against a 600ms delay |
| A normal image is requested as soon as the HTML is parsed; a lazy one waits until layout is known | Inferred | Consistent with the results; the browser's request ordering was not traced |
The inference is the simple one. A normal image is found early and fetched in parallel with the stylesheet. A lazy image has to wait until layout shows whether it is in view, and layout waits for the stylesheet. Anything that delays layout, such as a slow stylesheet or a long task on the main thread, therefore delays the lazy image by the same amount.
The fix
<!-- the first, biggest image: no loading attribute, dimensions set -->
<img src="hero.jpg" width="1200" height="600" alt="...">
<!-- everything below the fold: lazy -->
<img src="chart.png" width="800" height="400" alt="..." loading="lazy">
Keep the width and height attributes on both, as the
layout-shift guide explains, so the page
does not jump when they arrive. Check your CMS and plugins: many add
loading="lazy" to every image, including the first. If the image is slow because
the server is slow, that is a separate problem, covered in the
shared-hosting guide, since
time to first byte is added to LCP whatever the front end does.
fetchpriority="high" did not help in these tests; both it and the default
finished within a few milliseconds of each other. It is documented as a hint for when other
requests compete with the image, which a one-image test page does not reproduce, so try it
on your own page and measure rather than assuming.
Find your LCP element and see if it is lazy
Paste this into the DevTools console and reload, or run it in a snippet:
new PerformanceObserver(list => {
const e = list.getEntries().at(-1);
console.log('LCP', Math.round(e.startTime) + 'ms', e.element,
'lazy:', e.element && e.element.loading === 'lazy');
}).observe({ type: 'largest-contentful-paint', buffered: true });
If it prints lazy: true, that attribute is costing you. Run it on your real
templates, not just the home page, since each template can have a different LCP element.
This is the same kind of check as the manual items on the
pre-launch checklist, and the
INP guide covers the
responsiveness half of Core Web Vitals.