
How to get a Next.js site to 90 plus Core Web Vitals scores across LCP, INP, and CLS with concrete patterns, tradeoffs, and measurement.
Next.js gives you an unfair performance advantage if you use it correctly. The framework ships with server components, streaming, edge rendering, and image optimization built in. Yet most Next.js sites in production still score below 60 on real user Core Web Vitals. The reason is not the framework. It is the accumulation of decisions that individually seem harmless but collectively kill performance. This post is the working guide we follow inside QwiklyLaunch 45-day sprints to ship Next.js sites with 90 plus Core Web Vitals scores on the day of launch, not after months of performance retrofitting. Every recommendation covers the concrete pattern, the tradeoff, and the expected impact. If you follow this guide during your build, you will not need a performance sprint later. That is the entire point.
The App Router with React Server Components is the modern Next.js default and it is significantly faster than the Pages Router for most workloads. RSC lets you render on the server without shipping JavaScript to the client for that component. That reduces bundle size, speeds up First Contentful Paint, and improves LCP. For a typical marketing page, migrating from Pages Router with client components to App Router with server components can reduce initial JavaScript by 40 to 70 percent. This translates directly to better field data. If you are starting a new project in 2026, App Router is the default. If you have an existing Pages Router site, plan the migration but do not rush it.
Everything else should be a server component. This alone shrinks bundles dramatically.
The Next.js Image component handles most of the hard work but only if configured correctly. Always provide explicit width and height to prevent CLS. Use priority on the LCP image, typically the hero. Use fetchpriority high in the link tag for images below the fold that must load fast. Serve modern formats by relying on the built-in AVIF and WebP conversion. Configure device sizes appropriately in next.config to avoid serving 3x images to 1x screens. A hero image should never exceed 200 kilobytes at any device size after optimization. If it does, your image is too large or your format is wrong.
Fonts are the most common CLS and LCP killer on Next.js sites. Use next/font with the display swap option to prevent invisible text during font swap. Self-host fonts through next/font/google or next/font/local rather than loading from fonts.googleapis.com. This eliminates a DNS lookup and a TCP handshake that cost 200 to 500 milliseconds on slow connections. Preload only the primary font. Preloading multiple fonts wastes bandwidth. Use font metric overrides with adjustFontFallback to reduce layout shift when the web font swaps in. These four steps alone typically move CLS from 0.15 to under 0.05 on real user data.
Next.js App Router supports streaming through Suspense boundaries. Wrap slow data fetches in Suspense with meaningful fallbacks. This lets the shell render immediately while data loads in the background. The user sees content faster, improving LCP and perceived performance. Do not wrap the entire page in one Suspense boundary. Wrap individual slow sections so the fast parts render immediately. Common streaming patterns include streaming below-the-fold content, deferring analytics dashboards, and progressive product listings. Streaming is one of the biggest wins in the App Router and most teams underuse it.
Every Next.js project should have the bundle analyzer configured. Run it monthly. Look for dependencies over 30 kilobytes gzipped and justify each one. Common bloat sources include full Lodash instead of individual imports, Moment.js instead of date-fns or dayjs, the full Firebase SDK when you only use auth, and heavy chart libraries used on one page. Replace, tree-shake, or lazy load. Dynamic import with next/dynamic pulls heavy components out of the initial bundle. Use it for modals, charts, and admin panels that most users never see. This engineering discipline is baked into our web development deliverables from day one.
The wrong data fetching pattern kills performance. Fetch data on the server whenever possible using async server components. This eliminates client waterfalls and reduces client JavaScript. Use parallel data fetching with Promise.all when a component needs multiple data sources. Use route segment configuration to control caching behavior. For pages with mostly static content, use force-static. For pages with per-request data, use force-dynamic. For hybrid pages, let Next.js infer. Misconfigured caching either serves stale data or blocks rendering unnecessarily.
Vercel and Cloudflare Workers both offer edge runtimes that reduce time to first byte globally. Use edge for marketing pages that need low TTFB and do not require Node-specific APIs. Do not use edge for pages that need heavy computation, filesystem access, or specific Node modules. TTFB improvements from edge are typically 100 to 400 milliseconds for users far from your origin. That alone can move you from failing to passing LCP for a significant slice of users.
Every third-party script is a performance liability. Google Analytics, Hotjar, Intercom, and chat widgets all cost real milliseconds. Load them with next/script and the strategy attribute set to lazyOnload or afterInteractive depending on need. Never load third-party scripts synchronously in the head. If your analytics tool cannot be deferred, replace it with one that can. Server-side tagging through Google Tag Manager Server-Side removes most third-party JavaScript from the client entirely. This is a major win for both performance and privacy.
Lighthouse is a starting point, not the finish line. Instrument real user monitoring with the web-vitals library. Send data to your analytics endpoint or a RUM service like SpeedCurve or Datadog. Aggregate by URL and device type. Alert on 75th percentile regression per URL. Field data tells you what real users experience. Lighthouse tells you what a simulated environment shows. Chase field data. This is the same measurement discipline we ship in every DevOps and cloud engagement that includes observability.
Next.js App Router lets you configure caching per route segment. Use force-static for pages with truly static content. Use force-dynamic for pages that must run per request. Use auto for hybrid pages where the framework can infer. Add revalidate values for pages that update on a schedule. Misconfigured caching either serves stale data or blocks rendering unnecessarily. Understanding these knobs is the difference between a fast site and a slow one running on the same framework. Read the App Router caching docs thoroughly before your first production launch. The behavior is different from Pages Router in ways that trip up experienced Next.js developers.
AVIF beats WebP on file size in most cases. WebP beats JPEG. Serve AVIF as the primary format with WebP fallback and JPEG as the last resort. next/image handles this automatically with the correct configuration. Compression quality of 75 to 80 for photos and 90 to 95 for graphics with text produces the best balance of file size and visual quality. Test on your own images before locking in defaults. Some image types compress much better in one format than others. Bulk compress existing image assets before migrating to next/image. Migration alone does not compress source files.
Every third-party script is a performance liability. Use next/script with the strategy attribute set based on priority. Set lazyOnload for anything not needed for first paint like chat widgets and heatmaps. Set afterInteractive for analytics that need to fire early but not block rendering. Set beforeInteractive only for scripts that must run before hydration, which is almost never. Server-side tagging through Google Tag Manager Server-Side removes most third-party JavaScript from the client entirely. This produces both performance and privacy wins.
RSC is powerful but has real constraints. Server components cannot use hooks, cannot subscribe to events, and cannot use browser APIs. Client components cannot fetch data during render, cannot be async, and ship JavaScript to the browser. Design your component tree with clear boundaries. Fetch data in server components, render UI in server components where possible, and drop into client components only where interactivity is needed. Avoid deep client component trees. A large client component with many children hydrates as one JavaScript module and undoes the RSC size benefit. Small client leaves inside a mostly server tree is the pattern that ships the least JavaScript.
Modern privacy regulations require cookie consent for most third-party scripts. Implement consent management that blocks scripts until the user consents. Most consent tools inject JavaScript at the top of the head, which is a performance disaster. Use a lightweight consent library or roll your own that defers third-party scripts through next/script with strategy attributes tied to consent state. This preserves performance while meeting compliance requirements. It is a moderate amount of engineering work but pays back in both performance and legal safety.
The CSS size-adjust, ascent-override, descent-override, and line-gap-override properties let you match a fallback font's metrics to a web font. When configured correctly, the swap from fallback to web font produces zero layout shift. next/font handles this automatically when you pass the adjustFontFallback option. Verify with the layout instability panel in Chrome DevTools. Any layout shift during font swap indicates misconfiguration. Fixing font-induced CLS is one of the fastest wins in a Core Web Vitals sprint.
Deploy strategy affects perceived performance. Blue-green deploys eliminate downtime. Progressive rollouts through feature flags let you catch performance regressions on a small percentage of users before full rollout. CDN cache invalidation should be surgical, not global. Global cache purge on every deploy destroys hit rates and produces slow pages until caches rewarm. Use cache tags to invalidate only affected paths. This engineering discipline is boring but produces measurable improvements in the field data users experience. Deploy hygiene is often ignored until a bad deploy causes an incident. Building it in from day one is far cheaper than retrofitting after a production fire. Automated performance regression tests in CI, combined with canary deploys and cache-tag invalidation, keep the field data stable across dozens of deploys per week rather than dropping every Friday deploy. The engineering time to set this up is one to two weeks and pays back many times over across a year of production changes. This is the boring infrastructure work that separates sites that stay fast from sites that slowly degrade under the weight of accumulated feature releases and quiet dependency updates over many quarters.
Set performance budgets in CI. Lighthouse CI catches regression before deploy. Bundlewatch fails a pull request that adds too much JavaScript. These automated guardrails prevent the slow accumulation of performance debt that kills sites over time. Every deploy without a budget is a risk. Every deploy with budgets enforced is safe. The setup cost is a few hours. The ongoing value is enormous.
Every dependency you add is a potential performance cost. Audit dependencies quarterly and remove anything you no longer use. Prefer smaller alternatives when they exist. Use tools like npm ls or pnpm why to trace why a heavy transitive dependency landed in your bundle. Sometimes a small direct dependency pulls in a massive transitive one. Removing the direct dependency or finding an alternative that avoids the transitive weight produces silent bundle wins. This is boring but consistent hygiene that keeps bundles small over years.
Inside a QwiklyLaunch 45-day sprint, we make performance decisions upfront in week one so we ship green vitals on day one. That means picking App Router, configuring next/font correctly, setting up next/image with the right defaults, wiring streaming for slow data, and setting CI budgets before writing feature code. Retrofitting performance is always more expensive than doing it right the first time. Founders who partner with us on a launch never need a separate performance sprint six months later. That is the compounding value of doing this correctly at the start.
Next.js performance is a solved problem when you commit to the discipline. Every recommendation in this post is well documented in the framework and battle tested in production. Founders who follow the discipline ship fast sites and win rankings. Founders who skip the discipline ship slow sites and blame the framework. If you want a team that ships green Core Web Vitals on day one, start a conversation with our team. You can also see recent Next.js builds on the projects page.
Content Writer at Qwikly Launch
Dharmendra Singh Yadav is an experienced writer covering SaaS, technology, and product development trends.
More articles coming soon...
Want to build or scale your SaaS product? Book a free consultation with our expert team and let's turn your idea into reality.
Book a Free Consultation