📋 Table of Contents
Web Development12 min read2025-06-12
Next.js PerformanceOptimization
Optimization of Next.js applications for maximum performance and SEO – best practices, techniques, and tools for modern web applications.
Next.jsPerformanceSEOWeb VitalsOptimization
⚡ Performance in Next.js
Next.js offers many built-in performance optimizations, but to get the most out of it, you need to apply additional techniques and best practices. Performance is not only important for user experience, but also a ranking factor for search engines.
- Automatic optimizations: Code Splitting, Image Optimization, Font Optimization
- Rendering strategies: SSR, SSG, ISR for optimum performance
- Bundle optimization: Tree Shaking, Minification, Compression
- Caching: HTTP Caching, CDN, Browser Cache
📊 Core Web Vitals
Google's Core Web Vitals are essential metrics for user experience:
LCP (Largest Contentful Paint)
Should be under 2.5 seconds
FID (First Input Delay)
Should be under 100 milliseconds
CLS (Cumulative Layout Shift)
Should be under 0.1
Measuring Web Vitals in Next.js:
// pages/_app.js
export function reportWebVitals(metric) {
console.log(metric);
// Google Analytics
if (metric.label === 'web-vital') {
gtag('event', metric.name, {
value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
event_label: metric.id,
non_interaction: true,
});
}
}
🖼️ Image Optimization
The Next.js Image component provides automatic optimizations:
Optimized Image Component:
import Image from 'next/image';
// Basic usage
<Image
src="/hero-image.jpg"
alt="Hero Image"
width={800}
height={600}
priority // For above-the-fold images
placeholder="blur"
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ..."
/>
// Responsive images
<Image
src="/responsive-image.jpg"
alt="Responsive Image"
fill
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
style={{ objectFit: 'cover' }}
/>
Image Configuration:
// next.config.js
module.exports = {
images: {
formats: ['image/webp', 'image/avif'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
domains: ['example.com', 'cdn.example.com'],
},
}
📦 Code Splitting & Lazy Loading
Reduce initial bundle size with smart code splitting:
Dynamic Imports:
import dynamic from 'next/dynamic';
import { Suspense } from 'react';
// Lazy loading for components
const HeavyComponent = dynamic(() => import('../components/HeavyComponent'), {
loading: () => <div>Loading...</div>,
ssr: false // Load only client-side
});
// Lazy loading for libraries
const loadChart = async () => {
const { Chart } = await import('chart.js');
return Chart;
};
// Route-based code splitting (automatic in Next.js)
// pages/dashboard.js is only loaded when the route is visited
React Suspense:
import { Suspense, lazy } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
);
}
💾 Caching Strategies
Optimize HTTP Headers:
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/static/(.*)',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
],
},
{
source: '/api/(.*)',
headers: [
{
key: 'Cache-Control',
value: 'public, s-maxage=60, stale-while-revalidate=30',
},
],
},
];
},
};
ISR (Incremental Static Regeneration):
// pages/products/[id].js
export async function getStaticProps({ params }) {
const product = await fetchProduct(params.id);
return {
props: { product },
revalidate: 60, // Revalidate every 60 seconds
};
}
export async function getStaticPaths() {
return {
paths: [],
fallback: 'blocking', // Generate pages on-demand
};
}
🏗️ SSR vs. SSG Optimization
Choose the right rendering strategy:
Static Site Generation (SSG)
- • Best performance
- - Ideal for static content
- • SEO-optimized
- • CDN-friendly
Server-Side Rendering (SSR)
- • Always up-to-date data
- • Personalized content
- - Longer TTFB
- • Server load
🔍 Bundle Analysis
Install Bundle Analyzer:
npm install --save-dev @next/bundle-analyzer
// next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer({
// Your Next.js configuration
});
// Add script to package.json
"analyze": "ANALYZE=true next build"
Webpack Bundle Optimizer:
// next.config.js
module.exports = {
webpack: (config, { isServer }) => {
// Remove unnecessary polyfills
config.resolve.fallback = { fs: false, path: false };
// Optimize bundle splitting
if (!isServer) {
config.optimization.splitChunks.cacheGroups = {
...config.optimization.splitChunks.cacheGroups,
vendor: {
test: /[\/]node_modules[\/]/,
name: 'vendors',
chunks: 'all',
},
};
}
return config;
},
};
📈 Performance Monitoring
Real User Monitoring (RUM):
// lib/analytics.js
export function trackWebVitals(metric) {
const analyticsId = process.env.NEXT_PUBLIC_ANALYTICS_ID;
if (analyticsId) {
// Google Analytics
gtag('event', metric.name, {
event_category: 'Web Vitals',
event_label: metric.id,
value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
non_interaction: true,
});
}
// Custom Analytics
fetch('/api/analytics', {
method: 'POST',
body: JSON.stringify(metric),
headers: { 'Content-Type': 'application/json' },
});
}
Using the Performance API:
// Collect performance metrics
useEffect(() => {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (entry.entryType === 'navigation') {
console.log('Page Load Time:', entry.loadEventEnd - entry.fetchStart);
}
if (entry.entryType === 'paint') {
console.log(entry.name + ':', entry.startTime);
}
});
});
observer.observe({ entryTypes: ['navigation', 'paint'] });
}, []);
✅ Best Practices
- Preload critical resources: Use
<link rel="preload">
for important assets - Font optimization: Use
next/font
for automatic font optimization - Third-Party Scripts: Use
next/script
withstrategy="lazyOnload"
- API optimization: Implement efficient API routes with caching
- Database optimization: Use connection pooling and optimized queries
- Prefetching: Use Next.js link prefetching for better navigation
- Service Worker: Implement for advanced caching strategies
- Compression: Enable gzip/brotli compression on the server