awesome-everything RU
↑ Back to the climb

Browser & Frontend Runtime

LCP: four phases, one dominant cost

Crux Largest Contentful Paint decomposes into TTFB, resource load delay, resource load time, and render delay — diagnosing and fixing the right phase is the whole game.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at middle altitude — in the sky
◷ 14 min

A team adds a CDN, compresses the hero image, and LCP barely moves. The problem was never the image size — the browser was discovering the image late, after a JavaScript bundle ran. The fix was one HTML attribute. LCP fails in four different ways, and only one fix works per phase.

What LCP measures and when it fires.

Largest Contentful Paint is the render time of the largest content element visible in the viewport during load. “Content” is a specific list: <img>, the poster of a <video>, a background image loaded via url(), and block-level elements containing text. The browser watches as elements paint, keeps updating its candidate for “largest”, and finalises the LCP value at the first user interaction (because once the user is interacting, further “loading” is not load anymore). On most pages the LCP element is the hero image or the main headline — the thing the user perceives as “the page has loaded.”

The four phases.

LCP time is the sum of four sequential sub-parts. The fix is different for each.

  1. TTFB — time to the first byte from the server. Causes: slow SSR data fetch, no CDN, slow database query on the render path. Fix: cache, CDN, move slow data off the render path (streaming SSR with a Suspense boundary).

  2. Resource load delay — the gap between TTFB and the browser starting to load the LCP image. Cause: the browser discovers the image late because it is referenced only in CSS or injected by JavaScript, not in the initial HTML — the preload scanner cannot find it. Fix: put the LCP image in the initial HTML as a plain <img> with fetchpriority="high" or a <link rel="preload">.

  3. Resource load time — how long the LCP image itself takes to download. Cause: oversized file, wrong format, not compressed. Fix: correct format (AVIF, WebP), correctly sized for the viewport via srcset, compressed.

  4. Element render delay — the gap between the image finishing download and it actually painting. Cause: render-blocking CSS or JavaScript still occupying the main thread after the image is ready. Fix: inline critical CSS, defer non-critical scripts.

LCP four-phase breakdown
TTFB
Server + CDN + DB
Resource load delay
Discovery gap in HTML/CSS/JS
Resource load time
File size × connection speed
Element render delay
Render-blocking resources

The preload scanner — why discovery is everything.

Browsers run a secondary parser called the preload scanner that races ahead of the main HTML parser looking for resources to fetch, even while the main parser is blocked on a script. The preload scanner is why an <img> in the initial HTML loads early: the scanner sees it and kicks off the fetch immediately. It is also why an image referenced only in CSS (background-image) or injected by JavaScript loads late: the scanner cannot see it — the image is not discovered until the CSS is parsed or the JS runs.

This is the deep reason “put the LCP image in the HTML” works: it is the only place the preload scanner can find it without waiting. fetchpriority="high" then tells the browser to raise that fetch above other early resources; <link rel="preload"> is the heavier hammer for resources the scanner still cannot reach.

The most common self-inflicted LCP regression.

Adding loading="lazy" to the hero image. Lazy loading exists to defer images that are below the fold — images the user has not scrolled to yet. Applying it to the hero tells the browser to delay loading exactly the image the LCP metric is timing. The result is almost always a large resource load delay that did not exist before. Never lazy-load the LCP element.

Why this works

A common mistake is compressing the image when the real problem is load delay. If resource load delay is the dominant phase (the browser is discovering the image late), then even a 10× smaller file does not move LCP much — the delay before the download starts is still the bottleneck. The discipline is to read the phase split first (available in DevTools or from the web-vitals library’s LCP attribution), then fix the dominant phase. Applying the wrong fix wastes effort and leaves the number poor.

Quiz

A hero image is the LCP element and LCP is poor. The image has loading='lazy'. What is the likely problem?

Quiz

LCP time is dominated by 'resource load delay' — the gap between TTFB and the image starting to download. Which fix targets that phase?

Order the steps

Order the four phases of LCP time, from the request leaving the browser to the LCP element painting.

  1. 1 TTFB — server produces and sends the first byte
  2. 2 Resource load delay — browser discovers and starts loading the LCP image
  3. 3 Resource load time — the LCP image downloads
  4. 4 Element render delay — the image paints once render-blocking work clears
Recall before you leave
  1. 01
    Name the four phases of LCP time and what causes each one to be slow.
  2. 02
    Why does putting the LCP image in the initial HTML help LCP, and what does fetchpriority='high' add?
  3. 03
    A team measures the LCP phase split and finds load delay is 80 ms, but resource load time is 3.5 s. Which phase should they fix first and how?
Recap

LCP measures the render time of the largest content element in the viewport, and it decomposes into four sequential phases: TTFB, resource load delay, resource load time, and element render delay. A fix only works if it targets the dominant phase — compressing the image when load delay is the bottleneck does nothing. The preload scanner is why image placement in the initial HTML matters: only HTML-embedded images are visible to the scanner before blocking scripts run. Never apply loading="lazy" to the LCP element; it adds load delay to the exact element being measured. The full phase split is available from the web-vitals library’s LCP attribution or from a DevTools performance trace, and reading it before applying any fix is the discipline that separates fast iteration from wasted effort.

Connected lessons
appears again in193
Continue the climb ↑INP: input delay, processing, presentation
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.