Next.js performance in production: fix the 20% users feel
Field data instead of Lighthouse theatre. The hero image, client boundaries, third-party scripts, and caching decisions that change how a Next.js site feels.
Zynitex Engineering
Product & engineering notes

The short answer
Start with field data, not a Lighthouse score. Most Next.js performance wins come from the hero image, heavy client components, third-party scripts, and the order in which content streams. Set a mobile budget of LCP under 2.5s, INP under 200ms, and CLS under 0.1 at the 75th percentile, then fix the largest contributor.
What this note covers
- Lab scores are diagnostics; field data is the truth.
- The LCP image is usually the highest-leverage fix.
- Every `use client` boundary ships code; keep them small and low.
- Stream slow sections instead of blocking the shell.
- Caching is a product decision, especially for personalized pages.
On this page(9 sections)
A Lighthouse score on a fast laptop is a lab result. Production performance is a field result from a mid-range Android phone on a crowded network, with your analytics, chat widget, and cookie banner competing for the main thread.
Next.js gives you the tools to do well. It also makes it easy to wrap the whole app in a client provider and call it architecture. The difference shows up in the field.
Measure the right number
- Core Web Vitals at the 75th percentile, mobile, per route group.
- LCP: largest contentful paint.
- INP: interaction to next paint.
- CLS: cumulative layout shift.
- Real-user data from CrUX or your own
useReportWebVitalsstream.
Set a budget before you optimize: LCP under 2.5 seconds, INP under 200 milliseconds, CLS under 0.1. If you’re far off, fix the biggest contributor, not every item on a checklist. A page can score 95 in the lab and still feel slow on a train.
The usual suspects
- A hero image that isn’t prioritised or is served at the wrong size.
- A client component at the root that pulls the whole tree into the browser.
- A 3D scene or animation library on the critical path.
- Third-party scripts loading before the copy.
- Custom fonts without a swap strategy.
- A data-fetching waterfall that delays the first useful paint.
Lab scores are diagnostics. The 75th percentile on a real phone is the number that matters.
Fix LCP first
Find the LCP element in DevTools. If it’s the hero image, make it priority, give it accurate dimensions, and provide a sizes value that matches the layout. A priority image inside a client component that waits for a context provider is still late.
<Image
src="/hero.webp"
alt="Team reviewing a product dashboard"
width={1600}
height={900}
priority
sizes="(max-width: 768px) 100vw, 60vw"
/>- Don’t lazy-load the hero.
- Don’t animate the hero from opacity 0; it delays the visual paint.
- Use AVIF/WebP through the image pipeline and set a sensible cache TTL.
- Preload only what the first viewport needs.
- Test the actual mobile viewport, not a resized desktop window.
Stop shipping the whole client
Every use client boundary is a place where React has to ship, parse, and execute code. Keep formatting, data shaping, and static content on the server. Put state where it’s needed, not at the root.
const SplineScene = dynamic(
() => import("@/components/spline-scene"),
{
ssr: false,
loading: () => <div className="h-[420px]" aria-hidden />,
}
);Heavy visual libraries are fine when they’re behind a user action or below the fold. They’re expensive when they block the hero. The same rule applies to map libraries, charting packages, and rich text editors.
Note
Server Components are not a free pass
A server component that fetches five things in sequence still delays the response. Parallelise data, stream the shell, and keep the critical path short.
Streaming beats a spinner
Server Components let you send the shell early and stream the slow parts. Use Suspense around product recommendations, reviews, or dashboard widgets. Don’t suspend the hero copy for a personalization call.
- Render the static shell immediately.
- Stream below-the-fold data inside Suspense.
- Show a stable skeleton with reserved space.
- Avoid a global loading screen that hides everything.
Caching is a product decision
Static pages are cheap. Personalized pages need a clear cache key. Decide what can be stale for five minutes, what must be real-time, and who is allowed to see a cached response.
- Marketing pages: static or long revalidation.
- Catalogue pages: short revalidation plus tag-based invalidation.
- User dashboards: no shared cache.
- CMS content: invalidate by tag after publish.
- API responses: explicit cache headers and a key that includes tenant or user scope.
Watch out
Never cache another user’s data
A shared cache with the wrong key is not a performance bug. It’s a data leak. Test with two accounts before you enable caching on anything personalized.
Third-party scripts are a tax
Every tag manager, chat widget, heatmap, A/B tool, and review widget adds network and main-thread work. Load them after interaction or consent, self-host where possible, and remove what nobody uses.
- Audit every script quarterly and name an owner.
- Load chat and feedback widgets on interaction, not on page load.
- Delay marketing tags until consent or idle time.
- Self-host fonts and critical scripts where licences allow.
- Check what the tag manager is injecting after a “small” update.
A two-week sprint
| Days | Focus | Output |
|---|---|---|
| 1–2 | Field data and route priorities | A ranked list by traffic and P75 metrics |
| 3–5 | LCP elements | Hero images, fonts, and render order fixed |
| 6–8 | Client boundaries | Heavy libraries moved behind interaction or lazy loading |
| 9–10 | Third parties | Scripts deferred, removed, or consent-gated |
| 11–12 | Streaming and caching | Slow sections isolated; cache keys reviewed |
| 13–14 | Guardrails | Budgets in CI or a dashboard, not a one-off audit |
What good looks like
Not a 100 Lighthouse score. A fast first screen, a page that responds when tapped, and no layout jump when the cookie banner appears. The rest is decoration.
The most useful question is not “is this fast?”. It’s “which part would our users notice if we fixed it tomorrow?”. Usually it’s one image, one script, and one component.
Direct answers
Questions people actually ask
Short, checkable answers. Every entry here is also published as structured FAQ data for search and answer engines.
Why is my Lighthouse score high but field LCP slow?
Lighthouse runs in a controlled lab environment and often on a fast machine. Field data includes real devices, networks, cache states, and third-party scripts. Use CrUX or real-user monitoring to find the actual LCP element and route.
Do Server Components make everything fast automatically?
No. They reduce client-side JavaScript for static and data-shaped content, but a slow server, sequential fetches, or a heavy client boundary can still hurt. Measure the response and the critical path, not just the bundle.
How do I measure INP?
Collect interaction events in the field, including event timing and long tasks. Lab tools can approximate it, but INP is best measured with real user interactions. Start by finding the routes and components that block the main thread.
Should I use a CDN for a Next.js app?
Usually yes for static assets and cacheable pages. If you deploy to a platform with edge caching, understand what it caches and how invalidation works. Personalized responses should not be cached at the edge without a user-specific key.
References
Sources and further reading
Compliance notes are operational guidance, not legal or tax advice. Rules and thresholds change — confirm your position with a qualified professional.
Written by
Zynitex Engineering
We design and build the systems behind Zynibill, Invoice Studio, and client platforms like Regalia, Kristive, and ElyteSpark. These notes are the things we wish we had read before shipping them.
More about ZynitexKeep reading
Related notes
Shipping AI features without shipping a liability
Demos are easy. Production AI needs an eval set, guardrails, privacy rules, cost controls, and a fallback. A field guide from two AI products we shipped.
9 October 2026·10 minCommerceCustom storefront or Shopify in 2026? The honest maths
A practical comparison for Indian D2C brands: launch speed, three-year cost, checkout constraints, app creep, and the specific signals that justify a custom storefront.
20 October 2026·11 minEngineeringA sane AWS video streaming architecture for 2026
Upload, transcode, package, deliver, authorise, observe. A practical HLS pipeline with S3, MediaConvert, CloudFront, signed access, and the cost traps.
22 September 2026·12 min