# How to fix Largest Contentful Paint (LCP), step by step

> Find the element that decides your LCP, see which of its four parts is slow, and fix them in the order that pays off most, from preloading the hero to a faster server. With HTML and WordPress code.

Updated 2026-09-25 · Speed & Core Web Vitals · HTML version: https://getreport.app/guides/fix-largest-contentful-paint

Largest Contentful Paint is the moment the biggest thing on screen finishes drawing: the hero photo, the product image, the headline. Visitors decide whether a page is slow at about that moment, and Google uses it as one of the three Core Web Vitals. This guide shows how to find your LCP element, work out which part of its loading is slow, and fix it in order. Most pages get under 2.5 s with the first three steps.

## Quick answer

| LCP | Rating |
| --- | --- |
| ≤ 2.5 s | Good |
| 2.5–4.0 s | Needs improvement |
| > 4.0 s | Poor |

- Find the LCP element in the [speed test](https://getreport.app/tools/speed-test): it is named in the finding.
- If it is an image: make sure it is **not lazy-loaded**, give it `fetchpriority="high"`, and **preload** it if it is not in the HTML (CSS background, slider, script).
- Serve it **at the size it is displayed** and as **WebP or AVIF**; aim for under 200 KB for a full-width hero.
- Cut **render-blocking CSS and JavaScript** in the `<head>`.
- Get the **server response** under 600 ms with page caching and a CDN.
- Judge success by the field data (75th percentile of real Chrome users over 28 days), not one lab run.

## Why LCP matters

Everything before LCP is the visitor looking at an unfinished page. On a shop, the product photo is usually the LCP element, so a slow LCP means people scroll past a grey box where the product should be, or leave before it appears. On an article, the headline or the lead image is the LCP element, and a slow one means reading starts late.

For Google, LCP is one of the three Core Web Vitals in the page experience signal, measured on real Chrome visits. A page passes when 75 % of visits are at 2.5 s or faster. The guide Core Web Vitals for site owners, listed at the end of this page, explains all three metrics in plain language.

LCP is also the metric with the most leverage. On most slow pages it comes down to one file (a hero image straight from a camera), one setting (lazy loading on that image), and one slow first byte. Fix those three and the number drops by seconds, not milliseconds.

## How getReport checks it

> **Free tool:** [Website speed test](https://getreport.app/tools/speed-test): Lighthouse lab results and real-user Core Web Vitals for any page, mobile and desktop, with a filmstrip, a request waterfall and a fix for every slow part. Free, no signup.

The speed test asks Google PageSpeed Insights for a Lighthouse run of the page on a throttled phone, plus the Chrome UX Report (CrUX) field data for the URL and the origin. The LCP finding names the element Lighthouse picked, as an HTML snippet, so you know exactly what to fix:

![The LCP finding card on a slow shop page: Largest Contentful Paint failing at around ten seconds, the hero image named as the LCP element, and the three fix steps](https://getreport.app/guides/img/fix-largest-contentful-paint/finding.webp "The LCP finding names the element that decided the metric and lists the fixes in order.")

> **Check: Largest Contentful Paint.** LCP is when the biggest thing on screen — usually the hero image or headline — finishes loading. Visitors judge "is this site slow?" on it, and Google uses it for ranking.
>
> 1. Find the LCP element (named in your report) and make it lighter, earlier or both — a compressed WebP/AVIF, sized to the screen, without lazy loading.
> 2. Preload it with <link rel="preload" as="image"> or add fetchpriority="high" on the <img>.
> 3. Cut what comes before it — render-blocking CSS/JS and slow server responses push LCP back.

> **Check: Real-user Largest Contentful Paint.** LCP is when the biggest element on screen finishes loading. Google's threshold for "good" is 2.5 s at the 75th percentile of real Chrome users.
>
> 1. Find the LCP element in the Speed module and make it smaller, earlier or both.

The field finding only appears when Google has enough real visits for the page or the origin. When it is there, trust it over the lab number: it is what Google uses. The lab number is the diagnosis; it tells you why.

Three other findings in the same module are usually the causes behind a slow LCP:

> **Check: No render-blocking resources delay the first paint.** Stylesheets and scripts in <head> stop the browser from drawing anything until they have downloaded. Each one adds a round trip before the visitor sees the page.
>
> 1. Add defer or async to scripts that are not needed before the first paint, or move them to the end of <body>.
> 2. Inline the CSS needed for the first screen and load the rest asynchronously (WordPress: "Optimize CSS delivery" in WP Rocket / LiteSpeed Cache).
> 3. Remove unused plugin CSS/JS from pages that do not need it.

> **Check: Server response time.** Lighthouse flags a first byte slower than 600 ms. Caching and hosting fix this for most CMS sites.
>
> 1. Enable full-page caching (WordPress: WP Rocket, LiteSpeed Cache, WP Super Cache; or your host's server cache).
> 2. Use a CDN and keep database queries out of the request path.

> **Check: Images are served in modern formats (WebP or AVIF).** WebP and AVIF are 25–50 % smaller than JPEG and PNG at the same quality. Smaller images load faster and cost visitors less data.
>
> 1. Convert images to WebP or AVIF and serve them with <picture> or content negotiation.
> 2. WordPress: enable WebP/AVIF in your image plugin or CDN (Imagify, ShortPixel, Cloudflare Polish); Shopify does this automatically for its CDN.

The Core Web Vitals checker shows the same LCP values next to INP and CLS if you only want the three vitals.

## What is usually the LCP element

The browser watches the largest image, video poster or text block inside the first screen while the page loads, and records when it was painted. Which element wins depends on the viewport, so mobile and desktop can have different LCP elements:

- **Home and landing pages**: the hero image or banner slider.
- **Product pages**: the main product photo.
- **Articles**: the lead image, or the headline when there is no image above the fold.
- **Text-only pages**: the first large paragraph or the H1.

When the LCP element is text, the usual culprits are a web font the text waits for (see the [font-display learn page](https://getreport.app/learn/font-display)) and render-blocking CSS. When it is an image, the image itself is usually the problem.

## The four parts of LCP

[web.dev splits every image LCP](https://web.dev/articles/optimize-lcp) into four consecutive parts. Knowing which one is large tells you which fix to use:

| Part | What happens | Typical fix |
| --- | --- | --- |
| **Time to first byte** | The server produces the HTML | Page cache, CDN, better hosting |
| **Resource load delay** | Time between the HTML arriving and the image starting to download | Put the image in the HTML, preload it, remove lazy loading |
| **Resource load duration** | The image downloads | Smaller file, right size, WebP/AVIF |
| **Element render delay** | The image is downloaded but not yet on screen | Fewer render-blocking CSS/JS files, no hiding by scripts |

On a well-built page, the two "delay" parts are close to zero and most of the time is the first byte and the download. A large resource load delay is the most common and cheapest problem to fix: the browser found the image late. Chrome DevTools shows this breakdown for your own page: open the Performance panel, record a reload, and look at the LCP breakdown in the insights.

For a text LCP there is no resource to load, so it is only the first byte and the render delay.

## Step by step

Work in this order. Each step is cheap and the first three fix most sites.

### 1. Make sure the LCP image is not lazy-loaded

`loading="lazy"` tells the browser to wait until layout is done and the image is known to be near the viewport. For the hero that wait is pure delay. Remove it from the LCP image and from anything else in the first screen:

```html
<!-- Before: the hero waits for layout -->
<img src="https://getreport.app/img/hero.webp" loading="lazy" alt="Summer collection">

<!-- After: loads as soon as the HTML is parsed -->
<img src="https://getreport.app/img/hero.webp" alt="Summer collection" width="1200" height="600">
```

JavaScript lazy-load libraries are worse: the real address sits in `data-src` and the image cannot start until the script has run. Exclude the hero from them. The details are in [Lazy loading images and iframes done right](https://getreport.app/guides/lazy-loading-done-right).

### 2. Raise the priority with fetchpriority="high"

Browsers start images at low priority until they know the image is in the viewport. `fetchpriority="high"` tells them up front:

```html
<img src="https://getreport.app/img/hero.webp" fetchpriority="high"
     alt="Summer collection" width="1200" height="600">
```

Use it on one image per page: the LCP image. Putting it on five images makes it mean nothing.

### 3. Preload the image when the HTML does not contain it

The browser's preload scanner finds `<img src>` in the HTML almost immediately. It cannot find an image that is a CSS `background-image`, one inserted by a slider script, or one whose address is in `data-src`. Tell it about the image with a preload in the `<head>`:

```html
<!-- In <head>, before stylesheets and scripts -->
<link rel="preload" as="image" href="https://getreport.app/img/hero-1200.webp" fetchpriority="high">
```

For a responsive image, preload the same `srcset` the `<img>` uses so the browser picks the same file:

```html
<link rel="preload" as="image" fetchpriority="high"
      imagesrcset="/img/hero-600.webp 600w, /img/hero-1200.webp 1200w, /img/hero-2000.webp 2000w"
      imagesizes="100vw">
```

A better fix for a CSS background hero is to turn it into a real `<img>` with `object-fit: cover`; then no preload is needed.

### 4. Make the file small

A 2 MB PNG takes seconds on a phone connection whatever you do with priorities. Export the hero at the widest size it is displayed (times two for high-density screens), as WebP or AVIF at around 80 % quality. A full-width hero should land under 200 KB, a product photo under 100 KB. Offer smaller versions to phones with `srcset`:

```html
<img src="https://getreport.app/img/hero-1200.webp"
     srcset="/img/hero-600.webp 600w, /img/hero-1200.webp 1200w, /img/hero-2000.webp 2000w"
     sizes="100vw" fetchpriority="high"
     alt="Summer collection" width="1200" height="600">
```

Serve the image from your own domain or the same CDN as the page. A hero on a different host costs an extra connection (DNS, TCP, TLS) before the first byte. [Image sizes that do not hurt](https://getreport.app/guides/image-sizes-that-do-not-hurt) covers export sizes and formats per CMS.

### 5. Cut render-blocking CSS and JavaScript

Every stylesheet and synchronous script in the `<head>` must download before the browser paints anything, including an image that has already arrived. That is the element render delay. Defer scripts that are not needed for the first screen:

```html
<!-- Blocks rendering -->
<script src="https://getreport.app/js/slider.js"></script>

<!-- Downloads in parallel, runs after parsing -->
<script src="https://getreport.app/js/slider.js" defer></script>
```

For CSS, remove stylesheets for things not on the page (a plugin's CSS for a form that is only on the contact page), and consider inlining the small amount of CSS the first screen needs. The [render-blocking resources learn page](https://getreport.app/learn/render-blocking-resources) lists the options.

One trap: sliders and "fade in on load" animations that start with `opacity: 0` and wait for a script. The image is downloaded, but invisible until the script runs, so LCP waits for JavaScript. Show the first slide without a script.

### 6. Speed up the first byte

Everything above starts only when the HTML arrives. If the server-response finding says 1.5 s, no image work gets LCP under 2.5 s on a phone. Full-page caching fixes most CMS sites; a CDN helps visitors far from the server. [TTFB: what a slow server looks like](https://getreport.app/guides/ttfb-what-a-slow-server-looks-like) walks through it.

## Lab and field: why the numbers differ

The speed test reports two LCP values, and they rarely match.

- **Lab (Lighthouse)**: one load of the page in a data centre, on a simulated mid-range phone with a throttled connection, with an empty cache. It is repeatable, it names the element and shows the causes. It is usually slower than real visitors.
- **Field (CrUX)**: the 75th percentile of real Chrome visits over the last 28 days. Real visitors have warm caches, faster phones or slower networks, and may land on a different LCP element. This is what Google uses.

Fix with the lab, judge with the field. After a deploy the lab number moves immediately; the field number moves gradually over four weeks as old visits drop out of the window. A page with too few visits has no URL-level field data, and the origin figure is shown instead.

## Platform notes

### WordPress

- **Theme hero images** are often CSS backgrounds set in the Customizer. Switch to a block or template that outputs an `<img>`, or preload the file from `functions.php`:

```php
// functions.php (child theme): preload the front-page hero
add_action('wp_head', function () {
    if (!is_front_page()) {
        return;
    }
    $src = get_theme_file_uri('assets/img/hero-1200.webp');
    echo '<link rel="preload" as="image" href="' . esc_url($src) . '" fetchpriority="high">' . "\n";
}, 1);
```

- **Core lazy loading**: WordPress adds `loading="lazy"` automatically, skips the first content images, and since 6.3 adds `fetchpriority="high"` to the image it expects to be the LCP. Themes that print the hero outside the content can still get it wrong. In a template, ask for an eager, high-priority image explicitly:

```php
// In the theme template that prints the hero
echo wp_get_attachment_image($hero_id, 'full', false, [
    'loading'       => false,   // no loading attribute at all
    'fetchpriority' => 'high',
]);
```

- **Plugins that lazy-load everything** (many optimisation plugins, some page builders) replace `src` with `data-src` and undo all of the above. In **WP Rocket**, add the hero's file name to the excluded images list in the Media tab; recent versions also try to detect above-the-fold images automatically, so check the result. In **Perfmatters**, use "Exclude Leading Images" under Lazy Loading and "Preload Critical Images" under Preloading. In **LiteSpeed Cache**, add the file to the lazy-load image excludes. Whichever you use, turn off the theme's or builder's own lazy loading so only one system is in charge.
- **Sliders** (Revolution Slider, builder carousels) load a script, then the slides. A static first image is almost always faster.

### Shopify

Shopify's image CDN serves WebP automatically and resizes by URL parameter, so the file-size step is mostly done. The usual problem is a theme that lazy-loads the first section's image: check the hero section's settings, or edit the section's Liquid so the first image is not lazy.

## Verify

- Re-run the speed test. The LCP finding should be under 2.5 s in the lab, and the element should be the one you expect.
- View the page source and search for the hero's file name: it should appear in an `<img src>` or a `<link rel="preload">` in the HTML, not only in a script or CSS file.
- In Chrome DevTools, Network panel, reload and find the hero: its priority column should read "High" and it should start within the first few requests.
- Check the field value in the speed test or Search Console after four weeks.

## Common mistakes

- **Lazy-loading the hero.** Symptom: a large resource load delay, and the hero starts after the layout. Fix: remove `loading="lazy"` or exclude it in the plugin.
- **Preloading the wrong file.** Symptom: the hero downloads twice, or DevTools warns that a preloaded resource was not used. Fix: preload the exact URL (or the same `imagesrcset`) the `<img>` uses.
- **`fetchpriority="high"` on every image.** Symptom: no improvement. Fix: one image, the LCP one.
- **Optimising the desktop hero only.** Symptom: desktop is fine, mobile fails. Fix: run the test on mobile (the default) and check which element is LCP there; it may be a different image.
- **Chasing the lab number after the field passes.** Symptom: hours spent on a 3.1 s lab value while real visitors are at 1.9 s. Fix: when the field value is good, move on to the next finding.
