Next.js performance optimization focuses on keeping browser JavaScript bundles small and serving HTML as fast as possible. Moving logic into Server Components, lazy-loading heavy client widgets, and caching static routes directly improves Core Web Vitals.
Google measures real user experience using three Core Web Vitals: Largest Contentful Paint (LCP), Interaction to Next Paint (INP), and Cumulative Layout Shift (CLS).
| Metric | Measures | Good Target | Primary Fix in Next.js |
|---|---|---|---|
| LCP | Main content load speed | Under 2.5s | Add priority to hero <Image>, stream HTML |
| INP | Input responsiveness | Under 200ms | Keep heavy logic in Server Components |
| CLS | Visual stability | Under 0.1 | Set image bounds, self-host with next/font |
next/dynamicRich charts, code editors, and map widgets add hundreds of kilobytes to client bundles. Defer downloading them with next/dynamic until the user needs that section.
// app/analytics/page.tsx
import dynamic from 'next/dynamic';
const TrafficChart = dynamic(() => import('@/components/TrafficChart'), {
loading: () => <p className="p-4 text-gray-500">Loading chart...</p>,
});
export default function AnalyticsPage() {
return (
<section className="p-6 space-y-4">
<h2 className="text-2xl font-bold">Traffic Overview</h2>
<TrafficChart />
</section>
);
}
@next/bundle-analyzerInstall @next/bundle-analyzer to inspect which npm packages inflate your client chunks during npm run build.
npm install -D @next/bundle-analyzer
// next.config.ts
import withBundleAnalyzer from '@next/bundle-analyzer';
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {};
export default withBundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
})(nextConfig);
'use client' for one small widget: Keep the page as a Server Component and isolate interactivity in a tiny leaf component.optimizePackageImports in next.config.ts so tree-shaking drops unused exports.await calls: Run independent fetches together with Promise.all() or stream slow widgets inside <Suspense>.ssr: false to next/dynamic?Only pass ssr: false inside a 'use client' component when a third-party library accesses window or document during module evaluation.
Yes, each route in app/ downloads only the client JavaScript required for that route's 'use client' components.
Run ANALYZE=true npm run build on macOS/Linux, or $env:ANALYZE="true"; npm run build in Windows PowerShell to open the interactive treemap.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With
You've completed this section! Take a quick 5-question quiz to check your understanding.