Largest Contentful Paint: Complete Optimization Guide
Largest Contentful Paint (LCP) measures how long it takes for the largest visible content element to render on screen. It is the most impactful of the three Core Web Vitals for user perception — a page that renders its main content within 2.5 seconds feels "fast," regardless of what happens afterward. Failing LCP, on the other hand, makes users feel the page is broken before it has begun.
Optimizing LCP requires a systematic approach. The metric is influenced by server infrastructure, network conditions, resource prioritization, and rendering pipeline efficiency. This guide decomposes LCP into its four sub-parts and provides concrete optimization strategies for each.
Understanding LCP Sub-Parts
Every LCP measurement can be broken down into four sequential phases. Each phase has different causes and different solutions. Treating LCP as a single number to optimize is like treating a fever without diagnosing the infection — you need to know where the time is being spent.
Sub-Part 1: Time to First Byte (TTFB)
TTFB measures the time from the navigation request to the first byte of the HTML response arriving at the browser. This includes DNS resolution, TCP/TLS handshake, server processing time, and network latency. For most pages, TTFB is the single largest component of LCP.
TTFB optimization strategies include:
- CDN deployment: Serving content from edge nodes geographically close to users eliminates cross-continental network latency. A page served from a CDN PoP 20ms away will have fundamentally different TTFB than one served from an origin server 200ms away.
- Server-side caching: Full-page caching, fragment caching, and database query caching reduce the work the origin server performs per request. A cache hit that returns HTML in 5ms versus a database-backed render taking 800ms is the difference between passing and failing LCP.
- Connection reuse: HTTP/2 and HTTP/3 enable multiplexing, eliminating the per-request connection overhead. HTTP/3's QUIC protocol further reduces handshake latency by combining transport and TLS negotiation into a single round trip.
- Efficient server-side rendering: Optimizing database queries, using connection pooling, and streaming HTML responses can dramatically reduce server processing time.
For a comprehensive treatment of TTFB optimization, see our dedicated server response time optimization guide.
Sub-Part 2: Resource Load Delay
Resource load delay is the gap between TTFB and the moment the browser begins downloading the LCP resource (typically a hero image). This gap exists because the browser must parse HTML to discover the resource. If the image is referenced deep in the HTML, or worse, loaded via a CSS background property or JavaScript, discovery is delayed.
The primary tool for eliminating resource load delay is the preload hint:
<!-- Place in <head> for immediate discovery -->
<link rel="preload" as="image" href="/hero-image.webp"
fetchpriority="high">
<!-- For responsive images -->
<link rel="preload" as="image" href="/hero-lg.webp"
media="(min-width: 800px)">
<link rel="preload" as="image" href="/hero-sm.webp"
media="(max-width: 799px)">
The fetchpriority="high" attribute tells the browser to prioritize this resource over others discovered at the same time. Without it, the browser's default heuristics may deprioritize the image below stylesheets and scripts.
Sub-Part 3: Resource Load Duration
Once discovered, the LCP resource must be downloaded. The duration depends on the resource's byte size and the available bandwidth. Optimization focuses on reducing the resource size without sacrificing visual quality:
- Modern image formats: WebP typically achieves 25-35% smaller file sizes than JPEG at equivalent quality. AVIF pushes further with 50% reductions, though encoding is slower and browser support is narrower.
- Responsive images: Serving appropriately sized images via
srcsetandsizesprevents mobile devices from downloading desktop-sized assets. A 400px-wide viewport does not need a 2000px-wide image. - Compression: Brotli compression (supported by all modern browsers) achieves 15-25% better compression ratios than gzip for text-based resources like HTML, CSS, and JavaScript.
<img src="/hero-800.webp"
srcset="/hero-400.webp 400w,
/hero-800.webp 800w,
/hero-1200.webp 1200w"
sizes="(max-width: 640px) 100vw,
(max-width: 1024px) 80vw,
800px"
width="800" height="450"
alt="Descriptive alt text"
fetchpriority="high">
Sub-Part 4: Element Render Delay
After the resource downloads, the browser must still calculate styles, perform layout, and paint the element to the screen. Render delay is typically small (under 10ms) unless render-blocking resources prevent the browser from reaching the paint step.
Common causes of render delay:
- Render-blocking CSS: The browser will not paint any content until all render-blocking stylesheets have been downloaded and parsed. Large CSS files or slow-loading font stylesheets directly increase render delay.
- Render-blocking JavaScript: Synchronous
<script>tags in the<head>block HTML parsing and delay rendering. Usingdeferorasyncattributes prevents this. - Client-side rendering: SPAs that rely on JavaScript to render their initial content must download, parse, and execute the JS bundle before any LCP candidate element exists in the DOM.
Image Optimization for LCP
On most web pages, the LCP element is an image — a hero banner, a product photo, or a featured article thumbnail. Image optimization is therefore the highest-leverage intervention for LCP improvement.
Format Selection
| Format | Best For | Compression | Browser Support |
|---|---|---|---|
| WebP | General-purpose photos and illustrations | 25-35% smaller than JPEG | 97%+ global support |
| AVIF | High-quality photos where encoding time is acceptable | 50% smaller than JPEG | ~90% support (no IE, older Safari) |
| JPEG | Fallback for legacy browsers | Baseline | Universal |
| PNG | Graphics requiring transparency | Larger than JPEG for photos | Universal |
| SVG | Icons, logos, simple illustrations | Resolution-independent | Universal |
The <picture> element enables format negotiation:
<picture>
<source srcset="/hero.avif" type="image/avif">
<source srcset="/hero.webp" type="image/webp">
<img src="/hero.jpg" width="1200" height="675"
alt="Description" fetchpriority="high">
</picture>
Critical Image Loading Pattern
The optimal loading pattern for the LCP image combines several techniques:
- Preload the image in
<head>withfetchpriority="high" - Use the
<picture>element with modern format sources - Set explicit
widthandheightto prevent layout shift - Do not use
loading="lazy"on the LCP image - Do not use CSS
background-imagefor the LCP element
Common mistake: Applying loading="lazy" globally to all images, including the LCP element. Lazy loading delays discovery of below-the-fold images (good), but also delays the above-the-fold LCP image (bad). Only lazy-load images that are initially outside the viewport.
Render-Blocking Resources
Every resource that blocks rendering adds directly to the render delay sub-part. The browser's rendering pipeline works in a strict sequence: it cannot paint until the DOM is constructed and all render-blocking CSS is parsed into the CSSOM.
CSS Optimization
- Inline critical CSS: Extract the CSS required for above-the-fold content and inline it in a
<style>tag in the<head>. This eliminates the round trip required to fetch an external stylesheet before first paint. - Defer non-critical CSS: Load below-the-fold CSS asynchronously using the
mediaattribute trick orrel="preload"with anonloadhandler. - Remove unused CSS: Large CSS files (common with CSS frameworks) force the browser to parse rules that apply to zero elements on the current page. Tools like PurgeCSS can remove unused selectors during the build process.
JavaScript Optimization
JavaScript affects LCP in two ways: synchronous scripts block HTML parsing (delaying DOM construction and resource discovery), and heavy JavaScript execution competes for the main thread (delaying rendering even after the LCP resource is loaded).
- Use
deferfor scripts that need DOM access but can wait until after parsing - Use
asyncfor independent scripts (analytics, ads) that do not depend on DOM order - Move third-party scripts below the fold or load them after the LCP event
- Split large bundles with code-splitting to reduce the initial JavaScript payload
Server-Side Rendering Strategies
For applications that use client-side frameworks (React, Vue, Angular), the default client-side rendering (CSR) pattern is inherently hostile to LCP. The browser receives a minimal HTML shell, downloads a JavaScript bundle, executes it, fetches data, and only then renders the actual content. The LCP element does not exist in the DOM until JavaScript runs.
SSR and Streaming
Server-side rendering (SSR) generates complete HTML on the server, allowing the browser to display the LCP element as soon as the HTML arrives — no JavaScript execution required for initial render. Modern frameworks support streaming SSR, which sends HTML chunks as they are generated, allowing the browser to begin parsing and rendering before the server finishes generating the full response.
Static Site Generation (SSG)
For content that does not change per request (blog posts, marketing pages, documentation), static site generation produces HTML at build time. These static files can be served directly from a CDN edge cache with near-zero TTFB, making SSG the optimal architecture for LCP on content-heavy sites.
Font Loading and LCP
Web fonts can affect LCP when the LCP element is a text block. If the browser waits for the custom font to load before rendering text, the LCP measurement includes the font download time. The font-display property controls this behavior:
| Value | Behavior | LCP Impact |
|---|---|---|
swap | Show fallback font immediately, swap when custom font loads | Good — text is visible immediately |
optional | Use custom font only if already cached; otherwise use fallback permanently | Best — no font-related delay at all |
block | Hide text for up to 3 seconds while font loads | Poor — invisible text delays LCP |
fallback | Brief invisible period (~100ms), then fallback, then swap if loaded quickly | Moderate — small delay risk |
For LCP optimization, font-display: optional is ideal on repeat visits (the font is cached) and acceptable on first visits (the fallback font renders the LCP text immediately). Combine this with <link rel="preload" as="font" crossorigin> to prioritize font download.
Monitoring LCP in Production
Lab testing with Lighthouse gives you a baseline, but real user LCP values vary enormously based on device, network, and geography. Continuous monitoring through Real User Monitoring (RUM) or synthetic monitoring from multiple global locations is essential for catching regressions.
Key monitoring practices:
- Track p75 LCP at the page-level, not just origin-level — a single slow template can drag down the entire site's CrUX data
- Segment by device type (mobile vs. desktop) and connection type (4G vs. 3G vs. WiFi)
- Set up alerts when p75 LCP crosses 2.5s on any high-traffic page
- Integrate LCP tracking with your APM system to correlate backend latency with frontend paint timing
- Use distributed tracing to connect slow LCP values to specific backend bottlenecks
Key Takeaways
- LCP measures the render time of the largest visible content element — target ≤ 2.5s at p75
- Decompose LCP into four sub-parts (TTFB, resource load delay, resource load duration, render delay) to identify the bottleneck
- TTFB is usually the largest sub-part — CDN deployment and server-side caching are the highest-impact optimizations
- Preload the LCP image with
fetchpriority="high"and never lazy-load it - Use modern image formats (WebP/AVIF) with responsive
srcsetto reduce resource load duration - Eliminate render-blocking CSS and JavaScript to minimize render delay
- SSR or SSG produces faster LCP than client-side rendering for content-heavy pages
- Monitor LCP continuously with RUM, segmented by device and connection type