Largest Contentful Paint (LCP) is the time from the moment a visitor opens a page until the largest image or text block in the first screen is drawn: the hero photo, the product image, the headline. Google rates it good at 2.5 s or less and poor above 4 s, at the 75th percentile of real visits, and it is the loading metric among 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. For all three vitals and how Google uses them, start with Core Web Vitals for site owners.
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: it is named under the lab table.
- 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.
What is Largest Contentful Paint?
While the page loads, the browser keeps track of the largest visible element in the viewport and records when it was painted. The candidates are <img> elements, images inside an <svg>, the poster or first frame of a <video>, elements with a CSS background-image, and block-level elements containing text. The last candidate painted before the visitor first taps, scrolls or presses a key is the LCP element, and its paint time is the page's LCP.
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.
Chrome ignores full-viewport background images and, since 2023, low-detail placeholder images (such as a blurred or single-colour preview), so a placeholder cannot "win" LCP for you. Since Safari 26.2 in December 2025, every major browser can measure LCP, but Google's field data (the Chrome UX Report) still comes only from Chrome users.
Why LCP matters
Everything before LCP is the visitor looking at an unfinished page. On a shop, a slow LCP means people scroll past a grey box where the product should be, or leave before it appears. For Google, LCP is one of the Core Web Vitals its ranking systems use, as one signal among many; a page passes when 75 % of visits are at 2.5 s or faster.
LCP is also the vital 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
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 element is printed under the lab table as an HTML snippet, so you know exactly what to fix:

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. Three other findings are usually the causes behind a slow LCP:
The Core Web Vitals checker shows the same LCP values next to INP and CLS if you only want the three vitals.
The four parts of LCP
web.dev splits every image 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: the browser found the image late. In Chrome DevTools, record a reload in the Performance panel and open the "LCP breakdown" and "LCP request discovery" insights; Lighthouse 13 shows the same insights in PageSpeed Insights. For a text LCP there is no resource to load, so it is only the first byte and the render delay; the usual culprits are a web font the text waits for (see font-display) and render-blocking CSS.
How to improve Largest Contentful Paint, 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:
<!-- Before: the hero waits for layout -->
<img src="/img/hero.webp" loading="lazy" alt="Summer collection">
<!-- After: loads as soon as the HTML is parsed -->
<img src="/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; see Lazy loading images and iframes 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", supported in all major browsers, tells them up front:
<img src="/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 a CSS background-image, an image inserted by a slider script, or one whose address is in data-src. Tell it about the image with a preload in the <head>:
<!-- In <head>, before stylesheets and scripts -->
<link rel="preload" as="image" href="/img/hero-1200.webp" fetchpriority="high">For a responsive image, preload the same srcset the <img> uses so the browser picks the same file:
<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:
<img src="/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 page's CDN: a hero on a different host costs an extra connection before the first byte. 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:
<!-- Blocks rendering -->
<script src="/js/slider.js"></script>
<!-- Downloads in parallel, runs after parsing -->
<script src="/js/slider.js" defer></script>For CSS, remove stylesheets for things not on the page and consider inlining the small amount of CSS the first screen needs; the render-blocking resources learn page 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. 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 walks through it.
Lab and field: why the numbers differ
- Lab (Lighthouse): one load in a data centre, on a simulated mid-range phone with a throttled connection and an empty cache. It is repeatable, 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 see 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.
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:
// 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);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 the template, ask for an eager, high-priority image explicitly:
// 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 replace src with data-src and undo all of the above. In WP Rocket, exclude the hero in the Media tab (recent versions also detect above-the-fold images automatically; check the result). In Perfmatters, use "Exclude Leading Images" and "Preload Critical Images". In LiteSpeed Cache, add the file to the lazy-load image excludes. Let only one system handle lazy loading. WordPress Core Web Vitals covers themes, builders and hosting.
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. LCP 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">, not only in a script or CSS file. - In DevTools, Network panel, the hero's 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. When LCP passes, how to improve Core Web Vitals covers INP and CLS.
Common mistakes
- 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: check which element is LCP on the mobile run; 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.
Questions people ask
What is Largest Contentful Paint?
Largest Contentful Paint is the time until the largest image, video poster or text block in the first screen is drawn, counted from the moment the visitor starts loading the page. It is Google's Core Web Vital for loading: 2.5 s or less at the 75th percentile of real visits is good, over 4 s is poor. It stands for the moment a visitor sees the page's main content.
Should I preload the LCP image?
Only when the browser cannot find it in the HTML: a CSS background, an image added by a slider or a lazy-load script. A plain <img> in the HTML is found almost at once, so fetchpriority="high" without lazy loading is enough. When you do preload, use the exact URL or the same imagesrcset as the image, or the browser downloads it twice.
Can text be the Largest Contentful Paint element?
Yes. On pages without a large image in the first screen, the headline or the first big paragraph is usually the LCP element. Then there is no file to optimise; LCP depends on the server response, render-blocking CSS and the web font the text uses. Use font-display: swap or optional and preload the headline's font so the text paints without waiting for it.
Why is my LCP element different on mobile and desktop?
Because the browser picks the largest element in the visible area, and the two viewports show different things. On a phone the hero might be cropped or replaced, so a headline or product photo can be largest; on desktop a wide banner often wins. Run the test for both and fix each element, starting with mobile, where most visits happen.