Chapter 2 of 4

Lazy Loading Components

Splitting the bundle with lazy and Suspense.

lazy takes a function returning a dynamic import and gives you a component. The chunk is fetched the first time the component renders.

import { lazy, Suspense } from "react";

const SettingsPanel = lazy(() => import("./SettingsPanel"));

function App() {
  const [showSettings, setShowSettings] = useState(false);

  return (
    <>
      <button onClick={() => setShowSettings(true)}>Settings</button>
      <Suspense fallback={<Spinner />}>
        {showSettings && <SettingsPanel />}
      </Suspense>
    </>
  );
}

The module must have a default export

// SettingsPanel.jsx
export default function SettingsPanel() { /* ... */ }

// For a named export, map it yourself
const Chart = lazy(() =>
  import("./charts").then((module) => ({ default: module.BarChart }))
);

Preloading

Waiting until the click to start downloading adds a visible delay. Start the fetch on hover or focus, so the chunk is usually ready by the time it is needed.

const load = () => import("./SettingsPanel");
const SettingsPanel = lazy(load);

<button onMouseEnter={load} onFocus={load} onClick={open}>
  Settings
</button>

Handling a failed chunk

A network failure while loading a chunk throws, so pair Suspense with an error boundary - covered in the next tutorial.

<ErrorBoundary fallback={<p>Could not load this section.</p>}>
  <Suspense fallback={<Spinner />}>
    <SettingsPanel />
  </Suspense>
</ErrorBoundary>