beginner 3 min answer Multiple choice

A content site adds loading="lazy" to every image to cut bytes. Image bytes on first load fall about 60%. Field largest contentful paint at the 75th percentile goes from 2.1 s to 3.4 s, and the hero image is the LCP element on most pages. What did the change buy, what did it pay, and which change keeps the saving without the regression?

core web vitalslcplazy loadingpreload scannerbeginner
Pick one
Show the full answer Hide the answer

The mechanism

The browser's preload scanner reads the raw HTML ahead of the parser and starts fetching <img src> immediately - before stylesheets are applied and before layout runs. loading="lazy" removes an image from that early fetch, because the browser cannot know whether a lazy image is near the viewport until it has done layout, which happens later.

For an image below the fold that is a pure win: it may never be fetched. For the hero, it is pure loss - the image will be fetched anyway, only now it is discovered after CSS and layout and queued behind whatever else is in flight.

What the change bought

Roughly 60% fewer image bytes on first load. That is real: it helps every user who does not scroll, it cuts data cost on metered connections, and on a long article the deferred images are the majority of the page weight.

What it paid

The LCP element lost its early discovery, which commonly costs 300 ms to 1.5 s on mobile depending on how much CSS has to arrive first. Measured here: 2.1 s to 3.4 s at p75, which crosses out of the good band for LCP - 2.5 s or less at the 75th percentile, with over 4.0 s rated poor.

The bill arrives a week later, in field data, not at review time, because every number visible in the pull request got better. It shows up at p75 and above, because fast devices and fast links absorb the extra round trip and hide it from a median.

Why the other options fail

  • Remove the attribute everywhere. Pays the whole byte saving to fix one element. The policy was right for most images; it was wrong for one per page. Correct only if you cannot reliably identify the above-the-fold image - a possibility on user-authored layouts, and then it is a real choice.
  • Preload every image. It restores early discovery and destroys the thing that made discovery useful. Priority is relative: if forty images are high priority, none are, and the hero now competes for bandwidth and connection slots with content the user will never see. Preload is a scarcity mechanism, and this is how an LCP problem becomes a bandwidth problem.
  • Compress the hero further. The most popular wrong answer, because it improves the number that is on the dashboard. A 20 KB hero requested 900 ms late still paints 900 ms late. Discovery time and transfer time are separate terms, and only one of them is the problem here.

The decision rule

Never lazy-load anything in the initial viewport. Mark the single most likely LCP candidate fetchpriority="high". Lazy-load everything else. The browser cannot identify the hero for you - at parse time it does not know where the viewport ends - so this is the author's call, every time.

When not to protect the hero

When no image is in the initial viewport: a text-led article, a dashboard behind authentication, a settings screen. Then the LCP element is text or a heading block, and there is nothing to protect. Confirm it with field data per template rather than assuming - which template the hero lives in is exactly the kind of thing that changes without anyone telling the performance owner.