Skip to content
Skip to content
CHIMBUK.

Try flutter, ai, pricing or sudoku

[Engineering Insights]Engineering

Next.js Core Web Vitals: how we consistently hit 100 on Lighthouse

LCP under 1.2s, INP under 100ms, CLS of zero — consistently, in CI, before the client ever sees the page. The concrete techniques behind a perfect Lighthouse score on a production Next.js App Router site.

Chimbuk Engineering Studio10 min readPeer Reviewed

A perfect Lighthouse score in a lab is table stakes. The harder problem is a perfect score under real-world conditions: authenticated users, third-party scripts, images from a CMS, fonts loaded from a CDN, and a CI gate that fails the build when you regress. Here is the full technique stack we apply to every Next.js App Router project.

LCP: the hero must be HTML, not JavaScript

The single biggest LCP mistake in Next.js is a hero image inside a component that is lazy-loaded or client-side rendered. The browser cannot start fetching the LCP image until it has parsed and executed the JavaScript — which can be 400ms on a throttled mobile device. The fix is structural: the hero must be in a Server Component, the image must use next/image with priority={true}, and the image URL must be predictable at build time.

tsx
// ✅ Server Component hero with priority LCP image
import Image from "next/image";

export default function HeroSection() {
  return (
    <section className="relative overflow-hidden">
      <Image
        src="/assets/hero-banner.webp"
        alt="Production Engineering Showcase"
        width={1440}
        height={820}
        priority
        sizes="(max-width: 768px) 100vw, 1200px"
        quality={85}
      />
    </section>
  );
}

INP: interaction budget is 100ms, not 200ms

Google's threshold for Interaction to Next Paint is 200ms, but real-world P75 at that threshold leaves 25% of users with noticeable delay. We target 100ms P75. The main culprits: synchronous state updates on click, large component trees re-rendering on every keypress, and blocking third-party tag managers.

tsx
// Defer non-critical scripts past hydration
import Script from "next/script";

export default function AnalyticsScripts() {
  return (
    <>
      <Script
        src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXX"
        strategy="afterInteractive"
      />
      <Script src="/support-chat.js" strategy="lazyOnload" />
    </>
  );
}

CLS: zero layout shift discipline

Every layout shift has a concrete cause: images without explicit width and height, fonts swapping after render, and dynamically injected notification banners. Adding font-display: optional for secondary weights and ensuring all media containers define explicit aspect ratios completely eliminates CLS.

The score is a lagging indicator. The practice is structural: hero in a Server Component, images with priority and aspect ratio, fonts with display:swap, scripts deferred, and a CI gate that fails on regression. Build the practice, the score follows.

CK
Chimbuk Engineering StudioVerified Engineering

Field notes authored by the core engineering and systems team at Chimbuk. We build and maintain production Flutter mobile applications, Next.js web architectures, and cloud backends. No recycled marketing theory — only architecture decisions, code patterns, and performance budgets from real production deployments.

Share:
★★★★★4.9 Clutch & Google Reviews

We architect and build the systems we write about.

From vector search pipelines to high-concurrency payment gateways — schedule a technical discovery call.