Back to KB
Difficulty
Intermediate
Read Time
6 min

Next.js Dynamic Imports & Lazy Loading: The Complete Guide (2026)

By Codcompass TeamΒ·Β·6 min read

Every Next.js app has the same performance trap: you ship a heavy component that every user has to download, even when most users never interact with it. A rich text editor on an admin page. A chart library on a dashboard that half the users never open. A date picker that appears behind a modal.

Dynamic imports are the fix. They split these components into separate chunks that only load when needed. Less JavaScript on initial load, faster first paint, better Core Web Vitals.

This guide covers every dynamic import pattern in Next.js 15, when to use each, and how to measure the impact.

The Problem: Bundle Size

A Next.js page has two phases: the initial HTML from the server and the JavaScript bundle the browser has to parse and execute. The larger that bundle, the longer the Time to Interactive.

A single heavy library can add hundreds of kilobytes. Common offenders:

  • Chart libraries (Recharts, Chart.js, Victory) β€” 200–400KB each
  • Rich text editors (TipTap, Slate, Quill) β€” 300–600KB
  • Date pickers with locale data β€” 100–200KB
  • Map libraries (Leaflet, MapboxGL) β€” 300KB+
  • PDF renderers β€” 500KB+

If these are statically imported at the top of a file, every visitor downloads them on first load β€” including visitors who never see that component.

next/dynamic: The Next.js Approach

next/dynamic is Next.js's built-in wrapper around React.lazy with extra features for SSR control.

import dynamic from 'next/dynamic'

// Static import β€” included in main bundle
// import { HeavyChart } from '@/components/heavy-chart'

// Dynamic import β€” loaded only when rendered
const HeavyChart = dynamic(() => import('@/components/heavy-chart'), {
  loading: () => <div className="h-64 animate-pulse bg-muted rounded-lg" />,
})

export default function DashboardPage() {
  return (
    <main>
      <h1>Dashboard</h1>
      <HeavyChart data={data} />
    </main>
  )
}

Enter fullscreen mode Exit fullscreen mode

When Next.js builds this page, HeavyChart is split into a separate JavaScript chunk. The browser only fetches that chunk when DashboardPage renders and HeavyChart is actually in the tree.

The loading prop

The loading prop renders while the chunk is fetching. Design it to match the real component's dimensions to avoid layout shift:

const RevenueChart = dynamic(() => import('@/components/revenue-chart'), {
  loading: () => (
    <div className="flex h-64 items-center justify-center rounded-lg border bg-muted/30">
      <div className="flex flex-col items-center gap-2 text-muted-foreground">
        <div className="h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" />
        <span className="text-sm">Loading chart...</span>
      </div>
    </div>
  ),
})

Enter fullscreen mode

πŸŽ‰ Mid-Year Sale β€” Unlock Full Article

Base plan from just $4.99/mo or $49/yr

Sign in to read the full article and unlock all 635+ tutorials.

Sign In / Register β€” Start Free Trial

7-day free trial Β· Cancel anytime Β· 30-day money-back