Largest Contentful Paint (LCP): How to Improve It

Largest Contentful Paint (LCP): How to Improve It

Almost every guide on largest contentful paint gives the same advice: compress your images. It’s not wrong, but it’s why so many teams spend a week squeezing PNGs and watch their score barely move. LCP isn’t a single number you push down with one lever — it’s the sum of four sequential phases, and on most sites the slow phase isn’t the image at all. It’s the server, a render-blocking stylesheet, or a script that hides the image from the browser until it’s too late. This guide shows you how to find the phase that’s actually costing you and fix that one.

What Largest Contentful Paint Actually Measures

Largest Contentful Paint measures the time from when a user starts loading the page to when the single largest content element in the viewport finishes rendering. That element is whatever paints the most pixels above the fold — usually a hero image, a video poster frame, a CSS background image, or a big block of heading text. It is a proxy for one specific human experience: “how long until the page looks basically loaded?” Google made it a Core Web Vital because it correlates with perceived speed far better than old metrics like onload, which fires long after the user has already decided the page feels slow.

The word that matters is largest. The browser doesn’t care about your logo or nav bar; it watches for the biggest element rendered in the viewport and clocks when it appears. The candidate can change as the page loads — a text block might be largest at 800ms, then a hero image takes the title at 2.1s. The final LCP is the last candidate before the user starts interacting.

The 2.5-Second Threshold Your Laptop Is Hiding From You

The thresholds are fixed and worth memorizing: an LCP of 2.5 seconds or less is “good,” 2.5 to 4.0 seconds “needs improvement,” and anything over 4.0 seconds is “poor.” But here’s the catch that trips up most teams — Google grades you at the 75th percentile of real visits, not your average and definitely not your test on a wired connection and a fast laptop. That means one in four of your real users can be slower than your reported number, and it’s their experience the threshold is built around.

This is the most common reason a site “passes” in the developer’s browser and fails in Search Console. Your machine is a best case: warm cache, fast CPU, fibre connection. Real field data from the Chrome User Experience Report (CrUX) folds in mid-range Android phones on spotty mobile networks. Look only at lab numbers and you optimize for the user you already are, not the user Google scores you on.

The Four-Phase Breakdown That Changes Everything

This is the framework the compress-your-images crowd skips, and it’s the whole game. Every LCP measurement decomposes into four sequential sub-parts:

  • Time to First Byte (TTFB) — from the click to the first byte of HTML arriving. Typically ~40% of LCP.
  • Resource load delay — the gap between TTFB and the browser actually starting to download the LCP resource. Should be under ~10%. This is pure waste: the browser knew about the resource but hadn’t begun fetching it.
  • Resource load duration — how long the LCP image or font takes to download once fetching starts. Typically ~40%.
  • Element render delay — from the resource finishing to the pixels actually painting. Should be under ~10%.

Those percentages are guidelines, not laws — but they tell you where to look. The two phases that should be small (load delay and render delay) are the ones most likely to be secretly huge on a broken page, and they’re invisible unless you break the number apart. A hero image that downloads in 300ms but doesn’t start until 1.8 seconds in has a load-delay problem, not an image problem. Compress it all you want; you’ll save nothing.

So step one is always to locate your LCP element and its dominant phase before touching code. Record a load in Chrome DevTools’ Performance panel and it labels the LCP element with the phase breakdown; PageSpeed Insights does the same on real field data. Nine times out of ten it’s a hero image or a headline on a web font. Once you know which phase dominates, the rest of this guide is a lookup table: match your slow phase to its fix.

Fixing a Slow TTFB

If TTFB is eating half your LCP, no amount of image work will save you — the browser hasn’t even received the HTML yet. TTFB is a server and network story. Put a CDN in front of your site so the HTML is served from an edge node near the user instead of a single origin continents away. Cache the HTML itself where you can, and avoid unique query parameters that bust that cache. Cut redirect chains — every extra hop from an old URL to a new one adds a full round trip before the first byte lands. And if your backend is generating the page from scratch on every request, cache the render or move to static generation.

A slow TTFB points at architecture, not front-end tweaks. It’s the least glamorous phase and often the highest-leverage one: it sits in front of everything else, so improving it makes every other phase start sooner.

Killing Resource Load Delay

Load delay is the sneakiest phase because it’s invisible pixels of pure waste. The classic cause: the browser can’t discover the LCP image early. Its lightning-fast preload scanner reads the raw HTML and starts fetching images before the main parser even runs — but only if the image is a plain <img> tag in the HTML. Reference it from a CSS background-image or inject it with JavaScript, and the preload scanner never sees it. The fetch waits until the CSS or JS executes, adding hundreds of wasted milliseconds.

Two fixes. First, add fetchpriority="high" to the LCP image so the browser promotes it above other downloads instead of treating it as one image among many. Second, for resources the scanner genuinely can’t see early (a font, or an image only referenced in CSS), declare a preload hint: <link rel="preload" as="image" href="/hero.avif" fetchpriority="high">. Add a preconnect to any third-party origin serving the LCP resource so the DNS, TCP, and TLS handshakes happen in parallel rather than in series.

The Lazy-Loading Trap

