Largest contentful paint

Find what is holding up the biggest thing on the screen, and shorten the chain in front of it.

03of 16
≈ 13Min read
5Parts

What LCP measures

The moment the largest element in the viewport finished rendering — usually a hero image, a video poster, or a block of heading text. It is a proxy for "the page looks ready", and it is the vital that most often fails.

It is measured in the viewport, so the element that counts is whatever dominates the first screen; and it keeps updating until the user first interacts, which is why a late-loading banner can make it worse than it felt.

The four parts it breaks into

Google's own field data says load delay and render delay are together the majority of LCP on most sites — not the download itself. The bytes are the obvious suspect and rarely the biggest one.

PartUsuallyFix
Time to first byteserver, redirects, no cachingthe server lesson, and a CDN
Resource load delaythe browser did not know about it yetput it in the HTML; preload; no lazy-loading the hero
Resource load timethe file is too bigthe images lesson
Render delayblocked by CSS, fonts or JavaScriptthe critical-path lesson in the web course

The single most common bug

Lazy-load everything below the fold and nothing above it. A lazily-loaded hero is discovered late, downloaded late, and painted late — and it is in a surprising number of production pages because a component applied loading="lazy" to every image it renders.

lazy-loading the thing the metric is measuring
<img src="/hero.jpg" loading="lazy">          <!-- the LCP element, deferred. Never. -->

<img src="/hero.jpg" fetchpriority="high">    <!-- this is the opposite, and correct -->

Discovery beats bandwidth

The preload scanner reads the raw HTML before the parser is finished and starts fetching what it finds. Anything it cannot see — a CSS background, an image injected by a component, a font referenced inside a stylesheet — waits for whatever comes first.

the browser can only fetch what it can see
<!-- discovered by the preload scanner, immediately -->
<img src="/hero.jpg" fetchpriority="high" width="1200" height="600" alt="…">

<!-- discovered after CSS is parsed — hundreds of milliseconds later -->
<div style="background-image: url(/hero.jpg)"></div>

<!-- discovered after JavaScript has downloaded, parsed and run — later still -->
<script>renderHero();</script>

The order to try things

  • Find the element. The browser reports it: entry.element in the LCP observer, or the Lighthouse trace. Half the time it is not what you assumed.
  • Is it discoverable in the HTML? If not, make it so, or preload it.
  • Is anything blocking the paint? A render-blocking stylesheet or a synchronous script in the head.
  • Is it too big? Then the images lesson.
  • Is the server slow? Then the server lesson — and check for a redirect chain first, because each one is a whole round trip.

Practice