How to improve LCP: the four parts of LCP time

LCP breaks down into TTFB, resource load delay, load duration and render delay. How to measure each part and what to fix in each one.

Check my site freeSee the plans
On this page 9 min read
  1. What LCP measures and which elements count
  2. The four parts of LCP time
  3. How to measure each part
  4. How to reduce TTFB: server, CDN and caching
  5. How to reduce resource load delay
  6. How to reduce load duration
  7. How to reduce render delay
  8. How to confirm the fix in field data
  9. How to check your site

LCP (Largest Contentful Paint) measures how long the largest visible element on the screen, usually an image or a block of text, takes to appear after the user opens the page. Google considers an LCP of up to 2.5 seconds at the 75th percentile of visits good, and anything above 4 seconds poor (web.dev). To bring the number down, split the time into four parts (TTFB, the delay before the resource starts downloading, the download duration and the render delay), find out which one is large and fix that one. Optimizing the wrong part usually just pushes the time into another part.

This article follows the web.dev guide Optimize Largest Contentful Paint (updated in March 2025). For the context of all three metrics, see the Core Web Vitals guide.

What LCP measures and which elements count

LCP timing starts when the user begins the navigation. That is why it already includes redirects, connection setup and the time until the first byte of HTML arrives (web.dev). The elements that can be the "largest content" are:

  • <img> (for animated images, the first frame counts);

  • <image> inside an <svg>;

  • <video> (whichever appears first counts: the poster image or the first frame);

  • elements with a background image loaded through url() in CSS (gradients do not count);

  • block-level elements that contain text.

Chrome ignores some cases so it does not reward the wrong element: elements with opacity 0, elements that cover the whole screen (probably a background) and low-entropy images such as placeholders. Only the part visible on screen counts toward the size, and for a resized image the smaller of the displayed size and the file's real size is used (web.dev).

The LCP element can differ by device: the heading on mobile, the banner photo on desktop. Check both before deciding what to optimize.

The four parts of LCP time

web.dev splits LCP into four subparts. Added together, they make up the whole LCP:

PartWhat it measuresSuggested share of LCP
TTFB (Time to First Byte)From the start of the navigation until the first byte of HTML arrives.about 40%
Resource load delayFrom TTFB until the browser starts downloading the LCP resource (the image, for example). It is zero if the element is text with a font that is already available.under 10%
Resource load durationThe time the download of the resource itself takes. It is also zero when there is no resource.about 40%
Element render delayFrom the end of the download until the element appears on screen.under 10%

The percentages come from the guide itself and are a reference, not a rule (web.dev). The useful reading is comparative: if the two "delay" parts add up to a large share of LCP, the problem is not the image size, and compressing the file further will change little.

How to measure each part

Start with field data from real users, and only then move to the lab. The difference between these sources is explained in Lighthouse, CrUX and RUM. These are the tools that show the four parts:

  1. The web-vitals library, in its attribution build, calculates the four parts for each user's visit and reports the element and the resource URL (GitHub GoogleChrome/web-vitals). It is how you find out what happens on your visitors' devices.

  2. The Chrome DevTools Performance panel shows the local LCP next to the CrUX field LCP, and the recording's Insights give the time of each part (web.dev).

  3. PageSpeed Insights shows CrUX data for the URL or the origin, split into mobile and desktop. The Lighthouse diagnostics include the LCP element breakdown.

A minimal web-vitals example that logs the four parts to the console:

import {onLCP} from 'web-vitals/attribution';

onLCP(({value, attribution}) => {
  console.log('LCP', value, attribution.target, attribution.url);
  console.log('TTFB', attribution.timeToFirstByte);
  console.log('Resource load delay', attribution.resourceLoadDelay);
  console.log('Resource load duration', attribution.resourceLoadDuration);
  console.log('Element render delay', attribution.elementRenderDelay);
});

In production, replace console.log with a call that sends the data to your analytics system. Two signals help with a quick diagnosis: a large gap between TTFB and FCP (First Contentful Paint, the first paint of content) usually points to render-blocking CSS or JavaScript, or heavy client-side rendering; a large gap between FCP and LCP means the LCP resource is not available early (web.dev).

