/ โ† Core Web Vitals Fixes / Largest Contentful Paint
In this guide
  1. What LCP actually measures
  2. The four parts of LCP time
  3. How to find your LCP element and its bottleneck
  4. Common causes, stage by stage
  5. The key HTML changes
  6. Platform notes
  7. The fix, step by step
  8. Edge cases worth knowing
  9. Keeping LCP fast
  10. Questions and answers

What LCP actually measures

While a page loads, the browser keeps track of the largest piece of content it has painted inside the viewport so far. Each time something bigger appears, it becomes the new candidate. When the visitor first interacts with the page, by tapping, clicking or pressing a key, the browser stops looking, and the last candidate becomes the Largest Contentful Paint. Scrolling does not count as an interaction for this purpose on every browser, so treat the first screenful as what matters.

Only certain kinds of element can be the LCP element:

The size used is what is visible on screen, so an image that is partly off the edge only counts its visible area, and an image stretched beyond its natural size is counted at its natural size. Chrome also ignores elements with no visible content and very low-detail placeholder images, so a blurry placeholder usually does not steal the LCP from the real hero image that replaces it.

LCP at the 75th percentileRating
2.5 s or lessGood
Over 2.5 s up to 4 sNeeds improvement
Over 4 sPoor

The time is measured from when the visitor started navigating to your page, not from when the HTML arrived. Redirects, DNS lookups, connection set-up and a slow server all count towards it before a single byte of your hero image has been requested.

The four parts of LCP time

Every LCP time can be split into four consecutive stages. Knowing which stage is long tells you which fix to apply, and it stops you spending a day compressing an image when the real problem is that the browser did not ask for it until two seconds in.

StageRuns fromTypical cause when it is long
Time to First ByteNavigation start to the first byte of HTMLSlow hosting, no page cache, redirects, a distant server
Resource load delayFirst byte to the moment the LCP resource is requestedImage found late: set by JavaScript, hidden in CSS, lazy-loaded, or queued behind other files
Resource load durationRequest start to the last byte of the resourceOversized file, wrong format, slow or distant image host
Element render delayResource loaded to the element being paintedRender-blocking CSS or scripts, web fonts, client-side rendering, hidden-until-ready tricks

For a text LCP element, such as a large heading, there is no resource to fetch, so the two middle stages are zero and the time is split between TTFB and render delay. That usually points at blocking stylesheets or a web font.

As a rough guide, a healthy page spends most of its LCP time in TTFB and in downloading the image, with the load delay and render delay both kept small. If either of the "delay" stages is the largest slice, you have a prioritisation problem rather than a file-size problem, and that is good news, because prioritisation fixes are usually a single line of HTML.

๐Ÿ’ก PageSpeed Insights and the Performance panel in Chrome DevTools both show this breakdown for the lab run. In the Performance panel, record a page load and open the LCP insight to see the four stages as bars.

How to find your LCP element and its bottleneck

Start with the Core Web Vitals checker or PageSpeed Insights for the failing URL. Check the field result first, on mobile, to confirm the problem is real, then look at the lab diagnostics to see which element Lighthouse chose and how its time breaks down.

The lab element is not always the one real visitors see. A cookie banner, a different hero on small screens or personalised content can all change it. To see what your own browser records, open DevTools, go to the Console and paste this before reloading:

new PerformanceObserver((list) => {
  const entries = list.getEntries();
  const last = entries[entries.length - 1];
  console.log('LCP candidate', Math.round(last.startTime), 'ms', last.element, last.url);
}).observe({ type: 'largest-contentful-paint', buffered: true });

Every new candidate is logged, so the final line is the LCP element. Repeat on a phone-sized viewport with the network throttled, because mobile layouts often have a different hero.

Collecting it from real visitors

For the full picture, record LCP from real visits using Google's open-source web-vitals library with its attribution build. It reports the element selector and the time spent in each of the four stages, which you can send to your analytics tool:

import { onLCP } from 'web-vitals/attribution';

onLCP(({ value, attribution }) => {
  navigator.sendBeacon('/rum', JSON.stringify({
    lcp: Math.round(value),
    element: attribution.target,
    ttfb: Math.round(attribution.timeToFirstByte),
    loadDelay: Math.round(attribution.resourceLoadDelay),
    loadDuration: Math.round(attribution.resourceLoadDuration),
    renderDelay: Math.round(attribution.elementRenderDelay)
  }));
});

Group the results by page template. If the same element and the same slow stage keep coming up, you have found the fix to make.

Common causes, stage by stage

