DEV Community

137Foundry
137Foundry

Posted on

Why Your LCP Element Usually Isn't What You Think It Is

Ask most developers what their Largest Contentful Paint element is, and they'll guess the hero image, because that's usually the biggest visual thing on the page. More often than not, they're wrong, and the actual answer changes what you should be optimizing.

LCP Is About Rendered Size, Not File Size

The "largest" in Largest Contentful Paint refers to the rendered viewport size of the element, not its file size or visual prominence. A full-width background image that renders small on mobile can lose to a large block of text or a wide, unstyled <div> containing a background color, if that element's rendered dimensions happen to be bigger within the viewport at paint time.

This distinction trips people up constantly. Optimizing image compression on a hero image that isn't actually the LCP candidate does nothing for your Core Web Vitals score, even though it's a perfectly reasonable thing to do for other reasons like bandwidth savings.

How to Actually Find Your LCP Element

Chrome DevTools' Performance panel identifies the exact LCP candidate in any recorded trace, labeled directly in the timeline. This is the ground truth, not a guess based on what looks biggest. Run a trace on the actual page in question, in both mobile and desktop viewport sizes separately, since the LCP element frequently differs between the two, a text block might win on mobile while an image wins on a wider desktop layout.

Browser developer tools panel showing a performance trace timeline
Photo by AS Photography on Pexels

Common Surprises

A few patterns show up repeatedly once teams start actually checking instead of assuming:

  • A <h1> or large paragraph block wins over a smaller image, especially on text-heavy pages like articles or blog posts, where the image is often below the headline and subheading.
  • A background-image CSS property doesn't count as an LCP candidate at all in most cases, only elements loaded via <img>, <video> poster frames, or elements with background-image set via inline style in some browser implementations. A background image applied through an external stylesheet is frequently invisible to LCP measurement entirely, which surprises teams who spent time optimizing exactly that image.
  • Dynamically injected content can become the LCP element if it renders large enough and loads before whatever the developer assumed would be the candidate, which is common with client-side rendered frameworks where the initial HTML is minimal and content populates after JavaScript executes.

Why This Matters for Optimization Priority

Once you know the actual LCP element, optimization effort has a clear target. If it's a text block, the question becomes about font loading and render-blocking resources delaying text paint, not image compression at all. If it's genuinely an image, then format choice, compression, and preloading are exactly the right levers. Optimizing the wrong element wastes engineering time on changes that will never move the metric that's actually being measured and reported to Search Console.

web.dev has detailed documentation on exactly which element types qualify as LCP candidates and under what conditions, which is worth a careful read since the qualifying rules have specific edge cases around CSS background images and dynamically loaded content that aren't intuitive from the name alone.

Server-Side Rendering Changes This Calculation Too

Pages that render meaningfully on the server versus those that rely on client-side JavaScript to populate content will often have very different LCP candidates, even for visually identical final pages. A server-rendered page's LCP element is typically present in the initial HTML response, while a client-rendered equivalent might not have its true LCP element until JavaScript execution completes, which is one of the reasons framework and rendering strategy choices show up directly in Core Web Vitals scores. Google's Search Central documentation covers how rendering strategy affects both crawling and Core Web Vitals measurement, worth a read if a framework migration is on the table.

Chrome DevTools documentation covers how to trace this rendering timeline directly, showing exactly when each candidate element becomes available, which is the fastest way to confirm whether a rendering strategy change is actually the cause of an LCP shift you're investigating.

A Second-Order Trap: Fixing the Wrong Page Template

Sites built on shared templates often have one LCP candidate pattern for one content type and a completely different pattern for another, product pages versus blog posts, for instance. A fix validated on one template doesn't automatically transfer to another, even on the same site, since the actual LCP element can differ entirely between them. Checking the LCP candidate per template, not just per site, avoids shipping a fix that helps one page type while leaving a different template's regression untouched.

This matters more than it sounds, because Search Console's Core Web Vitals report groups by URL pattern, so a fix applied broadly but validated on only one template can show mixed results in the aggregate report, some URL groups improving while others stay flagged, which is confusing until you realize the underlying LCP element was never the same across templates to begin with.

What to Do Once You've Confirmed the Right Element

Once you know the true LCP element, the fix path depends entirely on what it is. Text-based LCP elements usually trace back to render-blocking CSS or slow font loading delaying paint. Image-based LCP elements benefit from modern formats, proper sizing, and preload hints. Dynamically injected LCP elements, common in client-rendered apps, often need either server-side rendering for that specific content or a static placeholder that renders immediately while the dynamic version hydrates in behind it.

Treating all three cases the same, defaulting to "just compress the images" regardless of what the actual LCP element turns out to be, is the single most common reason LCP optimization work doesn't move the needle at all.

Check Before You Optimize

The practical takeaway is simple: check what your actual LCP element is before spending engineering time optimizing what you assume it to be. It takes two minutes in DevTools and can redirect an entire optimization effort toward the change that will actually move the metric.

137Foundry's guide on diagnosing a Core Web Vitals regression after a deploy covers this exact check as the first diagnostic step for LCP regressions specifically, and 137Foundry's technical SEO team runs this kind of audit as a standard part of our performance work with clients.

One More Habit Worth Building

Add "confirm the actual LCP element" to your pull request checklist for any change touching layout, above-the-fold content, or font loading. It costs thirty seconds in DevTools and catches the case where a seemingly unrelated change quietly swaps which element becomes the LCP candidate, something that's much cheaper to catch before merge than to diagnose after Search Console flags a regression weeks later.

This one small habit consistently catches more regressions before they ship than any amount of after-the-fact debugging ever recovers, mostly because the check takes less time than writing the commit message for the change that caused the problem in the first place.

Worth pinning somewhere your team will actually see it, not buried in a wiki page nobody opens once onboarding is over, since checklists only work when they're actually in the path of the work being done.

Top comments (0)