How to reduce TTFB: server, CDN and caching

If the HTML is slow to arrive, nothing else starts. The web.dev guide on optimizing TTFB points to these areas:

  • Hosting with enough memory and capacity for the traffic the site gets.

  • A CDN, to serve content from a server closer to the user.

  • Caching at the CDN edge with Cache-Control headers, including the HTML when it is not personalized. Unique URL parameters (campaign parameters, for example) can keep the page from being served from cache.

  • Fewer redirects. The most common are same-origin ones, such as a missing https:// or an inconsistent trailing slash. HSTS avoids the redirect from HTTP to HTTPS.

  • HTML sent in chunks (streaming), or static pages when there is no personalization.

  • 103 Early Hints, so the browser starts downloading critical resources while the server builds the response.

To find out where the server spends its time, the Server-Timing header exposes each backend step and shows up in DevTools and in the Navigation Timing API:

Server-Timing: db;desc="Database";dur=121.3, ssr;desc="Server-side Rendering";dur=212.2

The names and numbers above are the web.dev example; each backend reports its own steps.

How to reduce resource load delay

The goal here is for the browser to start downloading the LCP image right after it receives the HTML.

Make the image visible in the initial HTML

The browser has a preload scanner that looks for resources in the HTML before it even builds the page. It only finds the image if it is in the markup with src or srcset. An image inserted by JavaScript, loaded by a lazy-loading library or defined only in CSS is hidden from it (web.dev).

If the LCP image has to come from an external stylesheet, such as a banner background, preload it in the <head>:

<link rel="stylesheet" href="/css/styles.css">
<link rel="preload" fetchpriority="high" as="image"
      href="/img/banner.webp" type="image/webp">

Give the LCP image high priority

The fetchpriority="high" attribute asks the browser to download the image before the others. web.dev recommends using it on one or two images at most, and fetchpriority="low" on images that start off screen, such as the later slides of a carousel:

<img src="/img/banner.webp" fetchpriority="high"
     width="1200" height="600" alt="Banner description">

<img src="/img/slide-3.webp" fetchpriority="low"
     width="1200" height="600" alt="Slide description">

According to MDN compatibility data, fetchpriority works in Chrome 101, Firefox 132 and Safari 17.2 onward (MDN browser-compat-data). Older browsers ignore the attribute without breaking anything.

Do not lazy-load the LCP image

loading="lazy" makes the browser wait for layout before deciding whether to download the image, and that delays the most important resource on the page. web.dev recommends loading images in the initial viewport normally and keeping lazy loading for the ones further down (web.dev). CMS themes and plugins sometimes apply loading="lazy" to every image; check the generated HTML.

<!-- Avoid on the LCP image -->
<img src="/img/banner.webp" loading="lazy" alt="...">

<!-- Prefer -->
<img src="/img/banner.webp" fetchpriority="high" alt="...">

When possible, serve the LCP image from the same origin as the HTML, so it reuses the connection that is already open.

How to reduce load duration

The guide points out that, most of the time, the download duration is not the biggest bottleneck. Check the data before investing here (web.dev). When it is, the fixes are:

  • Modern formats such as AVIF and WebP, with appropriate compression.

  • An image at the size it is displayed, with srcset and sizes so the browser picks the right version.

  • A CDN, preferably an image CDN.

  • Few high-priority resources at the same time, so they do not compete for bandwidth.

  • Efficient caching, so the next visit does not download it again.

A responsive image example, adapted from MDN (MDN):

<img
  srcset="/img/banner-480.webp 480w, /img/banner-800.webp 800w"
  sizes="(width <= 600px) 480px, 800px"
  src="/img/banner-800.webp"
  width="800" height="400"
  fetchpriority="high"
  alt="Banner description">

The browser reads sizes, uses the first condition that is true to know the width of the slot, and picks the image from srcset that fits. The width and height attributes do not change LCP, but they keep the page from jumping when the image arrives (see how to fix CLS).

How to reduce render delay

