Measure And Optimize Largest Contentful Paint (LCP)
The Largest Contentful Paint (LCP) measures how soon after navigation the main content of a website appears. This is the time during the page load process when the largest page element is rendered.
The Largest Contentful Paint is one of the three Core Web Vitals Google uses as part of its search rankings, along with Cumulative Layout Shift and First Input Delay.
This article explains how LCP works, how to measure LCP, and how to optimize it.
What is the Largest Contentful Paint?β
The Largest Contentful Paint measures at what point in the page load process the single largest content element appears.
LCP is a paint timing metric, like First Paint and First Contentful Paint. Contentful means that content like text or an image were rendered, rather than just showing empty boxes.
In the website filmstrip above the LCP is caused by text rendering. But it's also common for the LCP element to be an image.
What is a good Largest Contentful Paint?β
Websites should have an LCP of 2.5 seconds or less.
Largest Contentful Paint is one of Google's Core Web Vitals metrics that impact search result rankings.
LCP range | Rating |
---|---|
< 2.5 seconds | Good |
< 4.0 seconds | Needs Improvement |
> 4.0 seconds | Poor |
How to optimize Largest Contentful Paintβ
Here's a step by step guide on how to fix LCP issues on your website.
Start by identifying the element responsible for the Largest Contentful Paint, then look at what you can do to make this element render more quickly.
Identify the Largest Contentful Paint elementβ
To optimize the LCP metric, you first need to identify the element that's causing the paint.
You can do that using different performance tools, including PageSpeed Insights, Lighthouse, Chrome DevTools or DebugBear. See below for details on how to do this with different tools.
Identify what's blocking the Largest Contentful Paintβ
Let's say you see that the Largest Contentful Paint happens after 3 seconds. Why is that? What's blocking it so that it doesn't happen after, say, 2 seconds?
You need to identify the chain of requests that leads to the largest paint on your page.
Some of the potential optimizations here will apply to all LCP elements, others are specific to the type of content that's causing the LCP (for example text or images).
Render-blocking resourcesβ
Render-blocking resources prevent all page content from rendering. They therefore put a lower bound on when your largest content can appear.
Tools like Lighthouse or DebugBear can tell you what requests on your page block rendering.
CSS stylesheets are generally render-blocking (in order to prevent a flash of unstyled content before the styles are loaded). Avoid using @import
in your code as it creates sequential CSS request chains.
JavaScript files can also be render-blocking, though using the async
attribute or placing them at the bottom of the page avoids that problem.
Parser-blocking requests only block rendering of content that's placed below them in the HTML document.
Have all render-blocking requests finished but no content is showing up? Consider whether an anti-flicker snippet is blocking rendering.
Keep in mind that the document request is also always render-blocking, so reducing the Time to First Byte of your server response will also improve the Largest Contentful Paint.
If the LCP element is an imageβ
You'll need to find out what the image URL is and how you can make that request finish faster. In DebugBear the LCP image is highlighted with an LCP badge.
How can you make the image load faster?
- Image compression: a smaller image file will take less time to load than a large one. Use modern formats like WebP and AVIF.
- Shorten request chains: check the request initiator to see if the image is referenced in the body HTML of the document. If it isn't then you can move on to investigating how to speed up the resource that does initiate the LCP image request
- Avoid new server connections: if you load the document from example.com and the LCP image from image-cdn.com then the browser will need to establish a server connection to the other server before starting the image download. If a server connection is reused then this is not necessary, typically saving three round trips on the network.
- Preload the LCP image: Preloading a resource not only helps the browser discover it sooner but also prioritizes downloading it.
Let's take a look at the example request waterfall.
The image is only 30 KB large, so there's not much room to optimize further.
The request initiator is a <picture>
element in the HTML document, so the request chain is already quite short. (Although, if we really wanted to optimize the image LCP, we could embed the image in the document as a data URL.)
The document is loaded from www.shopify.com, but the image is loaded from cdn.shopify.com. That means a new connection is created for the first request on the cdn subdomain.
Loading the LCP image from the same server as the document would avoid having to wait for this new connection to be established before starting to download the image.
Finally, would a preload resource hint help here? I've shown some of the other requests that I didn't include in the previous waterfall views.
The LCP image is already linked in the document HTML, so a preload hint would not help the browser discover the resource sooner. However, it might increase the priority of the image request.
We can see that another resource is preloaded, and Chrome is allocating bandwidth to this resource first. Prioritizing the LCP image instead could help optimize the LCP score for this page.
Don't lazy load the LCP imageβ
Lazy loading can speed up your website by focusing bandwidth on high-priority resources. However, when used on LCP elements it can actually make your site slower, as these images should be loaded with a high priority.
Google found that websites with image lazy loading enabled tends to have a higher LCP. So be careful not to use loading="lazy"
incorrectly.
Prioritize the LCP imageβ
Even if you're not lazy loading the LCP image, the browser still assumes that most images on a page are low priority. For example, they might only appear below the fold or in the page footer.
The browser cannot identify important above-the-fold images until after the initial render of the page, when all relevant styles have been loaded.
You can use the fetchpriority="high"
priority hint to tell the browser that a specific image request is important. Since this is done in the initial page HTML, the browser can make a high-priority request for the image without waiting for the image to load.
If the LCP image is a CSS background image you can add a preload tag to HTML:
<link rel="preload" href="/images/photo.jpg" as="image" fetchpriority="high">
LCP Image Request Chainsβ
Ideally, only two requests should be needed to load an LCP image:
- Load the HTML Document
- Load the LCP image referenced in the document
But sometimes additional requests are part of the LCP request chain.
This waterfall shows a page that first loads and runs lazyload.min.js
before starting to download the LCP image. Instead of being initiated by the HTML, the LCP request is triggered by the JavaScript library.
If you use JavaScript to lazy load the LCP image, disable lazy loading or using the native loading="lazy"
attribute.
Client side applications can also cause request chains that include JavaScript files. Ideally you should implement server side rendering for you JS app, or otherwise optimize the client-side code by reducing bundle size or loading JSON data earlier.
To view requests that are involved in loading the Largest Contentful Paint image you can run a DebugBear speed test and open the Requests tab. Then click on the request marked LCP and open the Request Chain tab.
Be careful with progressive JPEGsβ
Progressive JPEGs can speed up your website by quickly showing a low-quality image and then loading the full-size version later on.
Unfortunately, the Largest Contentful Paint is only registered when the image is fully downloaded. In the filmstrip below it looks like the LCP element has rendered after 3.5 seconds, but actually the full-size version of the image only loads after 6.3 seconds.
Progressive JPEGs aren't larger than non-progressive files of the same quality. But to improve Core Web Vitals you still need to optimize the overall JPEG size even if the user experience is great already.
If the LCP element is a heading or paragraphβ
Ideally the text of the LCP element is included in the page HTML. Then it should show up as soon as all render-blocking resources are loaded.
However, there are a few reasons why text might only render later.
Web fonts take a while to loadβ
Here's an example where the Largest Contentful Paint doesn't happen until fonts are loaded. Using font-display: swap
can prevent this.
Text is inserted using JavaScriptβ
This waterfall shows the request waterfall of a typical single-page app. After loading the document, the page loads several JavaScript files, then loads the data it wants to display, and then finally spends a bunch of CPU time rendering the page. Only then does the LCP content appear.
The ideal solution here is server-side rendering. If that's not an option, make sure your app bundles load in parallel and work on reducing their download size.
How does Largest Contentful Paint affect Lighthouse scores?β
As of version 10.0.1 (Feb 2023) the Largest Contentful Paint determines 25% percent of the overall Lighthouse Performance score.
This table shows the maximum LCP you'd need to achieve a certain Largest Contentful Paint score.
LCP subscore | Max LCP (Mobile) | Max LCP (Desktop) |
---|---|---|
100 | 1.5s | 0.6s |
90 | 2.5s | 1.2s |
50 | 4.0s | 2.4s |
10 | 6.5s | 4.9s |
The performance subscore for LCP is visible in DebugBear for example:
Largest Contentful Paint definitionβ
Changes to the LCP definitionβ
Different versions of Chrome measure LCP slightly differently. Google keeps track of changes to the LCP definition here.
These changes aim to reduce cases where the LCP is identified incorrectly, resulting in a metric value does not match real user experience.
For example, full-size background images used to be counted as LCP candidates, even though they are less important than the main page content. Now, full-size backgrounds are ignored for the LCP calculation.
When is the LCP value final?β
The Largest Contentful Paint can update later on in the page load process, if a large part of the UI is updated.
For example, the LCP might update if:
- a low-resolution image is replaced with a high-resolution one
- a slider advances to the next slide
- parallax is enabled on a background image
Once the user starts interacting with a page, for example by clicking on it, the LCP value no longer updates.
Measuring Largest Contentful Paintβ
PageSpeed Insightsβ
The easiest way to measure the Largest Contentful Paint of your website is to use Google's PageSpeed Insights tool. It will report real user data Google has already collected and also trigger a lab-based test of your website. These results are reported under Field Data and Lab Data, respectively.
Google uses a fairly slow network connection in the lab test, so your metrics will usually be slower in the lab than they are for real users. For example, for you Youtube the field LCP is 3.1s, while the lab test returns a value of 7.8s.
If you speed up your website the lab data will improve immediately. However, it will take a while for up-to-date real-user data to be collected.
The lab test is run using Lighthouse, so PageSpeed Insights also provides a deeper page analysis that highlights opportunities for improvement.
How to identify the Largest Contentful Paint elementβ
You can also find the element that caused the Largest Contentful Paint in the Diagnostics section under Largest Contentful Paint Element.
Chrome DevToolsβ
If you run a Performance recording in Chrome's developer tools, you can find the Largest Contentful Paint in the Timings Section.
If you click on the LCP marker it will highlight the element causing that paint on the page.
DevTools also has a Lighthouse tab that generates a report similar to the PageSpeed Insights one.
Using the Largest Contentful Paint APIβ
You can use a PerformanceObserver
to access the LCP of a page that's open in your browser. Paste the following script in the console of your browser's developer tools:
let lastLcp;
const po = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
for (const entry of entries) {
if (entry.startTime !== lastLcp) {
console.log(
`New LCP: ${entry.startTime}ms
Size: ${entry.size} px^2
HTML: ${entry.element ? entry.element .outerHTML.slice(0, 80): "(no element)"}`
);
lastLcp = entry.startTime;
}
}
});
po.observe({ type: "largest-contentful-paint", buffered: true });
Every time the Largest Contentful Paint is updated you'll get a message in the console showing the element HTML and the size of the element.
Largest Contentful Paint in Google Search Consoleβ
Google uses the LCP metric as a ranking factor, so reducing it improves your site's SEO.
Search Console can help you identify pages with a poor LCP. Select Core Web Vitals in the sidebar, then Open Report for either the mobile or desktop data.
You can click on each reported issue to see example URLs where LCP needs improvement. Note that Search Console groups pages together into different groups βΒ the data you see doesn't always apply to that specific page URL.
Monitoring Largest Contentful Paint and other page speed metricsβ
DebugBear shows the Largest Contentful Paint as part of the filmstrip view showing the rendering progress of the page.
If you're monitoring your website with DebugBear you can see LCP trends on the project overview page.
The Web Vitals tab shows both lab-based LCP data and data from Google's Chrome User Experience Report (CrUX).
You can also see what the Largest Contentful Paint element is. It's highlighted with a red border in the screenshot.
Filtering down the list of network requests on the page to just those before the Largest Contentful Paint lets you see what's holding back rendering.