This deserves its own section because it’s the most common self-inflicted LCP wound. Lazy-loading — loading="lazy" — is excellent for images below the fold; it defers their download until the user scrolls near them. But blanket “lazy-load all images” plugins and CMS defaults happily apply it to the hero image too. That is exactly backwards. You’ve just told the browser to deprioritize the one image LCP is timing. Never lazy-load your LCP element or anything above the fold. If a plugin lazy-loads everything, exclude the hero explicitly. This single mistake can add a full second to load delay on otherwise fast sites.

Shrinking Resource Load Duration

This is where the traditional advice finally applies — but only if this is your slow phase. Serve modern formats: AVIF or WebP over JPEG and PNG, which cut file size dramatically at equal quality. Use responsive images with srcset and sizes so a phone downloads a phone-sized image instead of your 2400px desktop original. Compress aggressively; a hero rarely needs to be visually lossless. Serve from a CDN so the bytes travel a short distance. For web fonts driving a text LCP, set font-display: swap so text renders immediately in a fallback font rather than staying invisible while the custom font downloads.

Eliminating Render Delay

Render delay is the gap where the resource has arrived but nothing paints — the browser is blocked. The usual culprit is render-blocking resources in the <head>: synchronous stylesheets and scripts the browser must fetch and execute before painting. Inline the small critical CSS for the above-the-fold view and defer the rest. Move non-critical JavaScript out of the blocking path with defer or async, and break up long main-thread tasks. Server-side rendering or static generation helps too: if the LCP content is in the initial HTML rather than assembled by client-side JavaScript, it paints far sooner. A single-page app that renders its hero only after a large JS bundle boots almost always has an ugly render-delay tail.

LCP Is Not INP or CLS — Keep the Signals Straight

The three Core Web Vitals measure different things and share no fixes, so don’t conflate them. LCP is loading (target ≤2.5s). CLS is visual stability — how much the layout jumps as things load (target ≤0.1). And INP, which replaced FID as a Core Web Vital in March 2024, measures responsiveness to interaction across the visit (target ≤200ms). A page can have flawless LCP and terrible INP. When you preload a hero image for LCP, reserve its dimensions with width and height attributes so you don’t create a CLS problem instead. Optimize each metric against its own threshold; a fix for one can quietly hurt another.

Measure in the Field, Not Just the Lab

Because Google scores the 75th percentile of real users, lab tools alone will lie to you. Use both: DevTools and Lighthouse for lab diagnosis with the phase breakdown, and CrUX-backed field data (PageSpeed Insights, the Core Web Vitals report in Search Console, or your own real-user monitoring) for the number that actually counts. Field data lags — CrUX reports a rolling 28-day window — so a fix you ship today won’t show up in your grade for weeks. Watch the lab breakdown for immediate feedback, and confirm in the field over the following month.

Doing this once is easy; keeping LCP green as your site changes is the hard part. Every new hero image, third-party script, or CMS template can quietly regress it. This is where continuous auditing earns its keep — SEO Rocket’s real-crawler site audit flags Core Web Vitals problems, render-blocking resources, and heavy assets automatically, so a regression surfaces as a finding with the fix attached, not a ranking dip you spot three weeks later.

Why This Matters Beyond the Score

Core Web Vitals are a real but modest ranking factor — great content on a slow page still beats thin content on a fast one. The bigger payoff is behavioural: a page that paints in under 2.5 seconds keeps more visitors than one that hangs at four, and lower bounce compounds into engagement signals that do move rankings. Speed multiplies content quality; it doesn’t replace it. A playbook proven across 1,000,000+ ranking pages treats technical health as the floor you clear so your content can compete — which is why SEO Rocket bundles the site audit alongside its AI keyword research and validation-gated writer rather than selling speed as the whole story. Fast pages don’t rank on their own, but slow pages give away rankings they’d otherwise keep.

Frequently Asked Questions

What is a good largest contentful paint score?

An LCP of 2.5 seconds or less is “good,” 2.5 to 4.0 seconds “needs improvement,” and over 4.0 seconds “poor.” Crucially, Google measures this at the 75th percentile of real user visits — so you need three-quarters of your actual traffic, including mid-range phones on mobile networks, to come in under 2.5 seconds, not just your own test machine.

Why is my LCP slow even after I compressed my images?

Because image size is only one of four LCP phases. If your slow phase is TTFB (server response) or resource load delay (the browser discovering the image late), compression saves nothing. Break the number into its four sub-parts in DevTools or PageSpeed Insights, find the phase that dominates, and fix that one — often a preload hint, a removed lazy-load attribute, or a faster server, not a smaller file.

Does lazy-loading hurt largest contentful paint?

Only when you apply it to the LCP element. Lazy-loading below-the-fold images is good practice. But putting loading="lazy" on your hero image tells the browser to deprioritize the exact element LCP is timing, often adding a full second. Never lazy-load anything above the fold, and exclude the hero if a plugin lazy-loads images by default.

Is LCP the same as page load time?

No. Old metrics like load time fire when every resource finishes, long after the user perceives the page as ready. LCP times only when the single largest visible element paints, which maps far more closely to when a page feels loaded — that’s why Google chose it as a Core Web Vital over legacy load events.

Questions? Chat with us