Largest Contentful Paint (LCP)
🖼️ Largest Contentful Paint (LCP)
What Counts as "Largest Content"
The browser considers specific element types as LCP candidates: <img> elements, an <svg>'s <image>, a <video>'s poster frame, CSS background images, and block-level elements containing text. LCP is whichever of these is largest and currently visible in the viewport — and that candidate can genuinely change as the page loads.
The Candidate Can Change Mid-Load
A text block might be the largest visible element until a hero image finishes loading — at that point, the image becomes the new LCP candidate, and the metric keeps updating until the browser reports a final value.
When Reporting Stops
The browser finalizes LCP at first user interaction (a scroll, click, or keypress) or page unload — whichever LCP candidate was largest at that point is the reported value.
⏱️ Breaking Down the LCP Timeline
LCP time splits into four independently optimizable sub-parts — Chrome DevTools' Performance panel shows this breakdown directly:
| Sub-part | What It Covers |
|---|---|
| TTFB | Time until the first byte of the HTML document arrives |
| Resource load delay | Time between TTFB and the browser starting to fetch the LCP resource |
| Resource load duration | How long the LCP resource itself takes to download |
| Render delay | Time between the resource finishing download and actually being painted |
🔍 Common Causes
Slow Server Response (TTFB)
Heavy server-side processing, geographic distance from a CDN edge, or an uncached dynamic response all push TTFB higher, delaying everything after it.
Render-Blocking Resources
CSS or synchronous JS in <head> blocks the browser from painting anything until it's downloaded and parsed — especially costly with large third-party stylesheets.
Slow-Loading LCP Element
An unoptimized hero image — wrong format, no compression, served from a slow origin instead of a CDN — directly inflates resource load duration.
Client-Side Rendering
If the LCP element only appears after a JS bundle downloads, parses, executes, fetches data, and renders, that critical path is far longer than server-rendered HTML with the image already in the initial markup.
Fixing Each Cause
Preloading the Critical Resource
Beyond preloading: serve through a CDN, optimize and compress images (Chapter 5), remove render-blocking CSS/JS from the critical path (defer/async, inlining critical CSS), and prefer server-side rendering or static generation over full client-side rendering for above-the-fold content.
Unoptimized vs Preloaded Hero Image
Unoptimized
Full-resolution JPEG, discovered only after CSS/JS parsing, served from origin — LCP stretches past 4 seconds.
Preloaded + Optimized
Compressed WebP, preloaded before the parser even reaches the <img> tag, served from a CDN edge — LCP comfortably under 2.5 seconds.
💻 Coding Challenges
Challenge 1: Diagnose Which Sub-Part Is Slow
A page's LCP timeline shows: TTFB 0.3s, resource load delay 2.1s, resource load duration 0.4s, render delay 0.1s. Identify which sub-part is the real problem and propose one concrete fix for it.
Goal: Practice using the four-part breakdown to target the actual bottleneck, not just the page as a whole.
Challenge 2: Explain a Changing LCP Candidate
A page shows a large text headline immediately, then a hero image loads two seconds later and becomes visually dominant. Explain what happens to the reported LCP element and value in this case.
Goal: Practice reasoning about the "candidate can change mid-load" behavior with a concrete example.
Challenge 3: Choose Between SSR and CSR for LCP
A marketing landing page's only job is to display a hero headline and image as fast as possible, with no personalization. Recommend server-side rendering or client-side rendering for this page's markup, and justify it using this chapter's causes section.
Goal: Practice connecting a rendering-strategy decision directly to LCP impact.
<link rel="preload"> is meant for the one truly critical resource — preloading many resources at once competes for the same limited early bandwidth and connection slots, and can actually delay the real LCP resource by competing with it for priority. If everything is marked as preloaded, nothing is actually prioritized — the browser has no way to tell which "critical" resource matters most. Preload the single LCP resource, not a long list of "important-ish" assets.
🎯 What's Next
With LCP covered, the next chapter turns to visual stability: Cumulative Layout Shift (CLS) — what causes content to jump around, and how to prevent it.