Of the 3 indicators that make up the Core Web Vitals, the Largest Contentful Paint (LCP) is the one that speaks most directly to your users: it's what translates the feeling "the page loaded." It is also, paradoxically, the least optimized. The advice we see everywhere, "compress your images," is not wrong, but it misses the point in a large majority of cases.
In this article, we break down LCP as our experts do daily: not as a single number to reduce, but as a chain of four steps, each link of which can be diagnosed and optimized separately. With real Google data to support it, you will understand why the real performance potential is almost always found elsewhere than where you're looking, and you'll leave with a complete optimization method, from the server to the pixel.
LCP, what exactly is it?
The Largest Contentful Paint measures the time elapsed between the start of navigation (clicking a link, typing a URL) and the moment when the largest visible content element in the viewport is rendered on the screen. The underlying idea is simple: the most imposing element is, most of the time, what the user is waiting for to consider that something has actually happened.
This element qualifies for LCP title generally belongs to one of these categories:
- an
<img>tag, the most frequent case (typically a cover image or hero image); - an image loaded via CSS
background-image; - the poster of a
<video>tag; - since August 2023, a video or animated image can itself be considered an LCP element, according to the official Chromium changelog; a video banner site is therefore directly concerned;
- a block-level text element (a heading, a paragraph) rendered with its font.
To offer a good experience, Google sets a clear goal: an LCP of 2.5 seconds or less for at least 75% of visits. Beyond 4 seconds, it is considered poor; between the two, it needs improvement.
The precision of the "75th percentile" is crucial. The LCP that matters for your SEO is not the one you measure from your computer on a fiber connection, but the one your real users experience, consolidated by Google in the Chrome User Experience Report (CrUX). A synthetic test like Lighthouse or PageSpeed Insights remains an excellent diagnostic tool, but field data is what counts. We will revisit this in detail.
Note: the very definition of LCP has been refined over time to better match perception. As Tim Kadlec analyzes in The growing contentful gap, the LCP calculation has evolved to exclude certain non-significant elements (very low-content images, placeholders), which widens the gap with First Contentful Paint but makes the metric more representative of the moment the user truly sees the main content.
A single metric that hides a chain of dependencies
The main difficulty with LCP is that it aggregates phenomena of very different natures: server time, network time, browser computation time. Optimizing "LCP" in the abstract makes no sense. However, it can be broken down into four consecutive sub-parts, with no overlap or gap, whose sum exactly reconstructs the metric. This framework is from the reference guide Optimize LCP on web.dev.
- TTFB (Time To First Byte): the time between the navigation and the reception of the first byte of the HTML document. It includes DNS resolution, connection establishment, and server processing time.
- Resource load delay: the time between TTFB and when the browser *starts* downloading the LCP resource. If the LCP element is text rendered with a system font, this delay is zero.
- Resource load duration: the time to download the LCP resource itself (most often an image).
- Element render delay: the time between the end of the resource download and its actual display on screen.
This breakdown is not just a theoretical exercise: it radically changes how you optimize. Let's take an example that trips up most stakeholders. You identify a slightly heavy LCP image, you recompress it into AVIF, you divide its weight by three. The loading time drops, but the LCP doesn't budge an inch. Why? Because on this page, the image was actually hidden by JavaScript until its full execution: the time saved on downloading simply shifted to the render delay. The photograph is ready earlier, but it still waits for the same green light to display.
This is the full value of the sub-part approach: it tells you *where* to act, and prevents you from investing energy in a link that is not the bottleneck. On a well-optimized page, the ideal distribution looks like this:
- TTFB: about 40% of LCP;
- Download delay: less than 10%;
- Loading time: about 40%;
- Render delay: less than 10%.
The logic is clear: the two sub-parts that include the word "delay" are dead times, to be brought as close to zero as possible. The bulk of the LCP should be dedicated to what is incompressible: delivering the HTML, then downloading the resource. Any moment when neither of these is being loaded is an optimization opportunity.
What field data reveals (and which goes against the grain)
When the Chrome teams analyzed the LCP sub-parts on millions of real sites, the result surprised even the specialists. The historical advice, "reduce the weight of your images," simply doesn't correspond to where the time is lost. This study is detailed in Common misconceptions about how to optimize LCP.
Reading this graph is enlightening. For sites with a poor LCP, the image download time (350 ms) is the shortest of the four sub-parts. It's the TTFB (2,270 ms) and the download delay (1,290 ms) that cause the metric to skyrocket. In other words: the majority of struggling sites spend less than 10% of their LCP time downloading the implicated image.
The most telling figure concerns the loading time, that dead time too often ignored : a site with a poor LCP waits a median of 1.3 seconds between receiving the HTML and starting the image download, which is almost four times longer than the download itself. More than half of the 2.5-second budget is consumed before the browser even begins to fetch the page's main element.
The cause ? Almost always dependency chains. The LCP image is not declared directly in the HTML : it is set by a stylesheet that must first be downloaded, or injected by a script that must first execute. Each additional link in this chain delays the moment of discovery. HTTP Archive data confirms this unequivocally : the median LCP goes from 2.15s without an intermediate request, to 2.54s with one, then 2.85s with two.
Last myth debunked by the numbers : mobile isn't the culprit we imagine. For image download time, a mobile is only 20% slower than a desktop computer on average. The quality of mobile networks is no longer the excuse it once was. The real battle is fought on loading architecture, not on byte weight.
Optimizing LCP, section by section
Based on this diagnosis, here's how our experts intervene, in the order where the impact is generally strongest. The guiding objective never changes : ensure that the LCP resource starts loading as early as possible and displays immediately once downloaded.
1. Eliminate loading delay : the forgotten potential
This is, as the data has shown, the most profitable lever. The golden rule : your LCP resource must start downloading at the same time as the very first resource on the page. If it starts later, there's time to be gained.
First reflex : make the image discoverable by the preload scanner, that browser component that inspects the raw HTML even before rendering to anticipate downloads. An LCP image whose src (or srcset) is present directly in the HTML is discovered instantly. Conversely, an image injected via JavaScript or hidden behind a lazy-loading data-src attribute remains invisible until the script executes : this is exactly the delay we aim to eliminate.
We then explicitly signal the importance of the resource to the browser using the fetchpriority attribute (available on Chrome 102+, Safari 17.2+, and Firefox 132+). By default, the browser loads an image with low priority while it calculates its role in the layout: fetchpriority="high" promotes it immediately.
<!-- Image LCP directement dans le HTML, priorité haute -->
<img src="/img/hero.avif" fetchpriority="high"
width="1600" height="900" alt="Description pertinente">
The magnitude of the gain is not anecdotal: 5 to 30% on LCP depending on the stack, with an average of around 15%, according to Addy Osmani's (Chrome) measurements in his dedicated analysis. Three expert pitfalls to be aware of, however:
- a
fetchpriority="high"should only be set on a single image, a maximum of two: distributing it over ten resources means prioritizing nothing, and the browser reverts to its default heuristic; fetchprioritydoes not apply to CSS background images, which the preload scanner also doesn't see: if your LCP is abackground-image, convert it to<img>or<picture>;- the combination of
loading="lazy"andfetchpriority="high"is broken: lazy-loading always removes priority. Never set your LCP image to lazy.
Conversely, off-screen images, such as non-visible slides in a carousel, can be de-prioritized (fetchpriority="low") to free up bandwidth for the LCP. Finally, if the LCP image can only be a background image, preload it from the HTML to reveal it as early as possible:
<link rel="preload" as="image" href="/img/hero.avif"
type="image/avif" fetchpriority="high">
2. Order the correctly: the CAPO method
Here is a lever that 90% of sites neglect, and which directly affects loading time: the order of tags in the <head>. The preload scanner reads the <head> linearly. A synchronous <script> tag placed before a <link rel="preconnect"> delays the connection to your image CDN: the connection is only established once the script has executed. This means milliseconds are lost before the LCP resource is even discovered.
The CAPO (Critical Above Paint Order) method, formalized by Rick Viscomi of HTTP Archive in Get your head in order, proposes a strict ranking, from most to least priority: first meta charset (which must be within the first 1024 bytes of the document) and <title>, then critical preconnect and stylesheets, and only then scripts, prefetch, and SEO or Open Graph metadata. Ordering the <head> once in the template provides the browser with the shortest path to your critical resources.
In the same vein, resource hints deserve to be used precisely rather than scattered:
- a
preconnectto a font domain or a CORS CDN requires thecrossoriginattribute: without it, the established TLS connection is not reused and a new connection is opened, negating the benefit; - limit yourself to three or four
preconnects: each early connection has a cost, and beyond that the browser ignores the subsequent ones; - track down orphan preloads: a
<link rel="preload" as="image">pointing to an image that the page no longer uses (often a remnant of an old design) triggers a wasted download, and Chrome flags it in the console.
3. Eliminate render-blocking
Once the resource is downloaded, it must be displayed without delay. However, several culprits frequently delay this rendering:
- synchronous stylesheets or scripts in the
<head>that block rendering of the entire page until they are loaded; - an LCP element that is still absent from the DOM, waiting for JavaScript to inject it;
- an A/B testing tool that masks the content while deciding which variant to display;
- long tasks that monopolize the main thread, where image rendering also occurs.
The most effective solution is to lighten the critical rendering path. No synchronous script should be in the <head>: prefer defer (or async when order doesn't matter), ideally even moving it to the end of the body rather than the header.
<!-- À éviter : bloque le rendu -->
<head><script src="/js/app.js"></script></head>
<!-- À privilégier : n'interrompt pas l'affichage -->
<body>(...)<script src="/js/app.js" defer></body></head>
In this regard, server-side rendering (SSR) or static site generation (SSG) offer a decisive advantage: the content, including the LCP image, is present from the HTML itself, without depending on the download and execution of a JavaScript bundle. This is one of the reasons why purely client-side rendered sites structurally struggle with LCP. On this specific topic, our expertise in Single Page Applications helps overcome the blockers specific to modern frameworks.
4. Reduce resource load time
Data reminded us: your LCP is generally not decided here. But serving superfluous bytes remains a mistake, and on some sites, the image is truly the limiting factor. The first lever is the format. The preference hierarchy is now clear: AVIF first (about 50% lighter than JPEG, 20% less than WebP), WebP as a fallback, JPEG as a last resort via a <picture> tag. Be careful, however: WebP is not systematically better, as web.dev reminds us; on an already well-compressed image, conversion can increase file size, and AVIF has a higher decoding cost. An intelligent image pipeline only converts if the gain is real.
Next come the fundamentals:
- serve the right size via
srcsetandsizes, to never deliver a 2000px image to a mobile screen; - provide explicit dimensions (
widthandheight): they prevent layout shifts (CLS) and help the browser reserve space as early as possible; - bring the resource closer to the user with an image CDN, which often optimizes the format on the fly;
- completely remove the network for returning visitors thanks to an effective
Cache-Controlcache policy.
<img src="/img/hero-800.avif"
srcset="/img/hero-400.avif 400w,
/img/hero-800.avif 800w,
/img/hero-1600.avif 1600w"
sizes="(max-width: 600px) 100vw, 800px"
fetchpriority="high" width="800" height="450" alt="…">
A word on placeholder images (LQIP, for Low Quality Image Placeholder). Expert Harry Roberts details a crucial nuance in The ultimate LQIP/LCP technique: a poorly implemented placeholder, revealed by JavaScript, can delay the display of the actual image or distort the LCP measurement. The correct approach allows the final image to remain the LCP element, and reserves the placeholder for a light visual effect that doesn't block anything. Finally, if your LCP resource is a font (case of a title in webfont), a font-display: swap value displays the text immediately with a fallback font, then swaps it without blocking the LCP.
5. Reduce TTFB
Placed last because it's often the one with the least front-end control, TTFB remains foundational: nothing can happen on the browser side until the server has delivered its first byte. Every millisecond saved here mechanically impacts all subsequent steps. This is also confirmed by field data: on slow sites, TTFB alone (2,270 ms at the 75th percentile) is almost enough to doom the 2.5-second goal.
The classic causes of a high TTFB: cascading redirects (common behind ad links or shortcuts), an absence of HTML cache which forces regeneration of each page, or requests that bypass the CDN cache due to unnecessary URL parameters. The response relies on a solid server cache, infrastructure geolocated as close as possible to visitors, and controlled network configuration, the very foundation of our high-performance hosting offer.
The bonus: bfcache, an almost zero LCP
There is a case where LCP drops to a few milliseconds: navigation via the browser's back and next buttons, served by the Back/Forward Cache (bfcache). This mechanism keeps a complete snapshot of the already visited page in memory: upon return, it reappears instantly, without a new load or render. Navigations restored from bfcache report an almost zero LCP, which improves your aggregated field metrics.
However, one must not unintentionally deprive themselves of it. Certain practices disable bfcache: a Cache-Control: no-store header, the presence of an unload event handler, or certain unclosed open connections. The Chrome DevTools Application panel offers a dedicated test that precisely lists the reasons for ineligibility. A rigorous audit systematically includes this check, often yielding free gains.
Measure before optimizing, always
One principle guides our entire methodology: we do not optimize blindly. Before touching anything, you must know which sub-part is impacting your LCP, based on your actual data. An optimization that is relevant for one site may be completely useless for another: that's the whole point of diagnosis by sub-parts.
Concretely, we cross-reference three sources. First, field data (CrUX via PageSpeed Insights or Search Console, supplemented by RUM for low-traffic pages). Good news for diagnosis: since February 2025, CrUX now exposes the sub-parts of the LCP image and network round-trip time, where previously one had to resort to the lab. Then, lab tools (Lighthouse and the Chrome DevTools Performance panel, which show the details of the four sub-parts). Finally, for continuous monitoring, the PerformanceObserver API:
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lcp = entries[entries.length - 1];
console.log('LCP :', Math.round(lcp.startTime), 'ms');
console.log('Élément :', lcp.element);
}).observe({ type: 'largest-contentful-paint', buffered: true });
To go further, Google's web-vitals library, in its "attribution" variant, automatically calculates the breakdown into four sub-parts: it's the ideal tool for transforming an abstract LCP into a concrete action plan, directly for your real users.
In summary
LCP is not about compressing images: it's a loading chain to orchestrate. Remember the four-step approach our experts follow:
- make the LCP resource discoverable and startable as early as possible (clean HTML, preload scanner,
fetchpriority, CAPO-ordered<head>); - ensure its immediate display once downloaded (non-blocking rendering, SSR/SSG, free main thread);
- reduce its download time without sacrificing quality (AVIF, responsive images, CDN, cache);
- deliver the initial HTML as quickly as possible (TTFB, server cache, zero superfluous redirects).
This sub-part analysis, backed by real-world data, is precisely what distinguishes an optimization that shifts the problem from one that solves it. This is the core of our business: if your LCP resists despite your efforts, our web performance optimization experts identify the faulty link and fix it permanently. An audit often remains the best starting point to objectify your areas for improvement, with supporting figures.