Optimize Largest Contentful Paint
Largest Contentful Paint (LCP) is one of the three Core Web Vitals metrics, and it represents how quickly the main content of a web page is loaded. Specifically, LCP measures the time from when the user initiates loading the page until the largest image or text block is rendered within the viewport.
To provide a good user experience, sites should strive to have an LCP of 2.5 seconds or less for at least 75% of page visits.
There are a number of factors that can affect how quickly the browser is able to load and render a web page, and delays across any of them can have a significant impact on LCP.
It's rare that a quick fix to a single part of a page will result in a meaningful improvement to LCP. To improve LCP you have to look at the entire loading process and make sure every step along the way is optimized.
Understanding your LCP metric
Before optimizing LCP, developers should seek to understand if they even have an LCP issue, and the extent of any such issue.
LCP can be measured in a number of tools and not all of these measure LCP in the same way. To understand LCP of real users, we should look at what real users are experiencing, rather than what a lab-based tool like Lighthouse or local testing shows. These lab-based tools can give a wealth of information to explain and help you improve LCP, but be aware that lab tests alone may not be entirely representative of what your actual users experience.
LCP data based on real users can be surfaced from Real User Monitoring (RUM) tools installed on a site, or via the Chrome User Experience Report (CrUX) which collect anonymous data from real Chrome users for millions of websites.
Using PageSpeed Insights CrUX LCP data
PageSpeed Insights provides access to CrUX data in the top section labeled Discover what your real users are experiencing. More detailed lab-based data is available in the bottom section labeled Diagnose performance issues. If CrUX data is available for your website, always concentrate on the real user data first.
{% Img src="image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/enRFus2GE24gvchY9fdV.png", alt="CrUX data shown in PageSpeed Insights", width="800", height="478" %}
{% Aside %}
Where CrUX does not provide data (for example, a page with insufficient traffic to get page-level data), CrUX should be supplemented with RUM data collected using JavaScript APIs running on the web page. This can also provide much more data than CrUX can expose as a public dataset. Later in this guide we will explain how to collect this data using JavaScript.
{% endAside %}
PageSpeed Insights shows up to four different CrUX data:
Mobile data for This URL
Desktop data for This URL
Mobile data for the whole Origin
Desktop data for the whole Origin
These can be toggled in the controls at the top, and top right-hand side of this section. Be aware that where a URL does not have sufficient data to be shown at the URL level—but does have data for the origin—PageSpeed Insights will automatically show this.
{% Img src="image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/wWQxf1DbjElItJTcJsBn.png", alt="PageSpeed Insight's falling back to origin-level data where url-level data is not available", width="800", height="295" %}
The LCP for the whole origin may be very different to an individual page's LCP depending on how the LCP is loaded on that page compared to other pages on that origin. It can also be affected by how visitors navigate to these pages. Home pages tend to be visted by new users and so may often be loaded "cold", without any cached content and so are often the slowest pages on a website.
Looking at the four different categories of CrUX data can help you understand whether an LCP issue is specific to this page, or a more general site-wide issue. Similarly, it can show which device types have LCP issues.
Using PageSpeed Insights CrUX supplementary metrics
Those looking to optimize LCP should also use the First Contentful Paint (FCP) and Time to First Byte (TTFB) timings, which are good diagnostic metrics that can provide valuable insights into LCP.
TTFB is the time when the visitor starting to navigate to a page (for example, clicking on a link), until the first bytes of the HTML document are received. A high TTFB can make achieving a 2.5 second LCP challenging, or even impossible.
A high TTFB can be due to mutiple server redirects, visitors located far away from the nearest site server, visitors on poor network conditions, or an inability to use cached content due to query parameters.
Once a page starts rendering, there may be an initial paint (for example, the background color), followed by some content appearing (for example, the site header). The appearance of the initial content is measured by FCP. The delta between FCP and other metrics can be very telling.
A large delta between TTFB and FCP could indicate that the browser needs to download a lot of render-blocking assets. It can also be a sign it must complete a lot of work to render any meaningful content—a classic sign of a site that relies heavily on client-side rendering.
A large delta between FCP and LCP indicates that the LCP resource is either not immediately available for the browser to prioritize (for example, text or images that are managed by JavaScript rather than being available in the initial HTML), or that the browser is completing other work before it can display the LCP content.
Using PageSpeed Insights Lighthouse data
The Lighthouse section of PageSpeed Insights offers some guidance to improving LCP, but first you should check if the LCP given is broadly inline with real user data provided by CrUX.
If Lighthouse is showing no LCP issue, but the CrUX data is, then any Lighthouse suggestions may not be relevant. The opposite is also true—if Lighthouse is showing a really poor LCP time, but CrUX data is showing your users mostly have a good LCP, then you may wish to consider the priority of optimizing LCP further, or if time is better spent on other performance improvements. Also be sure to check that the CrUX data is for this page and not for the full origin as detailed above.
If both sources of data are showing an LCP that should be improved than the Lighthouse section can provide valuable guidance on ways to improve LCP. Use the LCP filter to only show audits relevant to LCP:
{% Img src="image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/himAvC1GMr7K0kcbvG4F.png", alt="Lighthouse LCP Opportunities and Diagnostics", width="800", height="498" %}
As well as the Opportunities to improve, there is Diagnostic information that may provide more information to help diagnose the issue. The Largest Contentful Paint element diagnostic shows a useful breakdown of the various timings that made up the LCP:
{% Img src="image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/KhIuP2CNToNeGa33w48K.png", alt="Lighthouse LCP phases", width="800", height="474" %}
We will delve into these sub-parts next.
LCP breakdown
Optimizing for LCP can be a more complex task when PageSpeed Insights does not give you the answer on how to improve this metric. With complex tasks it's generally better to break them down into smaller, more manageable tasks and address each separately. This guide will present a methodology for how to break down LCP into its most critical sub-parts and then present specific recommendations and best practices for how to optimize each part.
{% Aside %}
For a visual overview of the context presented in this section, see A Deep Dive into Optimizing LCP from Google I/O 2022:
{% YouTube "fWoI9DXmpdk" %}
{% endAside %}
Most page loads typically include a number of network requests, but for the purposes of identifying opportunities to improve LCP, you should start by looking at just two:
The initial HTML document
The LCP resource (if applicable)
While other requests on the page can affect LCP, these two requests—specifically the times when the LCP resource begins and ends—reveal whether or not your page is optimized for LCP.
To identify the LCP resource, you can use developer tools (such as PageSpeed Insights discussed above, Chrome DevTools, or WebPageTest) to determine the LCP element. From there, you can match the URL (again, if applicable) loaded by the element on a network waterfall of all resources loaded by the page.
For example, the following visualization shows these resources highlighted on a network waterfall diagram from a typical page load, where the LCP element requires an image request to render.
{% Img src="image/eqprBhZUGfb8WYnumQ9ljAxRrA72/96YFhl0GQYIKFnJeOsVW.png", alt="A network waterfall with the HTML and LCP resources highlighted", width="800", height="375" %}
For a well-optimized page, you want your LCP resource request to start loading as early as it can, and you want the LCP element to render as quickly as possible after the LCP resource finishes loading. To help visualize whether or not a particular page is following this principle, you can break down the total LCP time into the following sub-parts:
{% Img src="image/eqprBhZUGfb8WYnumQ9ljAxRrA72/4AriEgko87GR1iZSgOou.png", alt="A breakdown of LCP showing the four individual sub-parts", width="800", height="450" %}
This table explains each of these LCP sub-parts in more detail:
* If the LCP element does not require a resource load to render (for example, if the element is a text node rendered with a system font), this time will be 0.
LCP sub-part
Description
Time to first byte (TTFB)
The time from when the user initiates loading the page until when the browser receives the first byte of the HTML document response. (See the TTFB metric doc for more details.)
Resource load delay
The delta between TTFB and when the browser starts loading the LCP resource. *
Resource load time
The time it takes to load the LCP resource itself. *
Element render delay
The delta between when the LCP resource finishes loading until the LCP element is fully rendered.
Every single page can have its LCP value broken down into these four sub-parts. There is no overlap or gap between them, and collectively they add up to the full LCP time.
When optimizing LCP, it's helpful to try to optimize these sub-parts individually. But it's also important to keep in mind that you need to optimize all of them. In some cases, an optimization applied to one part will not improve LCP, it will just shift the time saved to another part.
For example, in the earlier network waterfall, if you reduced the file size of our image by compressing it more or switching to a more optimal format (such as AVIF or WebP), that would reduce the resource load time, but it would not actually improve LCP because the time would just shift to the element render delay sub-part:
{% Img src="image/eqprBhZUGfb8WYnumQ9ljAxRrA72/M3JgFnahW8pPb9o1lf2r.png", alt="The same breakdown of LCP shown earlier where the resource load time sub-part is shortened but the overall LCP time remains the same.", width="800", height="450" %}
The reason this happens is because, on this page, the LCP element is hidden until the JavaScript code finishes loading, and then everything is revealed at once.
This example helps illustrate the point that you need to optimize all of these sub-parts in order to achieve the best LCP outcomes.
Optimal sub-part times
In order to optimize each sub-part of LCP, it's important to understand what the ideal breakdown of these sub-parts is on a well-optimized page.
Of the four sub-parts, two have the word "delay" in their names. That is a clue that you want to get these times as close to zero as possible. The other two parts involve network requests, which by their very nature take time.
LCP sub-part
% of LCP
Time to first byte (TTFB)
~40%
Resource load delay
<10%
Resource load time
~40%
Element render delay
<10%
TOTAL
100%
Note that these time breakdowns are not strict rules, they're guidelines. If the LCP times on your pages are consistently within 2.5 seconds, then it doesn't really matter what the relative proportions are. But if you're spending a lot of unnecessary time in either of the "delay" portions, then it will be very difficult to constantly hit the 2.5 second target.
A good way to think about the breakdown of LCP time is:
The vast majority of the LCP time should be spent loading the HTML document and LCP source.
Any time before LCP where one of these two resources is not loading is an opportunity to improve.
{% Aside 'warning' %}
Given the 2.5 second target for LCP, it may be tempting to try to convert these percentages into absolute numbers, but that is not recommended. These sub-parts are only meaningful relative to each other, so it's best to always measure them that way.
{% endAside %}
How to optimize each part
Now that you understand how each of the LCP sub-part times should break down on a well-optimized page, you can start optimizing your own pages.
The next four sections will present recommendations and best practices for how to optimize each part. They're presented in order, starting with the optimizations that are likely to have the biggest impact.
1. Eliminate resource load delay
The goal in this step is to ensure the LCP resource starts loading as early as possible. While in theory the earliest a resource could start loading is immediately after TTFB, in practice there is always some delay before browsers actually start loading resources.
A good rule of thumb is that your LCP resource should start loading at the same time as the first resource loaded by that page. Or, to put that another way, if the LCP resource starts loading later than the first resource, then there's opportunity for improvement.
{% Img src="image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8mqXeiEsLQwjgq2lEbjn.png", alt="A network waterfall diagram showing the LCP resource starting after the first resource, showing the opportunity for improvement", width="800", height="375" %}
Generally speaking, there are two factors that affect how quickly an LCP resource can be loading:
When the resource is discovered.
What priority the resource is given.
Optimize when the resource is discovered
To ensure your LCP resource starts loading as early as possible, it's critical that the resource is discoverable in the initial HTML document response by the browser's preload scanner. For example, in the following cases, the browser can discover the LCP resource by scanning the HTML document response:
The LCP element is an
element, and its
src or
srcset attributes are present in the initial HTML markup.
The LCP element requires a CSS background image, but that image is preloaded via
in the HTML markup (or via a
Link header).
The LCP element is a text node that requires a web font to render, and the font is loaded via
in the HTML markup (or via a
Link header).
Here are some examples where the LCP resource cannot be discovered from scanning the HTML document response:
The LCP element is an
that is dynamically added to the page via JavaScript.
The LCP element is lazily loaded with a JavaScript library that hides its
src or
srcset attributes (often as
data-src or
data-srcset).
The LCP element requires a CSS background image.
In each of these cases, the browser needs to run the script or apply the stylesheet—which usually involves waiting for network requests to finish—before it can discover the LCP resource and could start loading it. This is never optimal.
To eliminate unnecessary resource load delay, your LCP resource should always be discoverable from the HTML source. In cases where the resource is only referenced from an external CSS or JavaScript file, then the LCP resource should be preloaded, with a high fetch priority (more on fetch priority in the next section); for example:
{% Aside 'warning' %}
On most pages, ensuring that the LCP resource starts loading at the same time as the first resource is good enough, but be aware that it is possible to construct a page where none of the resources are discovered early and all of them start loading significantly later than TTFB. So while comparing with the first resource is a good way to identify opportunities to improve, it may not be sufficient in some cases, so it's still important to measure this time relative to TTFB and ensure it remains small.
{% endAside %}
Optimize the priority the resource is given
Even if the LCP resource is discoverable from the HTML markup, it still may not start loading as early as the first resource. This can happen if the browser preload scanner's priority heuristics do not recognize that the resource is important, or if it determines that other resources are more important.
For example, you can delay your LCP image via HTML if you set
loading="lazy" on your
element. Using lazy loading means that the resource will not be loaded until after layout confirms the image is in the viewport and so may begin loading later than it otherwise would.
{% Aside 'warning' %}
Never lazy-load your LCP image, as that will always lead to unnecessary resource load delay, and will have a negative impact on LCP.
{% endAside %}
Even without lazy loading, images are not initially loaded with the highest priority by browsers as they are not render-blocking resources. You can hint to the browser as to which resources are most important via the
fetchpriority attribute for resources that could benefit from a higher priority:
It's a good idea to set
fetchpriority="high" on an
element if you think it's likely to be your page's LCP element—but limit this to just one or two images (based on common desktop and mobile viewport sizes), otherwise the signal becomes meaningless. You can also lower the priority of images that may be early in the document response, but isn't visible due to styling, such as images in carousel slides that are not visible at startup:
Deprioritizing certain resources can afford more bandwidth to resources that need it more—but be careful. Always check resource priority in DevTools and test changes with lab and field tools.
After you have optimized your LCP resource priority and discovery time, your network waterfall should look like this (with the LCP resource starting at the same time as the first resource):
{% Img src="image/eqprBhZUGfb8WYnumQ9ljAxRrA72/f9s7SJSBNKcMm3VmcvtT.png", alt="A network waterfall diagram showing the LCP resource now starting at the same time as the first resource", width="800", height="375" %}
{% Aside 'important' %}
Another reason your LCP resource may not start loading as early as possible—even when it's discoverable from the HTML source—is if it's hosted on a different origin, as these requests require the browser to connect to that origin before the resource can start loading. When possible, it's a good idea to host critical resources on the same origin as your HTML document resource because then those resources can save time by reusing the existing connection (more on this point later).
{% endAside %}
2. Eliminate element render delay
The goal in this step is to ensure the LCP element can render immediately after its resource has finished loading, no matter when that happens.
The primary reason the LCP element wouldn't be able to render immediately after its resource finishes loading is if rendering is blocked for some other reason:
Rendering of the entire page is blocked due to stylesheets or synchronous scripts in the