How to Fix LCP on Image-Heavy Pages (Next.js Patterns That Work)

Apr 24, 2026 · 4 min read

PerformanceNext.jsCore Web Vitals

If your page is image-heavy (portfolio, case studies, blogs with hero media), LCP is almost always dominated by one thing:

  • a hero image,
  • a gallery grid above the fold,
  • or an image carousel that loads too much too early.

This post is the exact workflow I use to get LCP < 2.5s reliably on real devices — not just in Lighthouse.

Step 0: Don’t guess — find the true LCP element

Before you change anything, identify what’s actually being reported as LCP.

Where to check

  • Chrome DevTools → Performance: record load and look for “Largest Contentful Paint”.
  • Lighthouse: use it to get hints, but verify in DevTools.
  • Field data: Search Console Core Web Vitals groups by URL pattern; it tells you where to focus.

If the LCP element is a heading, your issues are often fonts, CSS, or hydration.
If the LCP element is an image, your issues are usually bytes + priority + caching.

The main LCP killers on image-heavy pages

In practice, LCP gets worse because of:

  1. Slow TTFB (server work, no caching, cold starts)
  2. Too many bytes (oversized hero images, unoptimised formats)
  3. Bad loading order (the hero image isn’t requested early)
  4. Render-blocking work (fonts, CSS, heavy JS before paint)

You fix LCP by addressing these in order.

1) Reduce TTFB (because LCP can’t start without HTML)

Even perfect images won’t help if the server responds slowly.

Patterns that work

  • Cache full pages where possible (edge/CDN)
  • Avoid per-request expensive work on marketing pages (DB calls, heavy Markdown parsing)
  • Use static generation for content pages
  • Move non-critical work to after render (analytics, tracking, personalization)

Quick sanity check

If TTFB is > 800ms for most users, fix that first.

2) Ship the right image bytes (format + dimensions)

Most LCP wins come from sending fewer bytes earlier.

Rules I follow

  • Never serve a 2500px image to a 390px viewport.
  • Prefer AVIF/WebP when available.
  • Keep the LCP image as small as possible without looking soft.

Practical targets

  • Hero image often can be 100–250 KB (sometimes less) and still look great.
  • Avoid huge PNGs for photos (they’re almost always wrong).

3) Make the LCP request happen immediately

If the browser doesn’t request the hero image early, LCP will suffer even if the image is small.

Next.js patterns that work

  • Use next/image for responsive sizing and modern formats.
  • For the hero image, set priority so it’s fetched early.
  • Ensure the LCP image is not hidden behind a lazy-loaded carousel or a conditional render.

Common mistake

“Lazy-loading everything” feels like the right move but can backfire: the hero should be eager.

4) Use sizes correctly (this is where most teams fail)

If you don’t set sizes, the browser might choose a larger srcset candidate than needed.

The goal is simple: tell the browser the rendered width at each breakpoint.

Example thinking (not copy/paste):
If your hero is full-width on mobile, but constrained to 720px on desktop, sizes should reflect that.

5) Preload what matters (but don’t preload everything)

Preloading is powerful when used for the one resource that blocks LCP.

Good preload candidates:

  • the hero image (if it’s LCP)
  • the critical font file (if heading/text is LCP)

Bad preload candidates:

  • multiple gallery images
  • below-the-fold media

6) Avoid above-the-fold image “work”

Some patterns look nice but destroy LCP:

  • blur + heavy filters
  • client-side parallax effects on the hero
  • large background images applied via CSS that aren’t optimised

If you want motion, apply it after first paint.

7) Reduce JS before paint (especially client components)

On image-heavy pages, you often still lose LCP because the main thread is busy:

  • large client bundles,
  • third-party scripts,
  • hydration costs.

What I do

  • Keep the hero and initial layout server-rendered.
  • Move interactive widgets below the fold or load them after interaction.
  • Audit third-party scripts (most are a tax).

A repeatable debugging workflow (use this every time)

  1. Identify the true LCP element.
  2. Check TTFB and caching.
  3. Check image bytes and format.
  4. Ensure the hero request is early (priority/order).
  5. Fix sizes so the browser chooses the right candidate.
  6. Reduce JS/third-party blocking.
  7. Validate in field data.

Quick checklist for image-heavy pages

  • LCP element is known (image vs text)
  • HTML response is cached
  • Hero image is compressed + correct dimensions
  • Hero image is requested early (priority)
  • sizes is accurate
  • No render-blocking surprises (fonts/JS)
  • Third-party scripts deferred

Related posts

All posts →