Optimize LCP assets
Prioritize hero images and fonts for the first meaningful paint in App Router pages.
Learn how to optimize server-rendered hero images, font loading, and critical CSS to bring LCP under 2.5s for revenue-facing pages.
Prioritize hero images and fonts for the first meaningful paint in App Router pages.
Use preload, font-display, and native layout stability to avoid delayed LCP and cumulative shifts.
Keep the critical path tight with optimized responses and minimal hydration payloads.
Largest Contentful Paint measures how long the browser takes to render the largest visible element — usually a hero image or an above-the-fold heading. In Next.js 15 App Router, the biggest LCP traps come from three places: unoptimised hero images that aren't preloaded, web fonts that block rendering until they arrive, and server components that push data-fetching too close to the critical path. Google's threshold is 2.5 seconds. Teams targeting top-3 rankings on competitive keywords should aim for under 1.8s on mobile.
priorityBy default, Next.js lazy-loads all images. That's correct for below-the-fold images, but catastrophic for the hero. The browser won't start downloading the image until it has parsed layout — which means the LCP element arrives late. Fix it by adding the priority prop:
// app/(marketing)/page.tsx
import Image from "next/image";
<Image
src="/hero.webp"
alt="AuditJet performance dashboard"
width={1200}
height={630}
priority
sizes="(max-width: 768px) 100vw, 1200px"
/>
The priority prop injects a <link rel="preload"> tag into the document <head>, so the browser fetches the image in parallel with HTML parsing — before it ever sees the image tag itself. On pages with hero images, this single change routinely saves 0.3–0.8 seconds of LCP.
Also set the sizes attribute accurately. If you omit it, Next.js defaults to 100vw and generates a srcset sized for a full-width image — which means mobile users download a far larger file than they need.
next/font, not a stylesheet linkA <link rel="stylesheet"> to Google Fonts adds two round-trips before the browser knows which font file to download. next/font/google eliminates those extra requests by inlining the font CSS and preloading the subset the page actually uses:
// app/layout.tsx
import { Inter } from "next/font/google";
const inter = Inter({
subsets: ["latin"],
display: "swap",
preload: true,
});
display: "swap" means the browser renders text immediately in a fallback font and swaps to the web font when it arrives — preventing invisible text from blocking the LCP element. Pair this with a matching font-family in your CSS so the layout shift from the font swap is minimal (and your CLS score stays healthy).
LCP is measured from navigation start. That means a slow server adds directly to your LCP time before a single pixel is painted. In the App Router, the most common culprit is a Server Component that awaits a database call before streaming any HTML. Move uncritical data fetches below the fold with Suspense boundaries so the initial shell — including your hero — streams immediately:
// ✅ Hero renders immediately; reviews load asynchronously
export default function ProductPage() {
return (
<>
<HeroSection />
<Suspense fallback={<ReviewsSkeleton />}>
<ReviewsSection />
</Suspense>
</>
)
}
Combine this with Cache-Control: s-maxage headers or Next.js revalidate on your fetch calls so the edge serves cached HTML for repeat visitors. For mostly-static marketing pages, TTFB under 200ms is achievable on Vercel Edge without any architecture changes beyond correct cache configuration.
Analytics tags, chat widgets, and A/B testing scripts loaded synchronously in <head> block HTML parsing and delay when the browser can start painting. In Next.js 15, load all non-critical third-party scripts with the built-in Script component and thestrategy="afterInteractive" orstrategy="lazyOnload" strategy:
import Script from "next/script";
<Script
src="https://cdn.example.com/analytics.js"
strategy="afterInteractive"
/>
This defers script execution until after the page is interactive — your hero image has already painted by then. Run a free speed test before and after to measure the exact LCP saving from each script you defer.
One-off Lighthouse runs are snapshots, not monitoring. A deploy can silently regress LCP on a page you haven't tested — and you won't know until rankings drop or conversion data catches up, usually weeks later. The right approach is continuous automated scanning: every page, on a schedule, with alerts the moment a metric drops below threshold.
AuditJet runs Lighthouse on all your monitored pages daily or hourly, tracks LCP trends over time, and sends an alert with an AI-generated fix blueprint the moment a regression appears. The AI fix blueprint names the exact file and code change — no guesswork. For teams shipping multiple deploys per day on Next.js apps, this is the difference between catching an LCP regression in minutes and discovering it in the next quarterly review.
AuditJet helps teams catch LCP regressions before they impact SEO and conversion on their most valuable pages.