Where LCP Time Actually Goes
Largest Contentful Paint (LCP) measures the time from when a user initiates page load until the largest image or text block renders in the viewport. The target is 2.5 seconds or less for at least 75% of visits. But LCP isn't a single bottleneck — it's the sum of a chain of events, and shaving time from one link without addressing the others often moves the delay somewhere else instead of removing it.
Every page load can be broken into four non-overlapping LCP subparts that together account for the full LCP time:
- Time to First Byte (TTFB): from navigation start until the first byte of the HTML document arrives.
- Resource load delay: from TTFB until the browser begins loading the LCP resource. Zero if the LCP element is text that needs no resource fetch.
- Resource load duration: the time to transfer the LCP resource itself. Zero for text-only LCP elements.
- Element render delay: from the moment the LCP resource finishes loading until the LCP element is fully painted.
Two of these subparts carry "delay" in their names for a reason: they are the times when no LCP-relevant network work is happening, and they should be driven as close to zero as possible. The two load phases genuinely take time because they involve network transfers. A well-optimized page spends the vast majority of its LCP budget loading the HTML and the LCP resource. Any idle time before LCP where neither of those two resources is loading is an opportunity for improvement.
Identify the Real-User Problem First
Before optimizing, determine whether an LCP problem actually exists for real users. Lab tools such as Lighthouse are useful for diagnosis, but they don't represent what most visitors experience. Field data from Real User Monitoring (RUM) or the Chrome User Experience Report (CrUX) reflects actual conditions.
Chrome DevTools' Performance panel can show local LCP results alongside origin-level CrUX data in the live metrics view, and performance trace insights include a breakdown of LCP subpart timings. PageSpeed Insights surfaces CrUX in its "Discover what your real users are experiencing" section, with Lighthouse lab data below in "Diagnose performance issues." When both are available, prioritize the real-user data.
PageSpeed Insights presents up to four CrUX views: mobile and desktop for a specific URL, and mobile and desktop for the whole origin. If a URL lacks sufficient traffic for standalone data, origin-level data displays instead. Origin-wide LCP can differ substantially from a single page's LCP because of navigation patterns — home pages are often loaded cold by new visitors with no cache, making them the slowest pages on many sites.
Compare the two daily FCP and TTFB measurements to understand what drives a poor LCP:
- A large TTFB-to-FCP gap suggests heavy render-blocking resources or heavy client-side rendering before any meaningful content can display.
- A large FCP-to-LCP gap means the LCP resource isn't available early in the HTML — it's injected by JavaScript or the browser is busy with other work before it can paint the LCP element.
High TTFB itself can make the 2.5-second target hard or impossible to reach. Common causes include multiple redirects, visitors geographically distant from the closest server, poor network conditions, or cache misses caused by query parameters.
If the Lighthouse section of PageSpeed Insights shows an LCP that roughly agrees with CrUX, its audits can guide fixes. Use the LCP filter to isolate relevant opportunities, and check the "Largest Contentful Paint element" diagnostic, which breaks down the metric's constituent timings. CrUX has also begun exposing LCP resource types and subpart data.
Two Requests Matter Most
Pages issue many network requests, but for LCP optimization only two matter: the initial HTML document and the LCP resource. Identify the LCP element (using DevTools, PageSpeed Insights, or WebPageTest), find its URL on the network waterfall, and examine the timeline between the HTML response and that resource's load.
A classic failure mode appears when optimizing only the resource load duration. Suppose the LCP image is hidden until JavaScript finishes and reveals everything at once. Compressing the image or switching to a more efficient format shortens the resource load duration, yet LCP stays the same — the saved time shifts into element render delay while the page waits for JavaScript.
That is why optimizing one subpart in isolation rarely improves LCP meaningfully. The entire loading sequence must be addressed for real gains.
Start with resource load delay
Your first optimization target should be making sure the LCP resource begins downloading as early as possible. A practical benchmark: the LCP resource should start loading at the same time as the first resource on the page. If it starts later, there is room for improvement.
Two factors govern how quickly the LCP resource starts loading:
- Discovery time: when the browser learns the resource exists.
- Priority: how important the browser considers the resource relative to others.
Make the resource discoverable in the HTML
The LCP resource should be discoverable by the browser's preload scanner directly from the initial HTML response. This works naturally when the LCP element is an <img> with its src or srcset attribute in the markup. For CSS background images or web fonts, you need an explicit <link rel="preload"> (or a Link header) in the HTML.
Common cases where the resource is not discoverable from the HTML:
- An
<img>added dynamically via JavaScript. - Lazy-loading libraries that obscure the real
srcorsrcsetbehind attributes likedata-src. - CSS background images, which are only referenced from a stylesheet.
In those situations the browser must fetch and execute a script or apply a stylesheet before it even knows the LCP image exists. If the resource is only referenced from an external CSS or JavaScript file, preload it with a high fetch priority:
<!-- Load the stylesheet that will reference the LCP image. -->
<link rel="stylesheet" href="https://web.dev/path/to/styles.css">
<!-- Preload the LCP image with a high fetchpriority so it starts loading with the stylesheet. -->
<link rel="preload" fetchpriority="high" as="image" href="https://web.dev/path/to/hero-image.webp" type="image/webp">
Set the right priority
Even when the LCP resource is in the HTML markup, the browser may not load it first. The preload scanner's heuristics may not flag it as critical, or lazy loading can defer it until after layout. Images are not render-blocking by default, so they typically start with a lower priority than stylesheets or scripts.
Use the fetchpriority attribute to signal which resources matter most:
<img fetchpriority="high" src="https://web.dev/path/to/hero-image.webp">
Set fetchpriority="high" only on the image likely to be your LCP element. Applying it to more than one or two images dilutes the signal and won't help LCP. You can also deprioritize images that appear early in the document but are not visible at startup, such as images in off-screen carousel slides:
<img fetchpriority="low" src="https://web.dev/path/to/carousel-slide-3.webp">
Deprioritizing low-value resources frees bandwidth for the LCP resource. Verify the effect in DevTools' network waterfall and validate with both lab and field measurements. After tuning discovery and priority, the waterfall should show the LCP request starting at the same time as the first resource:
Eliminate element render delay
The LCP element must be able to render immediately after its resource finishes loading. Render delay usually comes from one of these blockers:
- Render-blocking stylesheets or synchronous scripts in the
<head>that are still downloading. - The LCP element is not yet in the DOM because it depends on a JavaScript file that has not finished loading.
- Scripts (such as A/B testing libraries) that are still hiding the element while they determine which variant to show.
- Long tasks on the main thread that defer all rendering work until they complete.
Shrink or inline render-blocking CSS
A large stylesheet that loads slower than the LCP image will block the LCP element's render, even after the image bytes arrive:
Two fixes are possible: inline the stylesheet into the HTML, or reduce its size. Inlining only makes sense for small stylesheets, since inlined CSS cannot be cached across page views. If the stylesheet takes longer to transfer than the LCP resource, inlining is the wrong answer.
The practical approach is usually to make the stylesheet smaller than the LCP resource. Concrete steps:
- Remove unused CSS rules detected in Chrome DevTools.
- Defer non-critical CSS by splitting out styles needed for initial render and loading the rest lazily.
- Minify and compress critical CSS to minimize transfer size.
Avoid render-blocking JavaScript
Synchronous scripts in the <head> almost never belong on a page. They block rendering of everything after them while the script downloads. If a script must run extremely early, inline it — but only if it is very small.
Don't load critical scripts as external synchronous resources:
<head> <script src="https://web.dev/path/to/main.js"></script> </head>
Inline them instead:
<head>
<script>
// Inline script contents directly in the HTML.
// IMPORTANT: only do this for very small scripts.
</script>
</head>
Render content on the server
Server-side rendering (SSR) executes client-side application logic on the server and returns the complete HTML markup. This gives two main LCP benefits: image resources are discoverable in the HTML source, and page content does not wait on additional JavaScript requests. The trade-off is higher server processing time, which raises TTFB — but server time is more controllable than users' network and device conditions.
Static site generation (SSG), sometimes called prerendering, generates HTML at build time instead of on demand. If your architecture supports it, prerendering is generally the better performance choice.
Break up long tasks
Even non-render-blocking JavaScript can delay LCP. Large scripts must be parsed and executed on the main thread, and since browsers render images on the main thread, the LCP image cannot paint until unrelated script execution finishes. Long tasks caused by script evaluation are a common source of avoidable element render delay.
Shorten resource load duration
This step targets the time spent transferring the LCP resource bytes. Four levers are available:
- Reduce resource size
- Reduce network distance
- Reduce bandwidth contention
- Eliminate the network request
Serve smaller resources
The LCP resource is either an image or a web font. For images, serve appropriately sized versions for the displayed dimensions, use modern formats, and compress aggressively. For web fonts, reduce the number of glyphs and subsets, use variable fonts where possible, and remove unnecessary weights and styles.
Move content closer to users
A content delivery network (CDN) places copies of your resources at edge servers near your users, cutting round-trip latency. Image CDNs go further by automatically applying size optimizations at request time.
Prevent bandwidth starvation
Network contention occurs when many resources compete for the same connection. A high fetchpriority on the LCP resource tells the browser to prioritize its bytes. But if too many resources carry a high priority, or the page simply loads too many assets, contention can still slow the LCP resource transfer.
Cache or inline to remove the request
The fastest request is one that never happens. An efficient cache-control policy serves repeat visitors from the local cache, making resource load duration nearly zero. If the LCP resource is a web font, set a font-display value other than auto or block so text renders during font load and LCP does not wait for the font request.
For very small resources, inlining as a data URL removes the network request entirely. This comes with caveats: data URLs cannot be cached independently, and the extra decode cost can increase element render delay.
Optimize time to first byte last
TTFB affects every downstream step, but it is often the area where frontend developers have the least direct control. Still, two common causes are fixable. Multiple redirects — from ads or shortened links — should be minimized. And if analytics tracking appends unique URL parameters, CDN caches may be bypassed, forcing every request to the origin server. Avoid unique parameters on the main document URL unless they actually change the response.
For deeper TTFB work, refer to dedicated guidance on optimizing server response times.
Reading LCP timings from the browser
Every subpart of the LCP timeline can be inspected in JavaScript. The relevant timestamps are exposed through the Largest Contentful Paint API, alongside the Navigation Timing API and the Resource Timing API. Most commercial RUM vendors already rely on these interfaces to break down LCP for their dashboards.
If you want to derive these subparts yourself, the web-vitals library ships an attribution build that includes all of the LCP component timings. Its source is a practical reference for computing the breakdown in your own code.
For development work, you do not have to wire this up manually. Chrome DevTools and Lighthouse surface the same subpart measurements in their performance panels, as shown in the traces earlier in this article.
Bringing it together
LCP is affected by many variables, and its timing can be difficult to reason about. A simpler mental model is to treat LCP optimization as the problem of loading a single key resource as efficiently as possible.
That approach reduces to four concrete actions:
- Kick off the request for the LCP resource as early as possible.
- Make sure the LCP element can paint immediately once the resource arrives.
- Shrink the resource's transfer and decode time without hurting quality.
- Serve the initial HTML document itself with minimal delay.
Pages that follow these steps will typically show strong real-user LCP measurements, since they remove the most common sources of avoidable delay from the critical path.



