# Next.js Optimization Guide
Performance optimization techniques for Next.js 14+ applications.
---
## Table of Contents
- [Rendering Strategies](#rendering-strategies)
- [Image Optimization](#image-optimization)
- [Code Splitting](#code-splitting)
- [Data Fetching](#data-fetching)
- [Caching Strategies](#caching-strategies)
- [Bundle Optimization](#bundle-optimization)
- [Core Web Vitals](#core-web-vitals)
---
## Rendering Strategies
### Server Components (Default)
Server Components render on the server and send HTML to the client. Use for data-heavy, non-interactive content.
```tsx
// app/products/page.tsx - Server Component (default)
async function ProductsPage() {
// This runs on the server - no client bundle impact
const products = await db.products.findMany();
return (
{products.map(product => (
))}
);
}
```
### Client Components
Use `'use client'` only when you need:
- Event handlers (onClick, onChange)
- State (useState, useReducer)
- Effects (useEffect)
- Browser APIs (window, document)
```tsx
'use client';
import { useState } from 'react';
function AddToCartButton({ productId }: { productId: string }) {
const [isAdding, setIsAdding] = useState(false);
async function handleClick() {
setIsAdding(true);
await addToCart(productId);
setIsAdding(false);
}
return (
);
}
```
### Mixing Server and Client Components
```tsx
// app/products/[id]/page.tsx - Server Component
async function ProductPage({ params }: { params: { id: string } }) {
const product = await getProduct(params.id);
return (
{/* Server-rendered content */}
{product.name}
{product.description}
{/* Client component for interactivity */}
{/* Server component for reviews */}
);
}
```
### Static vs Dynamic Rendering
```tsx
// Force static generation at build time
export const dynamic = 'force-static';
// Force dynamic rendering at request time
export const dynamic = 'force-dynamic';
// Revalidate every 60 seconds (ISR)
export const revalidate = 60;
// Revalidate on-demand
import { revalidatePath, revalidateTag } from 'next/cache';
async function updateProduct(id: string, data: ProductData) {
await db.products.update({ where: { id }, data });
// Revalidate specific path
revalidatePath(`/products/${id}`);
// Or revalidate by tag
revalidateTag('products');
}
```
---
## Image Optimization
### Next.js Image Component
```tsx
import Image from 'next/image';
// Basic optimized image
// Responsive image
// With placeholder blur
import productImage from '@/public/product.jpg';
```
### Remote Images Configuration
```js
// next.config.js
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'cdn.example.com',
pathname: '/images/**',
},
{
protocol: 'https',
hostname: '*.cloudinary.com',
},
],
// Image formats (webp is default)
formats: ['image/avif', 'image/webp'],
// Device sizes for srcset
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
// Image sizes for srcset
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
},
};
```
### Lazy Loading Patterns
```tsx
// Images below the fold - lazy load (default)
// Above the fold - load immediately
```
---
## Code Splitting
### Dynamic Imports
```tsx
import dynamic from 'next/dynamic';
// Basic dynamic import
const HeavyChart = dynamic(() => import('@/components/HeavyChart'), {
loading: () => ,
});
// Disable SSR for client-only components
const MapComponent = dynamic(() => import('@/components/Map'), {
ssr: false,
loading: () => ,
});
// Named exports
const Modal = dynamic(() =>
import('@/components/ui').then(mod => mod.Modal)
);
// With suspense
const DashboardCharts = dynamic(() => import('@/components/DashboardCharts'), {
loading: () => } />,
});
```
### Route-Based Splitting
```tsx
// app/dashboard/analytics/page.tsx
// This page only loads when /dashboard/analytics is visited
import { Suspense } from 'react';
import AnalyticsCharts from './AnalyticsCharts';
export default function AnalyticsPage() {
return (
}>
);
}
```
### Parallel Routes for Code Splitting
```
app/
├── dashboard/
│ ├── @analytics/
│ │ └── page.tsx # Loaded in parallel
│ ├── @metrics/
│ │ └── page.tsx # Loaded in parallel
│ ├── layout.tsx
│ └── page.tsx
```
```tsx
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
analytics,
metrics,
}: {
children: React.ReactNode;
analytics: React.ReactNode;
metrics: React.ReactNode;
}) {
return (
{children}
}>{analytics}
}>{metrics}
);
}
```
---
## Data Fetching
### Server-Side Data Fetching
```tsx
// Parallel data fetching
async function Dashboard() {
// Start both requests simultaneously
const [user, stats, notifications] = await Promise.all([
getUser(),
getStats(),
getNotifications(),
]);
return (