Performance Optimization

Build lightning-fast Next.js applications

⚡ What is Performance Optimization?

Next.js provides built-in performance optimizations including code splitting, lazy loading, and caching. These features work automatically to deliver fast page loads and smooth user experiences without complex configuration.


// Automatic code splitting per page
export default function Page() {
  return <h1>Fast Loading Page</h1>
}
                                    

Key Performance Features

📦

Code Splitting

Automatically splits code into smaller bundles

Route-based Component-based Automatic
🔄

Caching

Smart caching for faster subsequent loads

Static Dynamic Revalidation
🎯

Lazy Loading

Load components only when needed

Dynamic Import Suspense On-demand
🔮

Prefetching

Preload pages before user clicks

Link Prefetch Automatic Smart

🔹 Dynamic Imports

Load components only when needed using dynamic imports. This reduces initial bundle size and improves page load time by splitting code into smaller chunks loaded on demand.

import dynamic from 'next/dynamic'

// Load component only when needed
const HeavyComponent = dynamic(() => import('./HeavyComponent'))

export default function Page() {
  return (
    <div>
      <h1>My Page</h1>
      <HeavyComponent />
    </div>
  )
}

Result:

✅ Smaller initial bundle size

✅ Faster page load time

✅ Component loads when rendered

🔹 Loading States

Show loading indicators while dynamic components load. This provides better user experience by giving visual feedback during component loading instead of blank screens.

import dynamic from 'next/dynamic'

const Chart = dynamic(() => import('./Chart'), {
  loading: () => <p>Loading chart...</p>,
  ssr: false // Disable server-side rendering
})

export default function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      <Chart />
    </div>
  )
}

When to use dynamic imports:

  • Large third-party libraries (charts, maps)
  • Components not visible on initial load
  • Modal dialogs and popups
  • Browser-only components (no SSR needed)

🔹 Link Prefetching

Next.js automatically prefetches linked pages when Link components appear in the viewport. This makes navigation feel instant by loading pages before users click.

import Link from 'next/link'

export default function Navigation() {
  return (
    <nav>
      {/* Automatically prefetched */}
      <Link href="/about">About</Link>
      
      {/* Disable prefetch if needed */}
      <Link href="/contact" prefetch={false}>
        Contact
      </Link>
    </nav>
  )
}

Result:

✅ Instant page navigation

✅ Prefetch in background

✅ Better user experience

🔹 Static Generation

Generate pages at build time for maximum performance. Static pages are served instantly from CDN without server processing, perfect for content that doesn't change frequently.

🔸 Basic Static Page

// This page is automatically static
export default function About() {
  return (
    <div>
      <h1>About Us</h1>
      <p>We build amazing apps!</p>
    </div>
  )
}

🔸 Static with Data

export default function Blog({ posts }) {
  return (
    <div>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
        </article>
      ))}
    </div>
  )
}

// Fetch data at build time
export async function generateStaticParams() {
  const posts = await fetch('https://api.example.com/posts')
  return posts
}

🔹 Revalidation

Update static pages without rebuilding your entire site. Revalidation regenerates pages in the background after a specified time, keeping content fresh while maintaining performance.

// Revalidate every 60 seconds
export const revalidate = 60

export default async function Products() {
  const products = await fetch('https://api.example.com/products')
  
  return (
    <div>
      {products.map(product => (
        <div key={product.id}>{product.name}</div>
      ))}
    </div>
  )
}

Revalidation strategies:

  • Time-based: Revalidate after X seconds
  • On-demand: Revalidate via API call
  • Tag-based: Revalidate by cache tags

🔹 Bundle Analysis

Analyze your bundle size to identify optimization opportunities. Understanding what's in your bundle helps you make informed decisions about code splitting and dependencies.

🔸 Setup Bundle Analyzer

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

module.exports = withBundleAnalyzer({
  // Your Next.js config
})

🔸 Run Analysis

# Install package
npm install @next/bundle-analyzer

# Analyze bundle
ANALYZE=true npm run build

🧠 Test Your Knowledge

What does dynamic import do?