Next.js Performance Optimization Guide

3 min read

Next.js Performance Optimization Guide

Discover how to optimize your Next.js applications for better performance, faster load times, and improved user experience.

Image Optimization

Use Next.js Image component effectively:

import Image from 'next/image';

function OptimizedImage() {
  return (
    <Image
      src="/large-image.jpg"
      alt="Optimized image"
      width={800}
      height={600}
      placeholder="blur"
      blurDataURL="data:image/jpeg;base64,..."
      priority={true}
      loading="eager"
    />
  );
}

Route Optimization

Implement route prefetching:

import Link from 'next/link';
import { useRouter } from 'next/router';

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

  // Programmatic prefetching
  const prefetchRoute = () => {
    router.prefetch('/dashboard');
  };

  return (
    <nav>
      {/* Automatic prefetching */}
      <Link href="/dashboard" prefetch={true}>
        Dashboard
      </Link>

      <button onMouseEnter={prefetchRoute}>
        Go to Dashboard
      </button>
    </nav>
  );
}

Dynamic Imports

Use dynamic imports for code splitting:

import dynamic from 'next/dynamic';

const DynamicComponent = dynamic(() => import('../components/Heavy'), {
  loading: () => <p>Loading...</p>,
  ssr: false, // Disable server-side rendering if needed
});

function Page() {
  return (
    <div>
      <DynamicComponent />
    </div>
  );
}

API Route Optimization

Optimize API routes:

// pages/api/data.ts
import { NextApiRequest, NextApiResponse } from 'next';
import nc from 'next-connect';
import cors from 'cors';
import rateLimit from 'express-rate-limit';

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});

const handler = nc<NextApiRequest, NextApiResponse>()
  .use(cors())
  .use(limiter)
  .get(async (req, res) => {
    const data = await fetchDataWithCaching();
    res.json(data);
  });

export default handler;

Caching Strategies

Implement effective caching:

// lib/cache.ts
import LRU from 'lru-cache';

const cache = new LRU({
  max: 500,
  maxAge: 1000 * 60 * 60 // 1 hour
});

export async function fetchWithCache(key: string, fetchFn: () => Promise<any>) {
  const cached = cache.get(key);
  if (cached) return cached;

  const fresh = await fetchFn();
  cache.set(key, fresh);
  return fresh;
}

// Usage in getStaticProps
export async function getStaticProps() {
  const data = await fetchWithCache('key', () => 
    fetch('https://api.example.com/data')
  );

  return {
    props: { data },
    revalidate: 60 // ISR - revalidate every minute
  };
}

Bundle Optimization

Analyze and optimize your bundle:

// next.config.js
module.exports = {
  webpack: (config, { dev, isServer }) => {
    // Enable tree shaking
    config.optimization.usedExports = true;

    // Add bundle analyzer in build
    if (!dev && !isServer) {
      const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
      config.plugins.push(
        new BundleAnalyzerPlugin({
          analyzerMode: 'static',
          reportFilename: './bundles/report.html'
        })
      );
    }

    return config;
  }
};

Best Practices

  1. Use Static Generation when possible
  2. Implement proper caching strategies
  3. Optimize images and fonts
  4. Use proper code splitting
  5. Implement proper error boundaries
  6. Monitor performance metrics
  7. Use proper lazy loading

Conclusion

Performance optimization in Next.js requires a multi-faceted approach. Start with the basics and gradually implement more advanced optimizations as your application grows.