Code Splitting with lazy

Load components on demand to shrink the initial bundle.

Syntaxconst Page = lazy(() => import('./Page.jsx'));

Code splitting breaks your app into smaller chunks that load only when needed. lazy lets you import a component dynamically, and Suspense shows a fallback while its chunk downloads.

Where it helps

  • Routes that most users never visit.
  • Heavy components like charts or rich editors.

The result is a smaller initial download and a faster first paint.

Example

Example Β· javascript
import { Suspense, lazy, useState } from 'react';

const Chart = lazy(() => import('./Chart.jsx'));

function Report() {
  const [show, setShow] = useState(false);
  return (
    <div>
      <button onClick={() => setShow(true)}>Show chart</button>
      {show && (
        <Suspense fallback={<p>Loading chart...</p>}>
          <Chart />
        </Suspense>
      )}
    </div>
  );
}

When to use it

  • An admin panel lazy-loads the Reports section so users who only use the Dashboard never download the charting and export libraries.
  • A React Router app wraps each route component in lazy() so each page's JavaScript downloads only when the user navigates to that route for the first time.
  • A low-end device optimisation splits the rich text editor into a lazy chunk so the initial page load stays under 50 kB even though the editor is 200 kB.

More examples

Lazy route per page

Each route is lazy-imported so only the current page's JS chunk is downloaded on first visit.

Example Β· jsx
import { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';

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

function App() {
  return (
    <Suspense fallback={<p>Loading page...</p>}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/settings" element={<Settings />} />
        <Route path="/reports" element={<Reports />} />
      </Routes>
    </Suspense>
  );
}

Lazy-load a heavy modal

Downloads the editor bundle only when the modal is opened, not when the page first loads.

Example Β· jsx
import { lazy, Suspense, useState } from 'react';

const RichTextEditor = lazy(() => import('./RichTextEditor'));

function NewPostButton() {
  const [open, setOpen] = useState(false);
  return (
    <>
      <button onClick={() => setOpen(true)}>New Post</button>
      {open && (
        <Suspense fallback={<div className="modal-skeleton" />}>
          <RichTextEditor onClose={() => setOpen(false)} />
        </Suspense>
      )}
    </>
  );
}

Prefetch chunk on hover

Triggers the dynamic import on hover so the chunk is already cached by the time the user clicks.

Example Β· jsx
const ReportsPage = React.lazy(() => import('./ReportsPage'));

// Prefetch the chunk when the user hovers the link
function NavLink() {
  const prefetch = () => import('./ReportsPage');
  return (
    <a href="/reports" onMouseEnter={prefetch}>
      Reports
    </a>
  );
}

Discussion

  • Be the first to comment on this lesson.