Largest Contentful Paint (LCP)

Performance & Core Web Vitals — Largest Contentful Paint (LCP)
Performance & Core Web Vitals
Chapter 2 · Largest Contentful Paint (LCP)

🖼️ Largest Contentful Paint (LCP)

Chapter 1 introduced LCP at a high level. This chapter goes deep — what actually counts as "largest content," a four-part timeline for diagnosing exactly where the time goes, and the concrete fixes for each cause.

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-partWhat It Covers
TTFBTime until the first byte of the HTML document arrives
Resource load delayTime between TTFB and the browser starting to fetch the LCP resource
Resource load durationHow long the LCP resource itself takes to download
Render delayTime 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

<link rel="preload" as="image" href="/hero.webp" fetchpriority="high">

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.

→ Solution

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.

→ Solution

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.

→ Solution

⚠️ Gotcha: Preloading Too Many Resources

<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.