Skip to content
SiteCheckTools

Articles/Website Speed Test

How to Improve Largest Contentful Paint

Everyone tells you to compress your images. That fixes maybe a third of LCP problems, and it is the wrong place to start on the other two thirds.

·9 min read

The tool this explains

Website Speed Test

Core Web Vitals from Google Lighthouse and real Chrome user data, plus server response time measured from multiple regions.

Largest Contentful Paint measures when the biggest element in the viewport finishes rendering. Google's threshold is 2.5 seconds, and it is the metric users experience as "this site is slow".

Almost every guide tells you to compress your images. That fixes roughly a third of LCP problems. The other two thirds are caused by something else entirely, and compressing images will not move them at all.

First, find out what the element actually is

Before changing anything, identify which element is being measured. Lighthouse names it directly — run the page through the Website Speed Test and look at the LCP result, or open Chrome DevTools, go to the Performance panel, record a load, and find the LCP marker on the timeline.

It is usually one of:

  • A hero image or background image
  • A heading or paragraph of text
  • A video poster frame
  • A large block of content in a card or banner

This determines everything that follows. The fix for a slow image has nothing in common with the fix for slow text, and applying the wrong one wastes a day.

LCP has four distinct causes

Google breaks the time into four phases, and knowing which phase dominates tells you exactly what to fix.

1. Time to first byte

The wait before the browser receives anything at all. This is a hard floor on LCP — if the server takes 1.5 seconds to respond, your LCP cannot be under 1.5 seconds no matter how small the image is.

This is the cause people most often skip, and it is frequently the largest single component. If server response time is above 600 ms, fix that before touching anything else. See what is a good TTFB.

2. Resource load delay

The gap between the first byte arriving and the browser starting to download the LCP element. This is discovery time — the browser cannot fetch what it has not found yet.

This phase is usually large when the LCP image is:

  • Set as a CSS background-image, so it is not discovered until the stylesheet is parsed
  • Inserted by JavaScript, so it is not discovered until the script runs
  • Behind a lazy-loading attribute, so the browser deliberately defers it

The single highest-value fix in this article: if your LCP element is an image, it must be a plain <img> in the HTML, it must not be lazy-loaded, and it should be preloaded.

<link rel="preload" as="image" href="/hero.webp" fetchpriority="high">

Or more simply, on the image itself:

<img src="/hero.webp" width="1200" height="600" fetchpriority="high" alt="…">

Note the mistake this warns against: applying loading="lazy" to every image sitewide. Lazy loading is correct for images below the fold and actively harmful on the LCP element, because you are telling the browser to deprioritise the exact thing being measured.

3. Resource load duration

How long the download itself takes. This is the phase image compression actually helps, and it is why compression alone so often disappoints — it only addresses one of four phases.

What helps here:

  • Serve modern formats. WebP is typically 25–35% smaller than JPEG at the same quality; AVIF is smaller still.
  • Size the image for the space it occupies. A 4000px-wide image displayed at 800px wastes most of what it downloads.
  • Use srcset so phones get a phone-sized file rather than the desktop one.
  • Serve from a CDN, so the bytes travel a shorter distance.

4. Element render delay

The gap between the resource finishing and the element actually appearing. If this phase is large, something is blocking rendering — and it is usually one of two things.

Render-blocking CSS. The browser will not paint until it has parsed the CSS it needs. A large stylesheet, or several, delays everything.

Web fonts. If the LCP element is text and the font has not loaded, the browser may hide the text entirely while waiting. Set font-display: swap so text renders immediately in a fallback font, and preload the font file:

@font-face {
  font-family: "YourFont";
  src: url("/fonts/yourfont.woff2") format("woff2");
  font-display: swap;
}

Swap has a cost — the text visibly re-renders when the real font arrives, which contributes to CLS unless the fallback is metrically similar. Using size-adjust on the fallback reduces that shift.

A working order

Do these in sequence rather than all at once, and re-measure between each. Changing five things and seeing a 400 ms improvement tells you nothing about which one worked.

  1. Check server response time. If it is over 600 ms, stop and fix that. Nothing else matters until it is addressed.
  2. Identify the LCP element so you are optimising the right thing.
  3. If it is an image: remove any lazy-loading from it, ensure it is a real <img> in the HTML rather than a CSS background, add fetchpriority="high", and preload it.
  4. Compress and resize it. Modern format, correct dimensions, srcset for smaller screens.
  5. If it is text: preload the font and set font-display: swap.
  6. Reduce render-blocking CSS. Inline the critical styles for the top of the page, defer the rest.
  7. Re-measure, and check the field data over the following weeks rather than expecting an instant change.

Things that look like they should help but usually do not

Compressing every image on the page. Only the LCP element affects LCP. Compressing the rest is worth doing for total page weight and for users on metered connections, but it will not move this metric.

Removing JavaScript. This helps INP substantially and LCP only marginally, unless the script is what inserts the LCP element or blocks rendering.

Minifying CSS and JS. Real but small — a few percent. Not where a failing LCP gets rescued.

Switching to a faster host, when the server is already responding quickly. If TTFB is 150 ms, the server is not your problem and changing it changes nothing.

Why the field data lags

After shipping a fix, the lab number improves immediately and the field data takes weeks. Field data is a 28-day rolling window, so a change made today is diluted by 27 days of the old behaviour and only fully reflected after four weeks.

That delay is not a reason to keep changing things. Make the fix, verify it in the lab, then wait — re-optimising because the field number has not moved yet is how people end up breaking things that were already working.

Measure your current LCP, and the server response time underneath it, with the Website Speed Test.

Check this on your own domain with the Website Speed Test. Free, no signup, and every result shows the raw data behind it.