- March 24, 2025
Largest Contentful Paint has always been one of the hardest Core Web Vitals to diagnose and fix. It is a chained metric, meaning multiple factors contribute to the final LCP time. You could spend weeks optimising images only to find out the real problem is a slow server, delayed JavaScript execution, or the way the browser discovers the LCP element.
For the last few years, really, all we had was a single LCP value. That number told us the end result but did not explain where the delay was happening. Now, with LCP subparts, we finally get a breakdown of what is slowing things down. This makes it possible to fix the real issue instead of wasting time on optimisations that will not move the needle.
For those of us in technical SEO, this changes how we communicate LCP issues to clients and developers. If you have ever had a developer push back on an LCP recommendation or a client assume that optimising images should be enough, this update gives you the data to show exactly where the problem is and why their LCP is still slow.
LCP measures how long it takes for the most important content on a page, usually an image or a large text block, to appear. Unlike First Contentful Paint, which only measures when something becomes visible, LCP waits for several processes to complete.
If any of these steps are slow, LCP suffers. The problem is that, before LCP subparts, we had no way of knowing which step was causing the issue.
Google now breaks LCP down into four measurable subparts, but only for image-based LCPs. Since images are responsible for most slow LCP cases, this breakdown is an important step forward.
This measures the time between the request for the LCP image and when the browser receives the first byte of data. If this is slow, the problem is happening before the browser even starts downloading the image.
Common causes:
Fixing this means looking at server-side optimisations. A good CDN can reduce latency, and caching strategies can speed up responses. Redirect chains should be minimised, and database queries should be optimised to avoid slow backend processing.
This is the time between when the browser discovers the LCP image and when it starts downloading it. If this is slow, the browser is taking too long to locate the image.
Common causes:
A common mistake is assuming that image optimisation alone will improve LCP. If the browser is not finding the LCP image early enough, reducing file size will not help. The best fix is to make sure the LCP image is in the initial HTML and not hidden inside JavaScript. Preloading the image using <link rel=”preload”> can also give the browser a head start.
This is the actual time taken to download the LCP image once the browser starts fetching it. If this is slow, the problem is with the image file itself.
Common causes:
Switching to modern image formats and using responsive images can make a significant difference. Enabling Brotli or gzip compression also helps reduce the amount of data transferred.
This measures the time between when the image is downloaded and when it actually appears on the screen. If this is slow, something is blocking rendering.
Common causes:
If everything else looks good but LCP is still slow, JavaScript and CSS are likely getting in the way. Reducing unnecessary JavaScript execution, deferring non-critical scripts, and ensuring CSS loads efficiently can help bring this number down.
Instead of relying on Lighthouse reports or guessing, you can now pull real-world LCP subpart data directly from the Chrome User Experience Report API.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import requests import json API_KEY = "API_KEY" URL = "https://yourwebsite.com" payload = { "formFactor": "PHONE", "url": URL, "metrics": [ "largest_contentful_paint_image_time_to_first_byte", "largest_contentful_paint_image_resource_load_delay", "largest_contentful_paint_image_resource_load_duration", "largest_contentful_paint_image_element_render_delay" ] } response = requests.post( f"https://chromeuxreport.googleapis.com/v1/records:query?key={API_KEY}", headers={"Accept": "application/json", "Content-Type": "application/json"}, json=payload ) if response.status_code == 200: print(json.dumps(response.json(), indent=4)) else: print(f"Error: {response.status_code}, {response.text}") |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
const fetch = require("node-fetch"); const API_KEY = "API_KEY"; const URL = "https://chrisleverseo.com"; const payload = { formFactor: "PHONE", url: URL, metrics: [ "largest_contentful_paint_image_time_to_first_byte", "largest_contentful_paint_image_resource_load_delay", "largest_contentful_paint_image_resource_load_duration", "largest_contentful_paint_image_element_render_delay" ] }; async function getLCPSubparts() { try { const response = await fetch( `https://chromeuxreport.googleapis.com/v1/records:query?key=${API_KEY}`, { method: "POST", headers: { "Accept": "application/json", "Content-Type": "application/json" }, body: JSON.stringify(payload) } ); if (!response.ok) throw new Error(`Error: ${response.status} - ${await response.text()}`); console.log(await response.json()); } catch (error) { console.error(error); } } getLCPSubparts(); |
LCP has always been a difficult metric to fix because it depends on multiple factors, from server speed to JavaScript execution. LCP subparts finally give us clear, actionable data that tells us exactly where the problem is. A massive shoutout to Barry Pollard at Google for bringing this to us.
Instead of blindly optimising images or making changes that might not help, we can now target the specific bottleneck, whether it is TTFB, resource load delay, image size, or rendering issues.
For anyone working on Core Web Vitals, this is a major step forward. If you are serious about improving LCP, start analysing your subpart data. It will save you hours of trial and error and make it easier to communicate LCP issues to both developers and stakeholders.
Read Google’s latest blog on subparts – https://developer.chrome.com/blog/crux-2025-02
Comments: