Largest Contentful Paint (LCP) measures the render time of the largest element visible in the viewport during page load, expressed in seconds from navigation start. It is one of the Core Web Vitals. A good LCP is 2.5 seconds or less at the 75th percentile of real page loads, with 4.0 seconds marking the poor threshold.

Key Takeaways

  • LCP is the render time of the single largest in-viewport element, not a score and not an average of all content.
  • Thresholds are 2.5s good and 4.0s poor, measured at the 75th percentile of real user page loads, not the mean.
  • Four sub-parts decompose LCP so you can see whether server, network, or rendering is the bottleneck.
  • Lab data from Lighthouse and field data from CrUX disagree often because they sample different users and conditions.
  • Fixes map directly to sub-parts: preconnect, fetchpriority, preload, drop lazy-loading on the hero, trim render-blocking CSS.
  • LCP on paid landing pages moves conversion rate, so it is a media efficiency problem, not only an SEO ranking problem.

What Does LCP Measure?

LCP reports the time, in seconds, from the moment the user initiates navigation until the largest element in the viewport finishes rendering. The browser watches the page as it loads and, once it is confident which element is the largest, records when that element became visible. The metric is a single timestamp, not a blended score across the whole page.

An important nuance is that LCP changes as the page builds. Early in load a small hero text block might be the largest element, then a full-width image paints and overtakes it. The browser keeps updating LCP until the user interacts or load settles, then locks the final value. This is why optimizing a secondary element can backfire: the real LCP candidate is whatever ends up largest in the viewport.

Which Elements Count Toward LCP?

Only a defined element types can be the LCP element. If your largest block is outside this set, the browser picks the largest qualifying element instead:

  • Images, including <img> elements and the poster image of a <video>.
  • An image rendered inside an inline SVG, such as an <image> element.
  • The first frame of a video, but only its poster image qualifies, not the playing video.
  • An element whose background is painted from a background-image: url() CSS rule.
  • Block-level text nodes, meaning a paragraph, heading, or list rendered as a text block.

Note that background images count only via url(), and only the largest qualifying block wins. A giant decorative SVG icon made of paths will not qualify, so the LCP element may be a nearby heading instead, which changes your optimization target.

What Are the LCP Thresholds?

Google defines three bands for LCP, applied at the 75th percentile of page loads:

BandLCP (75th percentile)Meaning
Good2.5 seconds or lessFast enough that most users perceive the main content as present.
Needs improvementBetween 2.5s and 4.0sNoticeable delay that erodes perceived responsiveness.
PoorMore than 4.0 secondsSlow enough to hurt engagement and ranking signals.

These two numbers, 2.5 seconds and 4.0 seconds, are the stable definitions. A page is "good" only when at least 75 percent of real loads finish the LCP element inside 2.5 seconds; a 2.6-second 75th percentile still lands in the needs-improvement band, with no partial credit.

Why Is LCP Measured at the 75th Percentile?

The 75th percentile, not the average, is used so one or two catastrophically slow loads do not drag the metric down, and so a fleet of fast loads cannot mask a large slow cohort. The average would hide both problems: a few users on slow networks would pull the mean up, while a median-only view would ignore the worst third entirely.

Web performance is distributed, not uniform. Real users arrive on throttled phones, congested networks, and cold caches. The 75th percentile captures a user slower than the median but still in the realistic mainstream, the cohort most likely to bounce, so optimizing to the average quietly leaves that cohort behind.

What Is the Difference Between Field Data and Lab Data?

Field data from the Chrome UX Report (CrUX) comes from real Chrome users on real devices and networks, aggregated over 28 days. Lab data from Lighthouse is a synthetic run in a controlled environment with a fixed device and throttled connection. They measure the same metric but answer different questions:

DimensionField data (CrUX)Lab data (Lighthouse)
SourceReal users, real devices and networksSynthetic run on a simulated device
VariabilityReflects your actual traffic spreadSingle, repeatable, throttled run
What it affectsSearch ranking and the public CrUX reportLocal debugging and CI gates
Common failureBad even when lab looks cleanClean even when field is bad

They disagree because your real users are not the simulated mid-tier phone on a capped connection. A page that passes Lighthouse on a fast machine can still fail CrUX because a share of visitors hit slow networks, third-party scripts, or warm-but-not-hot caches. Treat Lighthouse as a diagnosis tool, not the score Google uses for ranking.