Long TTFB

If the first byte takes more than about 800 ms for most visitors, fix that before anything else. Common causes are pages built from scratch on every request, uncached database queries, a server on another continent from your visitors, and chains of redirects such as http to https to www. The TTFB guide covers server-side fixes in detail.

Long resource load delay

Long resource load duration

The file is simply too big for the connection: a 2,400-pixel-wide JPEG sent to a 400-pixel phone screen, no modern format, or an image served from a separate domain that needs its own connection set up. Serve AVIF or WebP, use srcset and sizes so small screens get small files, and host images on the same origin or a CDN that is already connected.

Long element render delay

The image has arrived but the browser is not allowed to paint it yet. Blocking stylesheets and synchronous scripts in the <head> are the usual reason. Others are A/B testing snippets that hide the page until an experiment loads, fade-in animations that start at zero opacity, a hero that is only revealed once a slider script initialises, and text headings waiting for a web font with a long block period.

The key HTML changes

For most image heroes, the fix is a correctly written image tag that the browser can find in the first chunk of HTML and knows is important:

<img
  src="/images/hero-1200.avif"
  srcset="/images/hero-600.avif 600w, /images/hero-1200.avif 1200w, /images/hero-1800.avif 1800w"
  sizes="100vw"
  width="1200" height="675"
  alt="Walkers on the coastal path above Hartcliffe Bay"
  fetchpriority="high"
  decoding="async">

There is no loading attribute, which means eager loading. fetchpriority="high" tells the browser to request it ahead of other images, and it is now supported across Chrome, Edge, Safari and Firefox. The width and height reserve space so the image does not also cause layout shift.

When the image cannot be in the HTML

If the hero must stay a CSS background, or it is inserted by a framework, preload it so the request starts straight away:

<link rel="preload" as="image"
  href="/images/hero-1200.avif"
  imagesrcset="/images/hero-600.avif 600w, /images/hero-1200.avif 1200w"
  imagesizes="100vw"
  fetchpriority="high">

Put it high in the <head>, before stylesheets and scripts. Preload only the one LCP image per template; preloading several images makes them compete with each other and with your CSS.

Images on another domain

<link rel="preconnect" href="https://images.example-cdn.com" crossorigin>

A preconnect lets the browser do the DNS lookup, connection and TLS handshake for an image host early. Use it for the one or two origins that serve above-the-fold content, not for every third party.

โš ๏ธ Do not add fetchpriority="high" to every image "just in case". If everything is high priority, nothing is, and you can slow down the stylesheet the page needs to render.

Platform notes

The fix, step by step

Step 1

Confirm the failure in field data

Run the failing URL through the Core Web Vitals checker and note the mobile field LCP at the 75th percentile, then open the Core Web Vitals report in Search Console to see which group of similar URLs it sits in. Field data tells you whether real visitors have a problem; if there is no field data for the page, use the origin figure and treat lab results as a guide only. Write the baseline down so you can compare later.

Step 2

Identify the LCP element

Read the element named in the lab diagnostics, then confirm it yourself by running a PerformanceObserver for largest-contentful-paint in the DevTools console on a phone-sized viewport with throttling on. Note whether it is an image tag, a CSS background, a video poster or a text block, and whether it is the same on desktop. Check for a cookie banner or popup becoming the element instead of your intended hero.

Step 3

Break the time into its four parts

Use the LCP breakdown in PageSpeed Insights or the DevTools Performance panel to split the time into Time to First Byte, resource load delay, resource load duration and element render delay. Find the largest slice. A long first stage sends you to server work, long delays mean a discovery or blocking problem, and a long download means the file itself is too heavy for the connection your visitors use.

Step 4

Make the element discoverable in the HTML

View the page source rather than the inspected DOM and search for the hero image address. If it only appears in a stylesheet, a data-src attribute or a JavaScript bundle, change the template so a normal <img> with a real src and srcset is in the server-sent HTML. Where that is impossible, add a single rel="preload" link for the image near the top of the head.

Step 5

Give it top priority and stop lazy-loading it

Remove loading="lazy" from the hero image and from anything else in the first screenful, and add fetchpriority="high" to the LCP image only. If the image lives on a different host, add a preconnect for that origin. Check that theme settings, lazy-load plugins and image components are not adding the lazy attribute back when the page is rendered, then reload and confirm the request starts early.

Step 6

Shrink the file and shorten the render delay

