Largest Contentful Paint measures when the largest element visible on page load appears on screen: usually a hero image, a video poster or a block of text. The threshold considered good is 2.5 seconds at the 75th percentile of real visits. Knowing that your LCP is 4 seconds, however, tells you nothing about where those seconds are lost. The most useful method I know is to break it into four consecutive phases and attack the longest one.
The four phases of LCP
Every LCP is the sum of four intervals:
| Phase | From… | To… |
|---|---|---|
| TTFB | start of navigation | first byte of the HTML |
| Load delay | first byte | start of the LCP resource download |
| Load duration | download start | end of the LCP resource download |
| Render delay | download end | element painted on screen |
If the LCP element is text, the two middle phases are zero and only the gap between TTFB and rendering remains, which almost always depends on blocking CSS and fonts.
As a rough reference, on a healthy page TTFB and load duration take up most of the time, while the two "delays" should be small. When the delays dominate, the browser has wasted time doing nothing useful for the most important element.
How to measure the phases
In the Chrome DevTools Performance panel, when you record a page load, the LCP track shows the breakdown into sub-parts. Alternatively, the web-vitals JavaScript library, in its attribution build, returns the four values and the element involved for each measurement: it is the best way to get the breakdown from real visitors too, collected alongside the metric.
First of all you need to identify which element is the LCP. It is often not the one you expect: on mobile it may be an introductory paragraph, on desktop the main photo. Optimize the right element, not the one that seems important to you.
Phase 1: slow TTFB
If the first byte arrives after a second or more, everything else starts late. Typical causes are pages generated on every request without caching, slow database queries, redirect chains (http to https, then to www, then to the language version) and servers far from users. The fixes: caching the HTML page, removing unnecessary redirects, a delivery point closer to the audience.
Phase 2: load delay
This is the most underestimated phase. The browser already has the HTML but has not yet started downloading the main image. It happens when:
- the image is set as a background in a CSS file, so it is discovered only after the stylesheet is downloaded and parsed;
- the image is inserted by JavaScript, for example by a slider or by a framework that renders on the client;
- the image has
loading="lazy", which postpones loading until layout confirms it is in the viewport; - many other resources compete and the image gets a low priority.
The fixes are straightforward. The LCP image must be present in the initial HTML as a normal img tag, without lazy loading, and it is worth flagging it with fetchpriority="high". If it really has to stay a CSS background, a link rel="preload" in the head lets the browser discover it immediately.
<img src="/img/hero-1200.avif" width="1200" height="675"
fetchpriority="high" alt="Meeting room overlooking the harbour">
Phase 3: load duration
Here what matters is file weight and how fast it arrives. A 900 KB hero photo on an average mobile connection takes time, regardless of any priority. The fixes:
- serve modern formats such as AVIF or WebP with a fallback;
- offer several widths with
srcset, so a phone does not download the version meant for 27-inch screens; - compress at a considered quality, checking the result by eye instead of using the tool's default value;
- serve the image from the same domain as the page or from one already connected, to avoid a new TLS negotiation.
Phase 4: render delay
The image is downloaded but not yet shown. The most frequent reasons are stylesheets still downloading, synchronous scripts in the head blocking the parser, a web font hiding the LCP text until it arrives, or an entrance animation that starts at zero opacity. The last case is common in themes: the element is ready, but a fade effect makes it visible only half a second later.
It is solved by reducing blocking CSS to what is really needed, moving scripts to defer and removing entrance animations from the main element.
A concrete example
On the website of a small professional practice, the typical case is this: an LCP of 4.1 seconds on mobile. The breakdown shows a TTFB of 0.6 seconds, a load delay of 2.2 seconds, a download of 0.9 seconds and rendering of 0.4. The problem is not the image's weight but the fact that it is the background of a slider loaded via JavaScript. Once the first slide is turned into a static img tag with high priority, the load delay drops below 200 milliseconds and the overall LCP falls under the threshold without touching the server.
In short
- LCP is the sum of TTFB, load delay, load duration and render delay.
- Measure the breakdown and work on the longest phase, not at random.
- The LCP image belongs in the initial HTML, without lazy loading and with
fetchpriority="high". - File weight and modern formats drive the load duration.
- CSS, blocking scripts and entrance animations stretch the final stretch.