What Are the Four LCP Sub-Parts?

Every LCP time decomposes into four sequential phases. The largest phase is where to spend effort:

Sub-partWhat it isTypical causeThe fix
Time to first byte (TTFB)Gap from navigation start to the first byte of HTMLSlow server, no edge cache, heavy origin workCDN edge caching, faster origin, trim upstream work
Resource load delayTTFB to when the LCP resource starts loadingRender-blocking CSS, late discovery of the imagePreconnect, preload, inline critical CSS
Resource load durationTime to download the LCP resource itselfLarge unsized image, slow origin, no CDNModern format, correct sizing, fetchpriority high
Element render delayResource ready to when it actually paintsMain-thread blocked by JS, layout thrash, lazy loadDefer JS, drop lazy-loading, avoid FOIT

To read which one dominates, open the performance trace and look at the LCP phase breakdown. If TTFB is the biggest slice, no image tweak helps until the server responds faster. If element render delay dominates, the network was fine but your JavaScript blocked the main thread past the point the image could paint.

What Is the LCP Diagnosis Workflow?

Follow this ordered sequence to find the real bottleneck before changing anything:

  1. Pull field LCP from CrUX or Search Console to confirm the page is actually poor at the 75th percentile, not just in lab.
  2. Open a Lighthouse or WebPageTest trace and read the LCP sub-part breakdown to see which phase is largest.
  3. Identify the LCP element in the trace and confirm it is the hero image or heading you expect, not a surprise block.
  4. Compare the resource's start time against TTFB to decide if the delay is server, discovery, download, or render.
  5. Reproduce the worst case on a throttled profile so you are debugging the slow cohort, not your own fast connection.
  6. Ship one change mapped to the dominant sub-part, then re-measure field and lab before touching the next phase.

How Do You Fix Server Response Time and Caching?

If TTFB is the dominant sub-part, the HTML itself is slow to arrive. Move the document behind a CDN edge cache so warm-cache visitors never reach origin. For personalized pages that cannot be fully cached, cache the shell and stream dynamic parts, or move slow work off the request path. Resolve the canonical URL at the edge so a user on the wrong scheme or host reaches the final document in one hop; a 300-millisecond TTFB improvement flows directly into LCP.

How Do Preconnect, Preload, and Fetchpriority Help LCP?

Preconnect opens an early connection to the host that serves the LCP resource, such as your image CDN, so the TCP and TLS handshake finishes before the browser discovers it needs that host. Preload, via a rel="preload" link, fetches the specific LCP image or font immediately rather than waiting for the parser. Preconnect saves connection setup; preload also saves discovery.

Setting fetchpriority="high" on the hero image tells the browser to prioritize its download over lower-value assets on the same page, shortening the resource load duration sub-part. Priority is a zero-sum signal, so set it only on the confirmed LCP candidate and never on below-the-fold or lazy-loaded images. Apply preload and fetchpriority only to the true LCP resource, because preloading the wrong asset wastes bandwidth and can hurt other metrics.

Why Should You Avoid Lazy-Loading the LCP Element?

Lazy-loading defers an image until it is near the viewport, which is exactly wrong for the hero that is already in the viewport on load. When the LCP image is lazy-loaded, the browser discovers and fetches it late, pushing the resource load delay and duration sub-parts outward and inflating LCP. The safe rule is to never lazy-load anything in the initial viewport. Many frameworks apply lazy-loading by default to all images, which quietly punishes the one image that matters most. Audit the rendered HTML and remove loading="lazy" from the hero so it loads with the document, not after it.

How Do Render-Blocking CSS, JS, and Fonts Hurt LCP?

Render-blocking CSS stops the browser from painting until the stylesheet downloads and parses, which delays when the LCP element can appear. Inline the critical CSS for the above-the-fold hero and load the rest asynchronously so first paint is not gated on a large secondary stylesheet. Render-blocking JavaScript is worse for element render delay: a long synchronous script prevents the browser from laying out and painting the LCP element even after its bytes arrive. Defer or split non-critical scripts and keep the main thread clear during the first second of load.

Fonts add a third trap. When the LCP element is text, font loading can cause FOIT, the flash of invisible text where the browser reserves space but paints nothing until the web font arrives, so LCP does not complete until the font loads. Use font-display: swap so text paints in a fallback face immediately, preload the above-the-fold font file, and subset it so the download is small. Watch for tag managers and A/B testing scripts that inject main-thread work exactly when LCP is trying to settle.

