What You'll Learn
- The four sub-parts of LCP and how to measure each one independently
- How to reduce TTFB — the single biggest lever for LCP improvement
- How to eliminate resource load delay using preload hints and fetchpriority
- How to reduce resource load duration through image format, compression, and CDN
- How to eliminate render delay caused by blocking JavaScript and CSS
- Image-specific optimisation: WebP/AVIF, responsive images, lazy loading errors
- Font loading strategies that prevent LCP delay
- How to use Chrome DevTools to diagnose the exact cause of poor LCP
LCP Broken Into Its Four Sub-Parts
Most LCP guidance treats it as a single metric and offers generic advice. Google's official web.dev documentation breaks LCP into four sequential sub-parts, each of which can be independently measured and optimised. Understanding which sub-part is responsible for your poor LCP score is the difference between targeted optimisation and guesswork.
Phase 1
TTFB
Time from navigation start until first byte of HTML received from server
Phase 2
Resource Load Delay
Time from TTFB until browser starts loading the LCP resource
Phase 3
Resource Load Duration
Time to fully download the LCP resource (image, font, etc.)
Phase 4
Render Delay
Time from resource loaded until it is actually painted on screen
The total LCP time is the sum of all four phases. According to Google's analysis of real-world CrUX data, the distribution of LCP time is dominated by different sub-parts for different site types: TTFB is the largest component for server-rendered sites; resource load duration dominates for image-heavy sites with unoptimised assets; render delay is the primary issue for JavaScript-heavy single-page applications.
Measuring sub-parts in Chrome DevTools
Chrome DevTools Performance panel (run a recording) shows all four LCP sub-parts when you click the LCP marker in the timeline. The "Timings" row shows TTFB, FCP, and LCP. Expanding the LCP entry in the "Main" thread shows the breakdown. Alternatively, the web-vitals JavaScript library (web.dev/articles/debug-performance-in-the-field) can report sub-part breakdowns from real user sessions.
Good LCP threshold
75th percentile of real page visits must meet this
Largest sub-part (typical)
TTFB share of total LCP time on average pages
Target TTFB
Google's recommended maximum TTFB for good LCP
Phase 1: Reducing TTFB
Time to First Byte is the time between the browser's HTTP request for a page and the first byte of the HTML response being received. It is the foundation of LCP — nothing on the page can begin loading until the browser has the HTML. Google recommends a TTFB under 800ms for a good LCP score.
A CDN caches your pages and assets at edge servers geographically close to each user. For most sites, using a CDN is the single highest-impact TTFB improvement available. Without a CDN, every user's request travels to your origin server regardless of distance. With a CDN, cached responses are served from the nearest point of presence.
Implementation: Use CDN providers like Cloudflare, Fastly, Akamai, or AWS CloudFront. Configure full-page caching for static or semi-static pages. Verify Cache-Control headers are set correctly so the CDN retains your pages.
If your server generates pages dynamically on every request (e.g. querying a database), caching the rendered HTML at the server level means subsequent requests are served from memory rather than re-generated. This can reduce TTFB from 500ms–2s to under 100ms.
Implementation: Page caching plugins for CMS platforms (WP Super Cache, W3 Total Cache for WordPress); Redis or Memcached for application-level caching; full-page caching at the reverse proxy level (Nginx, Varnish).
Shared hosting with insufficient CPU or memory allocation produces consistently high TTFB under load. Moving to a VPS, dedicated server, or managed cloud hosting is often necessary for sites with significant traffic. Database query optimisation (indexes, query caching) directly reduces dynamic page generation time.
HTTP 103 Early Hints is a response status that allows the server to send Link: rel=preload headers before the full 200 response is ready. This enables the browser to begin fetching critical resources (fonts, CSS, the LCP image) while the server is still generating the main response — effectively running Phase 2 in parallel with Phase 1.
Chrome has supported Early Hints since version 103. Server support requires configuration at the CDN or web server level — Cloudflare, Apache, and Nginx all support Early Hints.
Phase 2: Eliminating Resource Load Delay
Resource load delay is the time between the first byte of HTML being received and when the browser actually starts downloading the LCP resource. The ideal value is zero — the LCP resource should begin downloading as early as possible. Any delay here means the browser discovered the LCP resource later than necessary.
If the LCP element is an image (the most common case), add a <link rel="preload"> hint in the <head> of your HTML. This tells the browser to fetch the resource immediately during HTML parsing, before it has built the DOM or render tree.
<link rel="preload"
href="/images/hero.webp"
as="image"
fetchpriority="high">
For responsive images that vary by screen size, use the imagesrcset and imagesizes attributes on the preload link:
<link rel="preload"
as="image"
href="/images/hero-1200.webp"
imagesrcset="/images/hero-640.webp 640w, /images/hero-1200.webp 1200w"
imagesizes="100vw"
fetchpriority="high">
The fetchpriority attribute on <img> elements signals to the browser that this resource should be fetched at highest priority. Without it, the browser may deprioritise hero images relative to scripts and stylesheets.
<img src="/images/hero.webp"
alt="Hero image"
width="1200"
height="600"
fetchpriority="high">
Critical: Only add fetchpriority="high" to the LCP image. Adding it to multiple images defeats the purpose — the browser can only prioritise one resource. Adding it to non-LCP images will actually hurt LCP by diverting bandwidth.
loading="lazy" on the LCP image is one of the most common and impactful LCP mistakes. Lazy loading defers image loading until the user scrolls near the image — for the LCP element, which is above the fold, this delay is unnecessary and directly adds to LCP time.
Only use loading="lazy" on images that are below the fold. The LCP image, hero images, and any above-the-fold images should either have loading="eager" (the default) or no loading attribute at all.
Phase 4: Eliminating Render Delay
Render delay is time lost between the LCP resource finishing its download and the browser actually painting it on screen. The ideal render delay is zero. Common causes include render-blocking CSS and JavaScript that prevent the browser from painting even after resources are available, and client-side rendering frameworks that require JavaScript execution before any content appears.
CSS stylesheets in the <head> are render-blocking by default — the browser pauses rendering until all CSS is downloaded and parsed. JavaScript files with no async or defer attribute in the <head> are also render-blocking.
For CSS: Inline critical CSS (the styles needed to render above-the-fold content) directly in the <head>. Load the full stylesheet with media="print" and switch it back to all after load (a common pattern for non-critical CSS deferral):
<!-- Critical CSS inline -->
<style>/* above-fold styles here */</style>
<!-- Non-critical CSS deferred -->
<link rel="stylesheet" href="/styles.css"
media="print" onload="this.media='all'">
For JavaScript: Add defer to all non-critical scripts. Use async only for truly independent scripts (analytics, ads). Move scripts to the end of <body> if refactoring is not possible.
Single-page applications (React, Vue, Angular) that render content entirely on the client side have render delay as a structural problem — the LCP element cannot paint until JavaScript downloads, parses, and executes. This often adds 1–3 seconds of render delay.
Solutions: Server-side rendering (SSR) or static site generation (SSG) ensures the HTML delivered to the browser already contains the content. Streaming SSR (React 18+) sends HTML progressively, reducing TTFB and enabling earlier LCP. Partial hydration strategies can reduce JavaScript execution time for above-fold content.
Phase 3: Image Optimisation for LCP
When the LCP element is an image (the most common case — hero images, featured images, product images), resource load duration is determined by the image file size and the user's available bandwidth. Reducing file size without visible quality loss is the primary lever.
WebP provides 25–35% smaller file sizes than JPEG at equivalent visual quality. AVIF (AV1 Image Format) provides 50%+ reduction compared to JPEG, though encoding is slower and browser support, while now broad, is slightly less universal than WebP. Use the <picture> element with a JPEG fallback for maximum compatibility:
<picture>
<source srcset="/hero.avif" type="image/avif">
<source srcset="/hero.webp" type="image/webp">
<img src="/hero.jpg" alt="Hero"
width="1200" height="600" fetchpriority="high">
</picture>
Serving a 3000×2000px image for a hero that displays at 800×533px on desktop (and smaller on mobile) wastes significant bandwidth. Use responsive images with srcset and sizes to serve appropriately sized images for each viewport:
<img
srcset="/hero-640.webp 640w, /hero-1024.webp 1024w, /hero-1600.webp 1600w"
sizes="(max-width: 768px) 100vw, 1200px"
src="/hero-1600.webp"
alt="Hero"
width="1600" height="900"
fetchpriority="high">
This ensures mobile users on 360px-wide screens download a ~360px image rather than a 1600px image — a 20x file size reduction.
Width and height attributes prevent layout shifts (CLS) and allow the browser to allocate space for the image before it loads — enabling painting to begin sooner. Always add explicit width and height matching the image's intrinsic dimensions. Use CSS to make the image responsive, not the HTML attributes:
img { max-width: 100%; height: auto; }
If your LCP element is a CSS background image (set via background-image: url()), the browser cannot discover and begin downloading it during initial HTML parsing — it must parse the CSS first. This adds significant resource load delay. Where possible, use an <img> element instead of a CSS background for the LCP image. If you must use a CSS background, add a <link rel="preload" as="image"> hint for it in the <head>.
Font Loading and LCP
When the LCP element is a text block (the second most common case — large headings, hero text), web font loading becomes a critical LCP factor. If the LCP text renders using a web font that loads late, the text is invisible until the font arrives — directly adding to LCP time.
Add a preload hint for the font used in the LCP text element. Specify crossorigin (required for fonts even from the same origin):
<link rel="preload"
href="/fonts/Inter-Bold.woff2"
as="font"
type="font/woff2"
crossorigin>
font-display: optional prevents any font-related render delay — if the font is not available in cache, the browser uses a fallback immediately without waiting. This eliminates font-related LCP delay but means some users see a fallback font on first visit. font-display: swap shows fallback text immediately and swaps to the web font when it loads — this can cause CLS if the font metrics differ significantly from the fallback.
For LCP text elements, font-display: optional combined with font preloading is the most LCP-friendly configuration: returning visitors (with the font cached) get the intended font with no delay; first visitors get immediate text rendering with a system font.
Fonts loaded from Google Fonts (fonts.googleapis.com) require a cross-origin connection — an additional DNS lookup, TCP connection, and TLS handshake before the font download can begin. Self-hosting fonts on your own domain eliminates this cross-origin overhead. For LCP-critical fonts, self-hosting with a preload hint is consistently faster than Google Fonts.
Third-Party Scripts and LCP
Third-party scripts — analytics platforms, chat widgets, ad networks, tag managers, social embeds — are among the most common causes of poor real-world LCP. They consume bandwidth and main thread time, directly competing with the LCP resource for loading and rendering priority.
How third-party scripts harm LCP
- Bandwidth competition. HTTP/2 multiplexing allows parallel connections, but bandwidth is still finite. Multiple third-party scripts downloading simultaneously compete with the LCP resource.
- Main thread blocking. JavaScript execution is single-threaded. Any synchronous third-party script in
<head>blocks parsing and rendering until it executes. - Render-blocking. Third-party CSS loaded via
<link rel="stylesheet">blocks rendering in the same way as first-party CSS. - DNS and connection latency. Each unique third-party domain requires a new DNS lookup and connection. Preconnecting to critical third-party origins helps:
<link rel="preconnect" href="https://analytics.example.com">.
Audit and reduce third-party impact
Use Chrome DevTools Network panel with the "Third-party" filter. Sort by size and duration to identify the most expensive third-party requests. Consider: removing scripts that are not business-critical, loading non-critical scripts after the page becomes interactive (using a facade pattern for chat widgets, loading analytics after user interaction), and using Google Tag Manager's trigger configuration to fire tags only when needed rather than on every page view.
Measuring and Debugging LCP
| Tool | Data Type | Best For |
|---|---|---|
| Google Search Console — Core Web Vitals | Field (CrUX) | Understanding real user LCP distribution; identifying worst-performing page groups |
| PageSpeed Insights | Field + Lab | Per-URL diagnosis; combines real data with diagnostic recommendations |
| Chrome DevTools — Performance | Lab | Identifying the exact LCP element and which sub-part is largest |
| WebPageTest | Lab | Waterfall analysis; testing from multiple locations; filmstrip view showing when LCP element paints |
| Lighthouse (CLI/DevTools) | Lab | Automated recommendations; CI/CD integration for regression prevention |
| web-vitals JS library | Field (custom) | Collecting LCP sub-part breakdowns from real users in your own analytics |
Diagnosing LCP in Chrome DevTools step-by-step
- Open Chrome DevTools → Performance tab
- Check "Screenshots" and optionally "Web Vitals"
- Click the circular Record button, then reload the page
- Stop recording after the page fully loads
- In the Timings row, find the "LCP" marker — click it
- The panel below shows which element was the LCP element and its timestamp
- Examine the waterfall to find that element's resource request — identify when it was requested, when it started downloading, and when it finished
- The gap between navigation start and the request start = TTFB + resource load delay
- The download bar length = resource load duration
- The gap between download end and the LCP timestamp = render delay
Authentic Sources
Official LCP optimisation guide including the four sub-part framework.
Official LCP definition, eligible elements, and threshold values.
Official documentation on the fetchpriority attribute and its impact on LCP.
Official guidance on using preload with imagesrcset for responsive LCP images.
Official documentation on HTTP 103 Early Hints and its impact on TTFB and LCP.
Official guidance on font loading strategies and their impact on LCP and CLS.