How to improve Largest Contentful Paint (LCP): fix the slow sub-part
Split LCP into four sub-parts, compare each to its budget and fix the slow one: server time, a late or lazy hero image, a heavy file or blocked rendering.
LLaunchScaler·Published ·8 min read
To improve Largest Contentful Paint, split it into its four sub-parts (time to first byte, resource load delay, resource load duration and element render delay) and fix whichever is over its budget. web.dev's guideline is about 40% for the server response, about 40% for downloading the LCP resource, and under 10% each for the two delays, with a Good LCP at 2.5 seconds or less at the 75th percentile.
A lot of LCP advice jumps straight to "compress your images." That only helps when the download is the slow part. On many pages the hero image starts loading late, or finishes and then waits for a script, and a smaller file changes nothing. Measure the sub-parts first, then fix the right one.
What counts as a good LCP?
A Good LCP is 2.5 seconds or less at the 75th percentile of real page loads, measured separately for mobile and desktop. PageSpeed Insights rates 2.5 to 4 seconds as needs improvement and anything over 4 seconds as poor. The 75th percentile means three out of four visits must reach the largest element within 2.5 seconds.
The LCP element is often the hero image, a large heading or a video poster above the fold. PageSpeed Insights and Chrome DevTools both name the element for a given page, and it can differ between mobile and desktop because the layout changes. If LCP is part of a failed Core Web Vitals assessment, what the failed assessment means explains how the verdict is computed from field data.
What are the four sub-parts of LCP?
Every LCP can be split into four consecutive sub-parts that add up to the whole time with no gaps, according to web.dev. Two involve network work and two are delays. The delays should be close to zero; the network parts take time by nature.
Sub-part
What it measures
Guideline share of LCP
If it is over budget
Questions, answered
What people ask about this
01
What is a good Largest Contentful Paint?
2.5 seconds or less at the 75th percentile of real page loads. Between 2.5 and 4 seconds needs improvement, and over 4 seconds is poor, measured on mobile and desktop separately.
From navigation start until the first byte of HTML arrives
About 40%
Fix the server, CDN caching and redirects
Resource load delay
From TTFB until the browser starts loading the LCP image or font
Under 10%
Make the resource discoverable in the HTML and raise its priority
Resource load duration
How long the LCP resource takes to download
About 40%
Shrink the file, serve it closer, cache it
Element render delay
From the resource finishing until the element renders
Under 10%
Remove render-blocking CSS and scripts; render on the server
web.dev adds two cautions. The percentages are "guidelines, not strict rules," and they are "only meaningful relative to each other," so do not convert them into fixed milliseconds. And optimising one sub-part can simply move the time into another: in its example, a smaller image shortened the download but LCP did not improve, because the element was hidden until JavaScript finished.
Find your numbers in Lighthouse's "Largest Contentful Paint element" diagnostic in PageSpeed Insights, which shows the breakdown, or in the Performance panel of Chrome DevTools. For real users, the web-vitals library's attribution build reports the same sub-parts.
How do you cut resource load delay?
Make the LCP resource discoverable in the initial HTML and give it high priority. web.dev's rule of thumb is that "your LCP resource should start loading at the same time as the first resource loaded by that page." If it starts later, the browser either found it late or ranked it low.
The browser finds an image early when it is an <img> whose src or srcset is in the server's HTML. It finds it late when:
JavaScript adds the <img> after load, as in a client-rendered hero.
A lazy-loading library hides the URL in data-src or data-srcset.
The hero is a CSS background-image, which the browser only discovers after downloading and applying the stylesheet.
Then fix the priority:
Remove loading="lazy" from the LCP image. web.dev is blunt: "Never lazy-load your LCP image, as that will always lead to unnecessary resource load delay."
Add fetchpriority="high" to the LCP <img>. Images are not loaded at the highest priority by default "as they are not render-blocking resources."
Set high priority on one or two images at most. web.dev warns that "setting a high priority on more than one or two images makes priority setting unhelpful."
If the image is only referenced from CSS or JavaScript, preload it with high priority from the HTML.
<!-- Hero in the HTML: eager (the default) and high priority -->
<img src="/img/hero-1200.avif" width="1200" height="600" fetchpriority="high"
alt="Invoice editor with a paid invoice">
<!-- Hero referenced only from CSS: preload it so it starts with the stylesheet -->
<link rel="preload" as="image" href="/img/hero-1200.avif" type="image/avif" fetchpriority="high">
Lazy loading the hero is common because many frameworks and CMSs lazy-load by default. web.dev's analysis found that pages using browser lazy loading tended to have worse LCP, and in a lab A/B test on WordPress archive pages, disabling lazy loading improved LCP by 13% on desktop and 15% on mobile. Keep loading="lazy" for images below the fold only.
Host the LCP image on the same origin as the page when you can. web.dev notes that a resource on another origin needs a new connection first, which delays its start even when it is in the HTML.
How do you make next/image load the hero first?
Override the default. The Next.js Image component "defaults to lazy," so a hero rendered with <Image> and no other props is lazy-loaded. For the LCP image, set loading="eager" or fetchPriority="high", or use preload, which in Next.js 16 replaced the deprecated priority prop.
import Image from "next/image";
export function Hero() {
return (
<Image
src="/img/hero.png"
width={1200}
height={600}
fetchPriority="high"
loading="eager"
alt="Invoice editor with a paid invoice"
/>
);
}
The Next.js docs say preload inserts a <link> in the <head> and suit it to one above-the-fold hero, but advise that "in most cases, you should use loading="eager" or fetchPriority="high" instead of preload." Do not use preload when several images could be the LCP element depending on viewport size.
How do you cut resource load duration?
Send fewer bytes, from closer, without competition. web.dev lists four ways: reduce the size of the resource, reduce the distance it travels, reduce contention for bandwidth, and eliminate the network entirely with caching. It also notes that load duration "tends not to be a significant bottleneck for most sites," so check the sub-parts before starting here.
Concrete steps for an image LCP:
Serve the image at the size it is displayed. A 2,400 px wide original shown at 1,200 px sends roughly four times the pixels it needs. Use srcset and sizes so phones get a smaller file.
Use a modern format such as AVIF or WebP wherever it gives a smaller file at the quality you need; WebP vs AVIF compares the two and covers fallbacks with <picture>.
Serve it from a CDN, ideally on your own origin. web.dev notes image CDNs cut both distance and size, but a third-party domain "comes with an additional connection cost."
Cache it. With a long Cache-Control lifetime on versioned image URLs, repeat visitors load it from cache and the download time drops to near zero.
Load fewer other things at the same time. Many high-priority requests compete with the LCP image for bandwidth.
If the LCP element is text, the resource is a web font. Set font-display to something other than auto or block so text shows in a fallback font and "LCP won't be blocked on an additional network request."
How do you cut element render delay?
Make sure nothing stops the element from painting once its resource arrives. web.dev lists the usual blockers: stylesheets or synchronous scripts in the <head> still loading, the element not yet in the DOM because JavaScript has not run, the element hidden by an A/B testing script, or a main thread busy with long tasks.
Fixes, in the order they usually pay off:
Remove synchronous scripts from the <head>. web.dev says "it is almost never necessary" to add scripts without async or defer there.
Keep the render-blocking stylesheet smaller than the LCP image, by removing unused CSS and deferring non-critical styles, or inline it if it is small.
Render the LCP element on the server. Server-side rendering or static generation puts the image in the HTML, which fixes discovery and removes the wait for JavaScript. web.dev notes static generation is "generally a better choice for performance" where it fits.
Remove client-side A/B tests that hide the page until they decide, or run the experiment on the server.
Break up long JavaScript tasks, because "all browsers today render images on the main thread."
While you are in the markup, give the hero image width and height attributes. They do not speed LCP up, but without them the image pushes content down when it loads, which is a layout shift; fixing Cumulative Layout Shift covers why.
What if TTFB is the slow sub-part?
Then nothing on the page can fix LCP, because every other sub-part starts after the first byte. web.dev says a high TTFB "can make achieving a 2.5 second LCP challenging, or even impossible," and names multiple redirects, distant servers and uncacheable URLs (for example, analytics query parameters that bypass the CDN) as common causes.
PageSpeed Insights rates TTFB Good at 800 ms or less and poor above 1,800 ms. If your TTFB takes most of the LCP budget, work on the server, the CDN and caching first; reducing Time to First Byte walks through each stage of the request.
Find which LCP sub-part is slow
To see where your page's LCP time goes, run the free scan on LaunchScaler with its URL, no account needed. Its speed checks read LCP from real Chrome users at the 75th percentile, break the time into its sub-parts to flag a load delay that dominates, and catch the common causes: an LCP image with loading="lazy", render-blocking resources in the <head>, and heavy images that are not in AVIF or WebP.
Break LCP into its four sub-parts (time to first byte, resource load delay, resource load duration and element render delay) and fix the one over budget. The usual fixes are a faster server response, making the hero image discoverable in the HTML with fetchpriority high, a smaller image, and removing render-blocking CSS and scripts.
03
Should I lazy load the LCP image?
No. web.dev says never lazy-load your LCP image, because it always adds resource load delay. In a lab test on WordPress archive pages, disabling lazy loading improved LCP by 13% on desktop and 15% on mobile.
04
Does next/image lazy load by default?
Yes. The Next.js Image component defaults to loading lazy. For the hero image, set loading eager or fetchPriority high, or use the preload prop, which replaced priority in Next.js 16.
05
Will converting images to WebP or AVIF fix LCP?
Only if resource load duration is the slow sub-part. web.dev notes that a smaller image can just move the time into element render delay if something else holds the render back, and that load duration tends not to be the main bottleneck for most sites.
Run the URL through LinkedIn's Post Inspector, then fix what it shows: missing og tags, an image under 1200 x 627 px, a blocked LinkedInBot or a redirect.
New Lovable apps render on the server; older ones are pre-rendered for verified crawlers. Find your stack, then fix domain, sitemap, robots and titles.