Hello, Code Artisans! đź‘‹
I see you’ve been building some awesome React applications. Components left and right, features piling up. But then you notice something…
”Why does my app take 5 seconds to show up on my phone?” 🤔
The answer often lies in your Bundle Size. By default, React (and tools like Webpack or Vite) bundles everything into one giant JavaScript file. When a user visits your homepage, they are downloading the code for the Settings page, the Dashboard, and the Profile page—even if they haven’t logged in yet!
Today, we’re fixing that. We’re talking about Code Splitting with React.lazy and Suspense.
What is Code Splitting?
Imagine you’re packing for a trip.
- Without Code Splitting: You carry your entire house in your backpack. It’s heavy, and you walk slowly.
- With Code Splitting: You only carry what you need for the day. If you need a swimsuit, you go get it (fetch it) only when you’re at the beach.
In React terms, instead of sending one 5MB file, we send a small 200KB file for the homepage. The rest is downloaded only when the user needs it.
The Dynamic Duo: Lazy & Suspense
React 18 makes this super easy. We don’t need complex configurations anymore.
1. The Old Way (Static Import)
Usually, you import components at the top of your file:
import React from 'react';
import HeavyWidget from './HeavyWidget'; // <--- Loaded immediately!
const App = () => {
return (
<div>
<h1>My App</h1>
<HeavyWidget />
</div>
);
};
This means HeavyWidget is included in the main bundle, even if it’s hidden or at the bottom of the page.
2. The New Way (Lazy Load)
Let’s use React.lazy to import it dynamically.
import React, { lazy, Suspense } from 'react';
// This component is NOT loaded until we try to render it
const HeavyWidget = lazy(() => import('./HeavyWidget'));
const App = () => {
return (
<div>
<h1>My App</h1>
{/* We need Suspense to show something while the code is downloading */}
<Suspense fallback={<div>Loading widget... ⏳</div>}>
<HeavyWidget />
</Suspense>
</div>
);
};
What just happened?
- We replaced
import ... from ...withlazy(() => import(...)). - We wrapped the component in
<Suspense>. - We provided a
fallbackUI (like a spinner or text).
Now, the code for HeavyWidget is split into a separate file (chunk). It is only downloaded from the server when <HeavyWidget /> is actually rendered.
When Should I Use This?
Don’t lazy load everything. Splitting too much can actually hurt performance (too many network requests).
Good Candidates for Lazy Loading:
- Routes: If you use React Router, definitely lazy load your pages (
/dashboard,/settings, etc). - Heavy Components: Big charts, maps, or rich text editors.
- Modals/Dialogs: Things that aren’t visible when the page first loads.
Real World Example: Route-based Splitting
This is the most common use case.
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
const App = () => (
<Router>
{/* One Suspense to rule them all! */}
<Suspense fallback={<div>Loading page...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
</Router>
);
Conclusion
Code splitting is one of the most impactful performance optimizations you can do. It keeps your initial load fast and your users happy.
Start by splitting your Routes. Then, hunt down those heavy components.
Keep it light, keep it fast! 🚀