Largest Contentful Paint (LCP): how to measure and improve your page load
Table of contents
You open your site on mobile and the headline takes a while to show up. That gap is Largest Contentful Paint (LCP): the metric Google and your visitors use to judge whether your page loads fast. Here is how to measure it and how to fix it.
What Largest Contentful Paint (LCP) measures and what it doesn't
Largest Contentful Paint is the render time of the largest element visible in the viewport, counted from the start of the navigation. It measures when the main content is actually painted on screen. That clock includes redirects, the TLS handshake, the unload of the previous page and TTFB, which is why field data and lab data rarely match.
Which elements are LCP candidates
<img>and<image>inside an SVG.<video>: its poster or its first frame, whichever comes first.- Text blocks: paragraphs, headings and lists.
- Elements with a CSS
url()background; a gradient does not count.
The size is computed over the visible area: clipping and overflow do not add up. For images, the smaller of the visible and intrinsic size is used, so a huge photo shown as a thumbnail does not qualify on its real pixel count.
What the "low content" heuristics exclude
Chrome discards candidates with zero opacity, elements that cover the whole viewport as a background and low-entropy images: placeholders, blur-ups and single-colour blocks. LCP also stops reporting candidates as soon as the user interacts, and it is not counted when the page loaded in a background tab. An inline <svg> is not a candidate, but the same SVG image inside an <img src> is.
The thresholds: 2.5 seconds at the 75th percentile
2.5 seconds or less is good, between 2.5 and 4.0 seconds needs improvement and above 4.0 seconds is poor. These are the same thresholds Google publishes alongside the rest of the framework in Core Web Vitals and search results.
The nuance that changes everything is the 75th percentile of visits, with mobile and desktop reported separately. It is not an average and not the number from your last reload: the right question is not how long your site takes, but how long it takes for three out of four visits. And the metric does not stop at first paint: if a large banner appears after the headline and ends up bigger, the LCP is the banner's.
The four LCP subparts: where the time goes
Every page breaks its LCP down into four chunks that do not overlap and that add up to the total:
| Subpart | What it covers | Ideal share |
|---|---|---|
| TTFB | Until the first byte of HTML | ~40% |
| Load delay | From TTFB to the start of the element's download | <10% |
| Load duration | The download of the LCP resource | ~40% |
| Render delay | From the end of the download to the element being painted | <10% |
Two of them have "delay" in the name and should be driven to zero: that is work your site can skip. The other two involve the network and only shrink. The key idea, spelled out in the official LCP optimization guide, is that improving one subpart may not lower the total: it just moves the time to another one.
How to measure LCP: field data first, lab second
The order is not negotiable. Field data comes from real users (CrUX, PageSpeed Insights and the Core Web Vitals report in Search Console); lab data (Lighthouse, DevTools) is for diagnosing, not for deciding whether you have a problem.
What to check in the Core Web Vitals report
- It groups by URL groups and by device, with mobile and desktop kept apart.
- A group's status is that of its worst metric: if LCP is good but CLS fails, the group shows as poor.
- A group needs enough LCP and CLS data before it appears.
- The values shown are 75% of that group's visits.
Reading that panel well is almost a craft of its own: see how to use Google Search Console to improve your SEO.
Measuring LCP in JavaScript (and its traps)
You can record it with a PerformanceObserver on the largest-contentful-paint type, but it is smarter to use the web-vitals library instead of reimplementing the edge cases: iframes (the API does not measure them, the metric does), bfcache, prerendered pages (activationStart) and background tabs. The attribution build also hands you the subparts.
import { onLCP } from 'web-vitals/attribution';
onLCP(({ value, attribution }) => {
navigator.sendBeacon('/rum', JSON.stringify({ value, attribution }));
});
Your script may report a slightly different number than Google's, especially with ads and content inside iframes: your measurement approximates the metric, it does not replicate it.
What changed in 2026 (and why many guides are outdated)
- Chrome 147 (Feb 2026) emits candidates by comparing against the largest image already painted, not the one still pending download. On pages with a slow hero, intermediate candidates show up again in your analytics, while CrUX does not change.
- Chrome 151 (Jul 2026) delivers LCP for
<video>elements at the first paint opportunity, which lines field data up better with your own measurement. - Soft navigations are enabled by default since Chrome 151: you can now measure LCP for every route change in a single-page application. You need
web-vitalsv6 or higher and each soft navigation opens a new time origin, so itsstartTimehas to be subtracted. CrUX still does not publish metrics broken down by soft navigation, so this shows up in your analytics, not in Search Console.
And one correction to the classic advice: since Chrome 133 the renderTime of cross-origin resources is exposed without needing Timing-Allow-Origin. If your guide says you need it to avoid skewing LCP, update it.
How to improve LCP: four fronts, in order
1. Load delay: make the resource discoverable early
The LCP resource should be in the HTML, not injected by JavaScript or referenced only from an external stylesheet. If that is impossible, use <link rel="preload" fetchpriority="high" as="image">. Never put loading="lazy" on the main image, and limit fetchpriority="high" to one or two images: apply it to all of them and it helps none.
2. Load duration: fewer bytes, closer to the user
Modern formats (WebP or AVIF), correct dimensions with srcset and sizes, solid compression, a CDN and geographic proximity. Serve the resource from the same origin as the HTML. This complements what we already covered in how to optimize your images for SEO.
3. Render delay: remove what blocks
Fonts with font-display and a preload for the headline typeface, and no CSS or JS blocking the render ahead of the hero. If your site is client-rendered, the hero arrives late: it needs HTML with the content inside. And never keep the main element hidden until a script finishes.
4. TTFB: first in line
Fewer redirects in a chain, HTML caching and hosting that can keep up. A practical rule: if your LCP sits almost on top of TTFB, there is nothing to fix in the front end; the bottleneck is the server.
Mistakes that hurt LCP without you noticing
- Lazy loading on the main image.
- A carousel with several high-priority images competing with each other.
- The hero set as a
background-imagein an external stylesheet. - Web fonts with no preload and no
font-display. - Trusting Lighthouse alone, or the average of a single visit.
- Testing on your fibre-connected laptop and never looking at the mobile report.
How to verify a real improvement
Go back to field data. CrUX uses a 28-day window, so you will see the improvement progressively. Check by device and by URL group: if the homepage improved but a category page got worse, the problem is still there. Note a baseline before touching anything (date and 75th percentile on mobile and desktop).
LCP and SEO: what Google actually says
The official documentation is clear: Core Web Vitals are used by ranking systems, but there is no single page experience signal and the other page experience aspects do not directly help you rank. Google aims to show the most relevant content even when the experience is poor, as its page experience guide reminds us. The practical translation: LCP is not a ranking lever, it is an experience and conversion lever that also works in your favour when you compete with equally good content for the same query.
Conclusion: the checklist
- Look at your mobile 75th percentile in field data, not on your own machine.
- Break LCP down into its four subparts and note which one weighs the most.
- Identify the exact LCP element, by name.
- Attack load delay and render delay first: those can go to zero.
- Cut bytes and serve the resource from the same origin as the HTML.
- Return to field data after 28 days and compare by device and URL group.
Order matters more than speed: optimizing without measuring usually moves the problem around instead of solving it.