At this point the image has been downloaded, but something keeps the browser from showing it. The most common causes, according to web.dev:

Render-blocking CSS

The browser paints nothing until it finishes downloading the stylesheets in the <head>. If the CSS arrives after the image, the image waits. The ways out are removing unused CSS, deferring what is not needed for the first screen, and minifying and compressing. Putting the CSS directly in the HTML (inline) only pays off when it is small.

Synchronous JavaScript in the head

A script without defer or async in the <head> stops HTML parsing until it is downloaded and executed:

<!-- Blocks -->
<head>
  <script src="/js/main.js"></script>
</head>

<!-- Does not block: runs after the HTML is parsed -->
<head>
  <script src="/js/main.js" defer></script>
</head>

Content built in the browser

When the page depends on JavaScript to generate the HTML (client-side rendering), the LCP image only exists after the script downloads and runs. Even with a preload, the element still waits for the code. web.dev recommends server-side rendering and, when the architecture allows it, prerendered or statically generated pages. Long JavaScript tasks on the main thread also delay the paint; breaking those tasks up helps both LCP and INP (see how to improve INP).

Fonts, when the LCP element is text

If the LCP element is a heading, the web font can keep the text invisible while it loads. The font-display property controls this: with block the text can stay invisible for 2 to 3 seconds; with swap it appears right away in the fallback font; with optional the browser waits 100 ms at most and, if the font has not arrived, carries on with the fallback (web.dev). For LCP, the guide recommends any value other than auto and block. WOFF2 files, character subsets and fewer font families also cut download time. swap can cause a layout shift when the font changes, which is covered in the CLS article.

How to confirm the fix in field data

Lighthouse and DevTools show the effect of a change right away, on your own computer. The result that counts is the one from real users, and it takes time to show up. To track this across several sites at once, see how to monitor performance across multiple sites.

  1. Ship the fix and note the date.

  2. If you have RUM (real user monitoring) with web-vitals, compare the 75th percentile of LCP before and after, split by mobile and desktop. The four parts show whether the share you targeted went down.

  3. In CrUX, through PageSpeed Insights or the API, the value is a 28-day rolling average (Chrome for Developers). The improvement comes in gradually as the old days leave the window; wait for the full cycle before drawing conclusions.

  4. Check that the share you reduced has not reappeared in another part. Removing lazy loading, for example, can turn load delay into load duration if the image is heavy.

Keep in mind that CrUX only collects data from Chrome users who opted in to sharing statistics, on desktop and Android; Chrome on iOS is left out (Chrome for Developers). Low-traffic pages may not have their own CrUX data.

How to check your site

In WebVyze, the LCP finder uses Google PageSpeed to point out the element and what delays the page, and the Core Web Vitals checker shows CrUX data for the last 28 days, on mobile and desktop, for up to 10 URLs. Both tools are free. On the paid plans, a real-visitor script based on the web-vitals library records the LCP of each visit along with its cause.

Frequently asked questions

Can an SVG be the LCP element?
An svg element on its own does not count in Chrome. An image tag inside an svg, however, can be the LCP element.
When does the browser stop looking for the LCP element?
As soon as the user interacts with the page (a tap, a scroll or a key press). After that, no new LCP candidates are reported.
Does TTFB have its own threshold?
web.dev suggests 0.8 seconds or less as good and treats anything above 1.8 seconds as poor. TTFB is not a Core Web Vital; these numbers are a guide for diagnosis.
How is LCP measured on prerendered pages?
On those pages LCP should be counted from the moment the page is activated (activationStart), not from the start of the navigation.
About the author

Daniel Paz

WordPress, performance and SEO specialist, founder of WebOption

Daniel Paz is a WordPress, performance and SEO specialist, the founder of WebOption and an international speaker. With more than 18 years of experience in web development, he helps companies turn their websites into fast, efficient, results-driven platforms.

See what your site needs to fix.

A free check by law, with the cause and the fix guide for each problem.

Check my site freeCreate a free account

Keep reading

Partners

Partner benefits with one coupon.

WPLIGHT

Partner links. We may earn a commission at no extra cost to you.