Script Valley
Web Performance Fundamentals
JavaScript PerformanceLesson 4.2

How to code-split a JavaScript bundle with dynamic imports

dynamic import(), route-based splitting, component-level splitting, Webpack/Vite code splitting, prefetch, preload hints, React.lazy, loading states

Code Splitting with Dynamic Imports

Code splitting breaks a monolithic bundle into smaller chunks loaded on demand. Users download only what they need for the current page, reducing initial parse and execution cost directly.

The native browser API is import():

// Static import — always in the initial bundle
import { heavyChart } from './chart-library';

// Dynamic import — separate chunk, loaded on demand
const renderChart = async () => {
  const { heavyChart } = await import('./chart-library');
  heavyChart('#container', data);
};

In React, combine with React.lazy and Suspense for route-level splitting:

import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';

const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));

export function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/settings" element={<Settings />} />
      </Routes>
    </Suspense>
  );
}

Prefetch chunks the user will likely need next using a magic comment:

const Settings = lazy(() =>
  import(/* webpackPrefetch: true */ './pages/Settings')
);

Vite handles code splitting automatically at the import() boundary — no configuration needed. Webpack requires the optimization.splitChunks config or dynamic imports to trigger splitting.

Up next

How to tree-shake unused JavaScript with ES modules

Sign in to track progress