Code splitting is the single most impactful performance optimization for SPAs. Dynamic imports make it possible.
Static vs Dynamic Import
// Static import — always loaded, bundled together
import { heavyChart } from "./charts";
// Dynamic import — loaded on demand
const { heavyChart } = await import("./charts");
// Returns a Promise that resolves to the moduleReact Lazy Loading
import { lazy, Suspense } from "react";
// Component loaded only when rendered
const Dashboard = lazy(() => import("./pages/Dashboard"));
const Settings = lazy(() => import("./pages/Settings"));
function App() {
return (
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}Preloading Strategies
// Preload on hover (component will likely be needed)
function NavLink({ to, component, children }) {
const preload = () => component.preload?.();
return (
<Link to={to} onMouseEnter={preload}>
{children}
</Link>
);
}
// Preload after initial render
useEffect(() => {
// Preload routes the user is likely to visit
import("./pages/Dashboard");
import("./pages/Profile");
}, []);Conditional Loading
// Load polyfills only when needed
if (!window.IntersectionObserver) {
await import("intersection-observer");
}
// Load based on user role
const AdminPanel = user.isAdmin
? lazy(() => import("./AdminPanel"))
: () => null;
// Load based on feature flag
if (features.newEditor) {
const { Editor } = await import("./NewEditor");
}Webpack Magic Comments
// Name the chunk
const Comp = lazy(() => import(/* webpackChunkName: "dashboard" */ "./Dashboard"));
// Prefetch (low priority, loaded during idle time)
import(/* webpackPrefetch: true */ "./Settings");
// Preload (high priority, loaded immediately)
import(/* webpackPreload: true */ "./CriticalComponent");Measuring Impact
- Chrome DevTools → Network → filter by JS to see chunk sizes
- Webpack Bundle Analyzer for visual bundle composition
- Lighthouse → "Reduce unused JavaScript" audit
- Target: initial bundle under 200KB gzipped