Dynamic Component Loading: Load Heavy UI on Demand
Use React.lazy and Suspense to split bundles and speed up initial loads.
Introduction
Dynamic component loading uses React.lazy and Suspense to load parts of your UI only when needed. This helps keep initial bundle size small and speeds up first paint.
Why this matters
Not all components are needed at startup. Lazily loading heavy routes and panels reduces initial bundle size and improves perceived performance.
The problem
Dashboard is imported eagerly, so it’s always part of the initial bundle—even when hidden.
Inefficient approach
Eagerly importing heavy components inflates your initial bundle and delays first paint:
// 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>
);
}
Issues:
- Users pay the cost even if they never open the dashboard.
- Slower TTI on low-end devices and networks.
The solution
Load Dashboard with lazy and wrap it with Suspense for 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;
Knowledge base
Problem snippet:
import Dashboard from "./Dashboard";
{showDashboard && <Dashboard />}
Solution snippet:
const Dashboard = lazy(() => import("./Dashboard"));
{showDashboard && (
<Suspense fallback={<p>Loading...</p>}>
<Dashboard />
</Suspense>
)}
Step-by-step
-
Replace static import with
const Dashboard = lazy(() => import("./Dashboard")). -
Wrap the lazy component with
<Suspense fallback={...}>. -
Toggle rendering behind a state flag to avoid loading when hidden.
-
Keep the fallback minimal for fast first paint.
-
Confirm code-splitting in your build output.
Tips
-
Split by routes and large feature panels first—they usually yield the biggest wins.
-
Keep fallbacks simple and fast; avoid heavy spinners that delay paint.
-
Combine with prefetching when you can predict navigation.