Largest Contentful Paint (LCP) is the time from when a page starts loading to when its largest visible element finishes rendering. Google uses LCP as one of three Core Web Vitals (alongside CLS and INP) — it's a ranking factor, and pages with poor LCP underperform competitors with the same content but better speed. The "good" threshold is 2.5 seconds or less at the 75th percentile of real visitors.
This guide covers what LCP measures specifically, the four root causes of a slow LCP, and the practical fixes that reliably move it under 2.5 seconds. Most LCP problems come from a small set of recurring patterns that this guide walks through.
What LCP Actually Measures
The browser tracks every "contentful" element rendered to the viewport during page load — text blocks, images, video posters, background images on block-level elements. As each renders, the browser asks: "is this the largest contentful element so far?" Whichever element is largest at the moment user input occurs (or the page completes loading) is the LCP element, and the time of its render is the LCP value.
Practically:
- For an article page, LCP is usually the hero image at the top.
- For a product page, LCP is the main product photo.
- For a long-form text post, LCP might be a large heading or the first paragraph.
- For a SPA that renders nothing on first paint then hydrates, LCP can be very late — sometimes 5+ seconds.
Run any URL through Site Speed Check and the report shows the LCP element, the LCP time, and a score against Google's thresholds.
The Four Causes of Slow LCP
Google's documentation breaks LCP delays into four phases:
- Time to First Byte (TTFB) — server takes too long to respond.
- Resource load delay — the LCP resource (image, font, etc.) is discovered late by the browser.
- Resource load duration — the LCP resource takes a long time to download once it's been discovered.
- Element render delay — the browser has the resource but takes time to actually display it (typically due to render-blocking JS or CSS).
Identifying which of the four is your bottleneck is the first step. The fixes for each are different.
Fix 1: Slow Server (TTFB)
If TTFB exceeds 600ms, no amount of front-end optimisation will get LCP under 2.5s. Common causes and fixes:
- No CDN. Adding Cloudflare or similar typically halves TTFB by serving from edge nodes geographically close to visitors.
- No page caching. Dynamic CMSes (WordPress, Drupal) querying databases on every request. Add a full-page cache (Cloudflare, Varnish, WP Rocket).
- Slow database queries. N+1 queries, missing indexes, expensive joins. Profile and optimise.
- Underpowered hosting. Shared hosting plans struggle under load. Upgrade or move to managed hosting.
- Distance from origin. If you're in Australia and the server is in the US, every request adds 200ms of latency. Use a CDN or move the origin closer.
Fix 2: Late Discovery of LCP Resource
If the LCP element is an image inserted by JavaScript, the browser doesn't know about it until the JS runs — by which time precious milliseconds have passed. The fix is to make the LCP resource discoverable as early as possible:
- Use a regular
<img>tag, not a JS-injected image. The browser's preload scanner finds<img>tags in the HTML before any JS runs. - Add
fetchpriority="high"to the LCP image. New attribute that tells the browser "fetch this before everything else."<img src="hero.jpg" fetchpriority="high" alt="..." /> - Preload the image from the head of the document:
<link rel="preload" as="image" href="hero.jpg" fetchpriority="high"> - Avoid lazy-loading the LCP image.
loading="lazy"on the LCP image is one of the most common bugs we see — it tells the browser to defer loading, dramatically delaying LCP. Lazy-load only images below the fold.
Fix 3: Resource Takes Too Long to Download
The browser found the LCP resource quickly, but it's just a heavy file. Reduce the size:
- Choose modern formats. WebP is 25-35% smaller than JPEG at equivalent quality; AVIF is even smaller (50%+ vs JPEG) but newer. Use
<picture>elements with format fallbacks.<picture> <source srcset="hero.avif" type="image/avif"> <source srcset="hero.webp" type="image/webp"> <img src="hero.jpg" alt="..."> </picture> - Resize to actual display dimensions. An image displayed at 800×600 doesn't need a 4000×3000 source. Server-side or CMS-level image resizing typically cuts file size by 70%+.
- Compress aggressively. Aim for under 200KB for hero images. Tools like ImageOptim, Squoosh, or build-step plugins (sharp, imagemin) help.
- Use responsive images.
srcsetserves smaller images to smaller screens. A mobile user shouldn't download the desktop hero image.<img src="hero-1200.jpg" srcset="hero-600.jpg 600w, hero-1200.jpg 1200w" sizes="100vw" alt="..."> - Use a CDN with image transformation. Cloudflare Images, Imgix, Cloudinary all serve appropriately sized and formatted images on the fly.
Fix 4: Render Delay
The image is downloaded but the browser is busy with other work — typically blocked on JavaScript or CSS — and can't actually paint the LCP element. Fixes:
- Defer non-critical JS. Add
deferorasyncattributes to script tags so they don't block the parser.<script src="analytics.js" defer></script> - Inline critical CSS. The CSS needed to render above-the-fold content should be inlined in the head; everything else loaded asynchronously.
- Reduce JavaScript bundle size. Code-split, tree-shake, and remove unused libraries. A 1MB JS bundle takes meaningful time to download, parse, compile, and execute on slower devices.
- Eliminate render-blocking third-party scripts. Live chat widgets, analytics, ad networks, A/B test scripts. Each one delays LCP. Audit and remove or defer.
- Avoid hydration-as-LCP patterns in SPAs. If your React/Vue app renders nothing until hydration, LCP is awful. Use SSR (Next.js, Nuxt, SvelteKit) so the LCP element is in the initial HTML.
Common LCP Anti-Patterns
Hero image lazy-loaded
loading="lazy" on the above-the-fold hero. The browser explicitly defers loading. LCP suffers proportionally.
Massive hero images at desktop dimensions, served to mobile
3000-pixel-wide hero serving to a 400-pixel-wide phone. Most of the bytes are wasted on pixels the user can't see.
Web fonts blocking text render
Custom font on the LCP heading. Without font-display: swap, the heading is invisible until the font loads — pushing LCP into the seconds.
@font-face {
font-family: 'Custom';
src: url('custom.woff2') format('woff2');
font-display: swap; /* show fallback text immediately */
}
Background-image hero hidden behind CSS
Hero is a CSS background-image on a <div>. Browsers don't preload background images during HTML parsing — only after CSS has loaded and computed. Major LCP delay. Switch to a regular <img> element.
SPA with empty initial HTML
The initial HTML is just a <div id="root"></div>. Everything renders client-side. LCP is gated on JavaScript downloading, parsing, executing, and finally painting. Always 2-5 seconds. Use SSR for content pages.
Render-blocking analytics, A/B testing, or chat scripts
A 200KB analytics SDK loaded synchronously in the head blocks parser and rendering. Every script in the head should be deferred or async unless absolutely required for first paint.
The Diagnostic Workflow
- Run Site Speed Check on the URL — instant LCP measurement and identification of the LCP element.
- Open Chrome DevTools Performance panel, reload the page, and look at the LCP marker. The waterfall shows exactly which resource is the LCP element and what was happening at each phase.
- Run Lighthouse for actionable suggestions ranked by potential LCP improvement.
- Use PageSpeed Insights to see field data (real users) alongside lab data — field data is what actually counts for Google rankings.
- Apply fixes from this guide based on which phase is the bottleneck.
- Re-test. Lab data updates immediately; field data takes 28 days to fully update in Google's reports.
The 80/20
For most sites, three changes account for the bulk of LCP improvement:
- Add a CDN with full-page caching → fixes TTFB.
- Use modern image formats with proper sizing → fixes resource load duration.
- Defer non-critical JS → fixes render delay.
Apply those three and most LCP problems resolve. The advanced techniques (preload, fetchpriority, picture elements, font-display) are for the next 5-10% of improvement once the basics are in place.
Site Speed Check measures LCP and the other Core Web Vitals on any URL. Apply fixes, re-test, watch the number drop. The threshold to aim for is 2.5 seconds — once you're under, Google rewards you with the "good" classification, and the rankings follow.