Serve the hero as AVIF or WebP at sizes that match real screens, using srcset and an accurate sizes value so phones do not download desktop images. Then clear the path to painting it: inline the small amount of CSS the first screen needs, defer non-essential scripts, remove fade-in effects that start at zero opacity, and give headline fonts font-display: swap or a close fallback.

Step 7

Verify in the lab, then in the field

Rerun the lab test several times on mobile settings and compare the four stages with your baseline, not just the headline number. Deploy to every page that shares the template, then watch your own real-user monitoring for a few days. The Chrome UX Report and Search Console use a rolling 28-day window, so start validation in Search Console and expect the field figure to improve gradually over about four weeks.

Edge cases worth knowing

Keeping LCP fast

LCP tends to drift back when a new hero section, a new slider or a new plugin arrives. A few habits stop that:

  1. Write the hero image rules into the template itself, so editors cannot switch lazy loading back on for the first section.
  2. Set a size budget for hero images and have your upload process create AVIF and WebP versions at fixed widths.
  3. Add a Lighthouse check to your deployment process that fails when LCP on a test page goes over an agreed limit, and keep real-user monitoring running so field regressions are noticed within days.
  4. Review any script that must run before the page is shown, such as A/B testing or personalisation tools, before it is added.

If your pages also move around as they load, the layout shift guide covers the related fixes, and the First Contentful Paint guide deals with the blocking files that delay the very first paint.

Questions and answers

1. What is a good LCP score?

Google treats a Largest Contentful Paint of 2.5 seconds or less as good, between 2.5 and 4 seconds as needing improvement, and over 4 seconds as poor. The figure that counts is the 75th percentile of real visits over 28 days, measured separately for mobile and desktop. So at least three out of four page loads need to reach LCP within 2.5 seconds. A single fast lab test on your own computer does not show whether the page passes for your visitors.

2. How do I find out which element is my LCP element?

PageSpeed Insights and Lighthouse name the element in their diagnostics, and the Performance panel in Chrome DevTools highlights it when you record a page load. You can also paste a small PerformanceObserver script into the console that logs each LCP candidate as the page loads, with the final one being the element that counts. Check on a phone-sized screen too, because mobile layouts often use a different hero image or a large heading instead.

3. Should I lazy-load my hero image?

No. Lazy loading tells the browser to wait until it knows the image is near the viewport, which pushes the request later and directly lengthens LCP. Load the hero image and anything else in the first screenful eagerly, which is the default when there is no loading attribute, and give the single LCP image fetchpriority set to high. Keep lazy loading for images further down the page, where it saves data without affecting the first view.

4. Does fetchpriority high replace preload?

They solve different problems. fetchpriority on an image tag raises the priority of an image the browser has already found in the HTML. A preload link makes the browser aware of a resource it would otherwise find late, such as a CSS background image or an image inserted by JavaScript. If your hero is a normal image tag near the top of the HTML, fetchpriority is usually enough. If it is hidden from the HTML, you need a preload with high priority.

5. My LCP element is text, not an image. How do I speed it up?

A text element has nothing to download, so its LCP time is made up of server response time and render delay. Look at what stops the browser painting the text: large stylesheets in the head, synchronous scripts, and web fonts that hide text while they load. Inline the critical CSS, defer scripts, and set font-display to swap or optional for the heading font. Also make sure the text is in the server-sent HTML rather than rendered by JavaScript.

6. Why is my LCP fine in Lighthouse but poor in Search Console?

Lighthouse tests one simulated visit, while Search Console reports real visitors over 28 days. Real visitors may use slower phones, arrive through redirects from ads or emails, see a cookie banner, or be further from your server. The fix may also be too recent to show in the rolling window. Compare the field and lab breakdowns, add real-user monitoring to see which stage is slow for real visitors, and read our guide to CrUX and lab mismatches.

7. Will a CDN fix my LCP?

A CDN can help two of the four stages. Caching whole pages at the edge shortens time to first byte, and serving images from nearby servers shortens download time. It will not fix a hero image that is lazy-loaded, set by JavaScript or hidden behind blocking scripts, which are among the most common causes. Break your LCP into its stages first; if the delay stages are the largest, a CDN on its own will make little difference.

Find your LCP element and what is slowing it

The Core Web Vitals checker shows real-user field data from the Chrome UX Report and a Lighthouse lab test side by side, breaks down LCP and points to the assets and code behind the failure.

Open Core Web Vitals Fixes โ†’
๐Ÿ’ฌ Got a problem?
Related Guides: All Core Web Vitals Fixes  ·  Slow Time to First Byte  ·  Slow First Contentful Paint  ·  Core Web Vitals Guide  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.