Optimizing Largest Contentful Paint (LCP)

Redaktion ·

Largest Contentful Paint, or LCP, is one of the three Core Web Vitals and answers a surprisingly simple question: when does the user see the largest visible element above the fold? On most pages that’s a hero image, a big headline, or a video poster. LCP doesn’t measure when the page is finished — it measures when the main content is there. It’s the moment a human thinks, “Okay, the page is loading.”

Google uses the 75th percentile of real page loads for its threshold, split between mobile and desktop. The grading (per web.dev, June 2026): good at ≤ 2.5 seconds, needs improvement up to 4.0 seconds, poor beyond that. The “in the field” part matters — measurement happens on real users (CrUX field data), not on the fiber connection in your office.

What counts as the LCP element

Not every pixel is a candidate. LCP considers <img>, <image> inside SVGs, <video> (poster or first frame), elements with a CSS background image via url(), and block-level elements containing text. Heuristically excluded are elements with opacity 0, full-viewport backgrounds, placeholder images, and very low-contrast “low-entropy” images — the last rule keeps a grey spinner placeholder from counting as the LCP.

Which of these ends up being “the largest” is decided dynamically by the browser as it loads. It can shift: first the headline is the largest visible element, then the hero image loads in and takes over. That’s exactly why the most important groundwork is to actually know which element is the LCP element on your page — measured, not guessed. Lighthouse and Chrome DevTools flag it directly.

The most common mistake: it’s rarely the image itself

The intuitive reaction to a bad LCP score is “the hero image is too big, let me compress it.” Sometimes that’s right. Often it isn’t. LCP breaks down into four time phases that follow each other seamlessly — and web.dev’s recommendation distributes the budget like this:

  • Time to First Byte (TTFB) — ~40%: the time until the server even sends the first byte of HTML.
  • Resource Load Delay — < 10%: the gap between TTFB and the moment the browser starts loading the LCP image.
  • Resource Load Duration — ~40%: the actual download of the image.
  • Element Render Delay — < 10%: the time from finished download to display.

Compressing the image only affects “Resource Load Duration.” If your real problem is a slow TTFB or a high “load delay” because the browser discovers the image late, the smaller image barely helps. You’d be optimizing the wrong phase. So the rule is: look at the phase breakdown of your real measurement first, then pick the matching lever.

TTFB: the foundation everything waits on

Until the server has sent the first byte, the browser can discover nothing, load nothing, render nothing. A TTFB of 1.5 seconds eats more than half your good LCP budget before a single pixel is painted. Typical causes: uncached server-side rendering, slow database queries, a missing CDN, or redirect chains that each cost their own round-trip.

The most effective levers here: serve static HTML or aggressive server caching, put a CDN in front of the origin, eliminate redirects, and avoid random query parameters that defeat CDN caching. TTFB isn’t a side detail of SEO — it’s the phase that pushes every other phase back.

Render-blocking resources: the invisible traffic jam

Even with a fast TTFB, the LCP image can sit in a jam. Synchronously loaded CSS and JavaScript in the <head> block rendering: the browser must parse and execute the CSS before it shows anything at all. A heavy external stylesheet or a render-blocking script therefore delays not just the LCP element but the entire first paint. How this mechanism works in detail is covered in the article on render-blocking resources.

The standard levers: inline the critical CSS into the <head> (only what’s needed above the fold) and load the rest asynchronously. Remove render-blocking JavaScript from the <head> or defuse it with defer/async. And break up long JavaScript tasks that block the main thread and delay rendering.

The three image levers that almost always work

When the LCP element is an image, there are three high-impact interventions — and they’re often a matter of minutes:

1. fetchpriority="high" on the LCP image. By default the browser gives images a low priority, because during the first HTML parse it doesn’t know which image matters. With fetchpriority="high" you tell it explicitly: this image first. This mainly cuts the “Resource Load Delay.”

2. Never loading="lazy" on the LCP image. Lazy-loading is a great idea — for images below the fold. Applied to the LCP element it’s an own goal: the browser deliberately waits until the element is almost in the viewport before loading. Exactly the opposite of what you want. A single wrong loading="lazy" on the hero image can worsen the LCP value by seconds.

3. Modern image formats and correct dimensions. AVIF or WebP instead of JPEG/PNG shrinks the download dramatically (that hits “Resource Load Duration”). Responsive srcset sizes keep mobile users from downloading a 2000px desktop image.

If the LCP image appears late in the DOM or is loaded as a CSS background (and is therefore invisible during the HTML parse), a <link rel="preload" fetchpriority="high"> in the <head> helps additionally — so the browser discovers the image before it has evaluated the CSS.

Lab vs. field: why your Lighthouse 90 lies

Lighthouse and PageSpeed Insights deliver two kinds of data, and they’re constantly confused. Lab data is a simulated single measurement under standardized conditions — reproducible, good for debugging, but not what Google uses for ranking. Field data (CrUX) is aggregated real user experience from the last 28 days — that’s the basis of the 75th-percentile threshold.

A Lighthouse LCP of 1.8s in the lab and a field LCP of 3.4s aren’t a contradiction: your real users sit on slower devices and networks than the simulated test. Optimize against the field data, debug with the lab data. And give a change 28 days to fully show up in the CrUX window.

LCP is just one of the three Core Web Vitals — INP (interactivity) and CLS (layout stability) belong to it. How LCP fits into the larger arc of crawling, rendering, and indexing is in the hub article Technical SEO.

FAQ

What is a good LCP value? ≤ 2.5 seconds is considered good, measured at the 75th percentile of real page loads, split by mobile and desktop (per web.dev, June 2026). Values up to 4.0s need improvement, beyond that is poor. What matters is the field measurement on real users, not the lab value in a test.

My LCP image is small, yet the LCP is bad — why? Because the image itself usually isn’t the problem. LCP consists roughly 80% of TTFB and the actual download, plus two delay phases. A slow server (TTFB) or render-blocking CSS/JS that postpones loading the image drags the value down just as much — regardless of image size. Look at the phase breakdown of your measurement.

What does fetchpriority="high" do? It tells the browser explicitly that this image takes precedence. Without the hint the browser initially gives images a low priority because it doesn’t know which one matters. Set on the LCP image, the download starts earlier and the “Resource Load Delay” drops. A small intervention with often measurable effect.

Can I use lazy-loading on the hero image? No. loading="lazy" is meant for images below the visible area. On the LCP element it deliberately delays the load start and often worsens the LCP value by seconds. Lazy-loading belongs on everything below the fold, never on the largest visible element.

What’s the difference between lab and field data? Lab data is a simulated single measurement (Lighthouse) under standard conditions — good for debugging. Field data (CrUX) is aggregated real user experience from the last 28 days and the basis for Google’s grading. Optimize against the field data, debug with the lab data, and plan for 28 days until changes are fully visible.