Largest Contentful Paint measures when the largest visible image or text block finishes rendering in the viewport. To improve LCP, find the actual LCP element, separate its loading time into useful phases, fix the phase causing the delay, and retest the deployed page under the same conditions.
This guide goes deeper than the Core Web Vitals overview for developers. It focuses on diagnosing a slow LCP element, improving resource discovery and rendering, and keeping a production route from regressing after release.
Largest Contentful Paint checklist
Use this short pass on the public URL you plan to ship:
- Check field LCP at the 75th percentile for mobile and desktop when enough data exists.
- Record a clean mobile load in Chrome DevTools and identify the LCP element.
- Break the result into time to first byte, resource load delay, resource load duration, and element render delay.
- Make the LCP resource discoverable in the initial HTML and give it appropriate priority.
- Reduce the image, font, CSS, JavaScript, or server delay responsible for the longest phase.
- Repeat the same test with a cold cache and record the URL, viewport, and build revision.
- Monitor field data after deployment and keep a bounded lab regression check in CI.
Google's Core Web Vitals guidance defines a good LCP as 2.5 seconds or less. For field data, assess the 75th percentile of page visits and separate mobile from desktop. More than 2.5 seconds through 4.0 seconds needs improvement; more than 4.0 seconds is poor. LCP is one page-experience signal, not a guarantee of search placement or conversion.
What Largest Contentful Paint measures
LCP reports the render time of the largest eligible content element seen in the viewport, measured from the start of the navigation. The candidate is often a hero image, a video poster, a large heading, or a text block. As larger content renders, the browser can report a new candidate; the latest candidate before qualifying user input becomes the result for that document load.
The W3C Largest Contentful Paint specification defines the browser API and its limits. LCP is a heuristic for the point when the main visible content appears. It does not prove that the page is interactive, visually stable, accessible, or useful.
Do not assume the design's largest source asset is the LCP element. The answer changes with viewport size, responsive image selection, logged-in state, experiments, and content. A desktop hero image may be replaced by a large text block on mobile. Test the route and breakpoint that matter.
Test Largest Contentful Paint in field and lab data
Field and lab LCP answer different questions:
- Field LCP describes real visits across devices, networks, caches, redirects, and content variants.
- Lab LCP reproduces one controlled load so a developer can inspect the responsible element and request chain.
Start with field data when the route has enough traffic, then reproduce a slow case in the lab. A new or low-traffic page may not have field data. A fast laptop on a warm cache may also produce a good lab result while real mobile visits remain slow.
Chrome's LCP documentation explains that field LCP includes earlier navigation work such as redirects, connection setup, and time to first byte. Record the final URL, viewport, cache state, network and CPU settings, and build revision with every lab result so the next run is comparable.
Find the LCP element before changing code
Open the deployed page in Chrome DevTools, select Performance, and record a reload. Select the LCP marker in the timeline, then inspect the element and the timing breakdown. Chrome's Performance insights documentation shows where the LCP element, request, and four timing phases appear.
For a focused Chromium debugging pass, use PerformanceObserver to log the latest candidate:
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
const latest = entries.at(-1);
if (!latest) return;
console.table({
time: Math.round(latest.startTime),
element: latest.element?.tagName,
id: latest.id || "(none)",
url: latest.url || "(text or unavailable)",
size: latest.size,
});
});
observer.observe({ type: "largest-contentful-paint", buffered: true });
The `LargestContentfulPaint` API reference on MDN documents the entry fields and current browser support. Use this snippet as debugging evidence, not as a cross-browser field measurement system. The API can stop adding candidates after user input, and a same-document application navigation does not automatically become a fresh document load.
Break the LCP score into four phases
Treating LCP as one number often leads to the wrong fix. The web.dev LCP optimization guide divides the metric into four phases that add up to the final time:
- Time to first byte (TTFB): navigation start until the first byte of the HTML response.
- Resource load delay: first byte until the browser starts requesting the LCP resource.
- Resource load duration: time spent downloading that resource.
- Element render delay: resource completion until the element is painted.
A text LCP rendered with a system font has no image download phase, but it can still wait on CSS, client rendering, or main-thread work. An image can download quickly and still paint late because a stylesheet hides it or JavaScript delays the component. Optimize the longest observed phase instead of applying every performance technique at once.
Improve LCP resource discovery and priority
When the LCP element is an image, let the browser discover it from the initial HTML. Avoid inserting it only after a client-side request, hiding its URL in JavaScript, or marking it for lazy loading. Use responsive sources and truthful dimensions so the browser can choose the right file early:
<img
src="/images/audit-hero-1280.webp"
srcset="/images/audit-hero-640.webp 640w,
/images/audit-hero-1280.webp 1280w"
sizes="(max-width: 720px) 100vw, 60vw"
width="1280"
height="800"
fetchpriority="high"
alt="Website audit report with prioritized launch findings"
>
Reserve fetchpriority="high" for a likely LCP image instead of applying it to every image. If the browser cannot discover the resource in the HTML, such as a critical CSS background image, a matching preload can start the request earlier. Verify the Network panel after adding a preload so you do not create duplicate downloads or prioritize a resource that is not in the first viewport.
Compress the chosen image and serve dimensions close to its rendered size. The website image optimization checklist covers srcset, formats, intrinsic dimensions, and alternative text in detail. Image compression reduces resource load duration; it will not solve a long server delay or a component that waits several seconds before rendering.
Reduce server and element render delay
If TTFB dominates, inspect the document request before touching the hero component. Remove avoidable redirects, cache public HTML where the page contract allows it, move slow data work out of the critical response, and measure the production origin rather than a local server. Keep the first HTML useful so the browser can discover critical CSS, fonts, and images without waiting for the application bundle.
If element render delay dominates, look for work between resource completion and paint:
- render-blocking CSS that can be reduced or delivered earlier;
- a custom font that keeps a text LCP invisible;
- a client-rendered hero waiting on JavaScript or a second API request;
- a loading screen covering content that is already available;
- long main-thread tasks delaying style, layout, and paint.
Prefer server-rendered or static markup for the primary page promise when practical. Defer scripts and third-party widgets that are not needed for the first view. For a text LCP, use a font-loading strategy that keeps useful text visible while the preferred font arrives. Confirm the effect in a new trace; removing bytes from an unrelated bundle is not evidence that LCP improved.
Add a Largest Contentful Paint regression check
A release check should run against a deployed preview or production-like URL. Local development mode can change asset sizes, caching, and rendering behavior. This Playwright example captures the final exposed LCP candidate during one Chromium load:
await page.addInitScript(() => {
window.__lcp = null;
new PerformanceObserver((list) => {
const latest = list.getEntries().at(-1);
if (latest) window.__lcp = latest.startTime;
}).observe({ type: "largest-contentful-paint", buffered: true });
});
const targetUrl = process.env.PREVIEW_URL;
if (!targetUrl) throw new Error("PREVIEW_URL is required");
await page.goto(targetUrl, { waitUntil: "load" });
await page.waitForTimeout(1000);
const lcp = await page.evaluate(() => window.__lcp);
if (lcp === null || lcp > 2500) {
throw new Error(`LCP was ${lcp ?? "not observed"}ms`);
}
One headless run is noisy. Use a stable runner, fixed viewport, explicit cache policy, and several samples before making a strict gate. Save the LCP element or trace when possible so a failure leads to a concrete fix. The website regression testing checklist explains how to keep a reviewable baseline, and the GitHub Actions website audit guide covers attaching deployed-page evidence to a pull request.
Check Largest Contentful Paint with Slop Audit
Run a Slop Audit on the public preview or production URL after deployment. When Browser Run is available, Slop Audit records the final LCP entry exposed during its bounded 375 × 812 mobile load. It reports the measured time as evidence and classifies 2.5 seconds or less as a pass, above 2.5 through 4.0 seconds as a warning, and above 4.0 seconds as a failure.
That evidence is a lab observation from one viewport and run. It does not identify the LCP element, simulate the distribution of real user conditions, replace Chrome UX Report data, or certify Core Web Vitals. Use it to catch an obvious launch regression, reproduce the result in DevTools, fix the longest LCP phase, and rerun the same URL.
LCP release checklist
- Confirm the test names the final deployed URL and build revision.
- Separate field LCP from controlled lab results.
- Identify the LCP element at mobile and desktop breakpoints.
- Inspect TTFB, resource load delay, resource load duration, and element render delay.
- Keep the LCP image discoverable in HTML and do not lazy-load it.
- Size and compress the image selected for each viewport.
- Keep critical text visible while fonts and application code load.
- Defer non-critical CSS, JavaScript, and third-party work.
- Repeat the same cold-load conditions and compare several samples.
- Monitor field data after release for devices and visits a bounded test cannot represent.
After LCP is stable, use the Cumulative Layout Shift guide to make sure the same hero, font, or async component does not move visible content while it loads, then complete the mobile website testing guide around the page's primary task.