Skip to main content

Next.js Performance Optimization

Answer

Next.js provides many built-in optimizations, but understanding and applying advanced techniques can significantly improve your application's performance.

Core Web Vitals

Image Optimization

import Image from 'next/image';

// ✅ Use Next.js Image
<Image
src="/hero.jpg"
alt="Hero"
width={1200}
height={600}
priority // LCP image - preload
placeholder="blur"
/>

// ✅ Responsive images with sizes
<Image
src="/product.jpg"
alt="Product"
fill
sizes="(max-width: 768px) 100vw, 50vw"
/>

Font Optimization

// app/layout.js
import { Inter, Roboto_Mono } from "next/font/google";

const inter = Inter({
subsets: ["latin"],
display: "swap", // Avoid FOIT
preload: true,
});

const robotoMono = Roboto_Mono({
subsets: ["latin"],
variable: "--font-mono",
});

export default function RootLayout({ children }) {
return (
<html className={`${inter.className} ${robotoMono.variable}`}>
<body>{children}</body>
</html>
);
}

Script Optimization

import Script from 'next/script';

// Load after page is interactive
<Script
src="https://analytics.example.com/script.js"
strategy="afterInteractive"
/>

// Load during idle time
<Script
src="https://third-party.com/widget.js"
strategy="lazyOnload"
/>

// Inline critical JS
<Script id="critical-js" strategy="beforeInteractive">
{`window.config = { apiUrl: '...' }`}
</Script>

Bundle Analysis

# Install analyzer
npm install @next/bundle-analyzer

# next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});

module.exports = withBundleAnalyzer({});

# Run analysis
ANALYZE=true npm run build

Dynamic Imports

import dynamic from "next/dynamic";

// Code split heavy components
const Chart = dynamic(() => import("./Chart"), {
loading: () => <ChartSkeleton />,
ssr: false, // Client-only component
});

// Named exports
const MDEditor = dynamic(() => import("md-editor").then((mod) => mod.Editor), {
ssr: false,
});

Route Segment Config

// Optimize data fetching per route
export const revalidate = 3600; // ISR every hour
export const dynamic = "force-static"; // Force SSG
export const fetchCache = "force-cache";

// Prevent dynamic rendering
export const preferredRegion = "auto";
export const maxDuration = 30;

Parallel Data Fetching

// ✅ Parallel - faster
async function Page() {
const [products, categories] = await Promise.all([
getProducts(),
getCategories(),
]);

return <Shop products={products} categories={categories} />;
}

// ❌ Sequential - slower
async function Page() {
const products = await getProducts();
const categories = await getCategories();
}

Streaming with Suspense

import { Suspense } from "react";

export default function Page() {
return (
<>
<Header /> {/* Immediate */}
<Suspense fallback={<ProductsSkeleton />}>
<Products /> {/* Streamed */}
</Suspense>
<Suspense fallback={<ReviewsSkeleton />}>
<Reviews /> {/* Streamed independently */}
</Suspense>
</>
);
}

Prefetching

import Link from 'next/link';

// Auto-prefetch links in viewport (production)
<Link href="/products">Products</Link>

// Disable prefetch for rarely visited pages
<Link href="/terms" prefetch={false}>Terms</Link>

// Programmatic prefetch
import { useRouter } from 'next/navigation';

function SearchInput() {
const router = useRouter();

const handleFocus = () => {
router.prefetch('/search-results');
};

return <input onFocus={handleFocus} />;
}

Server Components Benefits

// Heavy dependencies stay on server
import { parse } from "marked"; // 50kb
import { highlight } from "prism"; // 30kb
import { format } from "date-fns"; // 20kb

// Server Component - these don't add to client bundle
export default async function BlogPost({ content }) {
const html = parse(content);
const highlighted = highlight(html);

return <article dangerouslySetInnerHTML={{ __html: highlighted }} />;
}

Performance Checklist

AreaOptimization
ImagesUse next/image with proper sizes
FontsUse next/font, font-display: swap
JSDynamic imports, tree shaking
CSSCSS Modules, minimal CSS-in-JS
DataParallel fetching, caching
RenderingStreaming, Suspense
Third-partyafterInteractive, lazyOnload

Key Points

  • Use next/image with proper sizing and priority
  • Use next/font for optimized font loading
  • Dynamic import heavy components
  • Prefetch important routes
  • Stream content with Suspense
  • Keep dependencies in Server Components
  • Analyze bundle and eliminate unused code