How Does LCP Behave in Spas and Client-Rendered Heroes?

On the first load, a single-page app measures LCP the same way as any page, but a client-side rendered app ships a near-empty shell and injects the hero by JavaScript after the framework boots. That pushes the entire LCP into element render delay, because the largest element cannot paint until the bundle downloads, executes, fetches data, and renders, which on slow devices can blow past 4.0 seconds easily.

The fix is to server-render or statically generate the hero markup so the LCP element is present in the first HTML document, then hydrate around it rather than rendering from scratch in the browser. On soft navigations, where the router swaps content without a full document load, there is no navigation start, so Google introduced LCP for soft navigations. Treat each route change as its own LCP budget: preload route-level data and render the new hero synchronously so the largest element paints without waiting on a waterfall of client requests.

Why Does LCP Matter for Paid Media Landing Pages?

Paid landing pages live or die on conversion rate, and LCP is a direct input to that rate, independent of SEO. A hero that takes four seconds to appear loses the visitor before the value proposition registers, which raises cost per acquisition because the same ad spend yields fewer conversions. This reframes LCP as a media efficiency metric: cutting LCP from 4.0 seconds to 2.0 seconds recovers the fraction of paid clicks that bounced during the blank period, so the ROI case is often stronger on a high-spend landing page than on an organic post.

What Are the Most Common LCP Mistakes?

The errors we see most often, each wasting optimization effort spent elsewhere:

  • Optimizing the wrong element because you assumed the hero image was LCP when a late-loading text block or background actually won.
  • Chasing a green Lighthouse score while field CrUX data stays poor, because lab and field rarely match for real traffic.
  • Leaving lazy-loading on the in-viewport hero, which delays the exact resource that defines the metric.
  • Using a hero carousel or autoplaying hero video, where the LCP candidate keeps changing or never settles to a fast value.
  • Blocking the main thread with tag-manager and A/B scripts during first paint, inflating element render delay.

Carousels are especially toxic: the LCP element can shift as slides rotate, so the browser may report a later, larger slide and the user never sees a stable fast paint. A static, single hero image is almost always the safer LCP candidate.

Frequently Asked Questions

What Is the LCP Threshold for a Good Score?

A good LCP is 2.5 seconds or less at the 75th percentile of real user page loads. Between 2.5 and 4.0 seconds is the needs-improvement band, and above 4.0 seconds is poor. The page must hit 2.5 seconds for at least 75 percent of loads; the average does not count. The 2.5s and 4.0s thresholds are the stable definitions used for ranking and the public CrUX report.

Which Elements Can Be the LCP Element?

The qualifying elements are images, the poster image of a video, an image inside an inline SVG, an element painted from a CSS background-image via url, and block-level text nodes such as headings or paragraphs. Decorative SVG paths, non-image video frames, and empty containers do not qualify, so the browser picks the largest qualifying block. If your biggest visual is non-qualifying, the LCP element is the next largest valid one, which may surprise you.

Why Do Lighthouse and Crux Disagree on LCP?

Lighthouse is a single synthetic run on a simulated mid-tier device with a throttled connection, while CrUX aggregates real Chrome users across the actual spread of devices and networks over 28 days. Your real visitors include slow phones and congested networks the lab never reproduces. A page can pass Lighthouse yet fail field data, which is why Google uses field data for ranking and treats Lighthouse as a debugging tool.

How Do I Find Which LCP Sub-Part Is Slow?

Open a performance trace in Lighthouse or WebPageTest and read the LCP phase breakdown, which separates time to first byte, resource load delay, resource load duration, and element render delay. The largest phase is your bottleneck. If TTFB dominates, fix the server; if element render delay dominates, the network was fine but JavaScript blocked the main thread. Confirm the identified LCP element is the one you expected before changing anything.

Does Lazy-Loading Hurt LCP?

It hurts only when applied to the LCP element itself, which is almost always the in-viewport hero. Lazy-loading defers that image until the browser decides it is near the viewport, delaying both discovery and download of the exact resource that defines the metric. The fix is to remove loading="lazy" from the hero and any above-the-fold image, while keeping lazy-loading on below-the-fold images where it helps total page weight.