Back to all notes

Dynamic Component Loading: Load Heavy UI on Demand

Use React.lazy and Suspense to split bundles and speed up initial loads.

2 min read

Dynamic component loading means React.lazy and Suspense: parts of your UI load only when needed, the initial bundle stays small, and first paint gets faster.

Not every component is needed at startup. If a dashboard sits behind a toggle, shipping it in the initial bundle means every user pays for it, including the ones who never open it. On a low-end device that’s a slower time-to-interactive for nothing.

The Eager Import

Here Dashboard is imported eagerly, so it’s part of the initial bundle even while hidden:

// app.jsx
import { useState, Suspense, lazy } from "react";
import Dashboard from "./Dashboard"; // eager import, always in initial bundle

function App() {
  const [showDashboard, setShowDashboard] = useState(false);
  return (
    <div>
      <h1>Dynamic Component Loading</h1>
      <button onClick={() => setShowDashboard(!showDashboard)}>
        {showDashboard ? "Hide" : "Show"} Dashboard
      </button>
      {showDashboard && <Dashboard />}
    </div>
  );
}

Load It When Shown

Replace the static import with lazy, and wrap the render in Suspense with a fallback:

// app.jsx
import { useState, Suspense, lazy } from "react";

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

function App() {
  const [showDashboard, setShowDashboard] = useState(false);

  return (
    <div>
      <h1>Dynamic Component Loading</h1>
      <button onClick={() => setShowDashboard(!showDashboard)}>
        {showDashboard ? "Hide" : "Show"} Dashboard
      </button>

      {showDashboard && (
        <Suspense fallback={<p>Loading...</p>}>
          <Dashboard />
        </Suspense>
      )}
    </div>
  );
}
// dashboard.jsx
function Dashboard() {
  return (
    <div>
      <h2>Dashboard</h2>
      <p>This component was loaded dynamically!</p>
    </div>
  );
}
export default Dashboard;

The component only renders behind the state flag, so nothing loads while it’s hidden. Keep the fallback minimal, something fast to paint rather than a heavy spinner, and check your build output afterward to confirm the split actually happened.

Split by routes and large feature panels first. Those usually yield the biggest wins, and if you can predict navigation, combining splits with prefetching gets you the rest of the way.