In React, code splitting allows us to load parts of our application only when they’re actually needed. Instead of downloading all JavaScript upfront, we can defer loading less important or heavy components until the user interacts with the UI.
This is especially useful for large applications or components that users may never see.
Why Code Splitting Matters
Benefits
Improved performance
Smaller initial JavaScript bundles mean faster page load times, especially on slow networks or low-end devices.Better user experience
Only essential code is loaded on first render. Additional code is fetched later, exactly when the user needs it.
The Problem: Unnecessary Network Requests
Before using React.lazy and Suspense, the SlowComponent was already loaded in the Network tab, even though:
- The toggle button was
false - The user never opened or saw the component
This means the browser downloaded code that the user might never use.
Before (without lazy loading)
SlowComponent is loaded immediately on page load:
This is wasted bandwidth and unnecessary work.
The Solution: React.lazy + Suspense
React provides lazy and Suspense to load components only when they’re rendered.
Basic Example
import React, { lazy, Suspense } from 'react';
const DataComponent = lazy(() => import('./DataComponent'));
function MyComponent() {
return (
<Suspense fallback={<div>Loading...</div>}>
<DataComponent />
</Suspense>
);
}
Here:
-
DataComponentis not loaded until it’s actually rendered - A fallback UI is shown while the component is loading
Practical Example with a Heavy Component
In this example:
-
SlowComponentis heavy - It should only load when the user clicks a button
- We also use
useTransitionto keep the UI responsive
import { useState, useTransition, lazy, Suspense } from 'react';
const SlowComponent = lazy(() => import('./SlowComponent'));
const App = () => {
const [text, setText] = useState('');
const [items, setItems] = useState([]);
const [isPending, startTransition] = useTransition();
const [show, setShow] = useState(false);
const handleChange = (e) => {
setText(e.target.value);
startTransition(() => {
const newItems = Array.from({ length: 5000 }, (_, index) => (
<div key={index}>
<img src="/vite.svg" alt="" />
</div>
));
setItems(newItems);
});
};
return (
<section>
<form className="form">
<input
type="text"
className="form-input"
value={text}
onChange={handleChange}
/>
</form>
<h4>Items Below</h4>
{isPending ? (
'Loading...'
) : (
<div
style={{
display: 'grid',
gridTemplateColumns: '1fr 1fr 1fr',
marginTop: '2rem',
}}
>
{items}
</div>
)}
<button onClick={() => setShow(!show)} className="btn">
Toggle
</button>
{show && (
<Suspense fallback={<h4>Loading...</h4>}>
<SlowComponent />
</Suspense>
)}
</section>
);
};
export default App;
What Changed in the Network Tab?
After Applying lazy
Now, SlowComponent is not loaded on initial page load:
Loaded Only on User Action
The component is fetched only when the user clicks the toggle button:
This is exactly what we want.
Key Takeaways
Lazy loading
SlowComponentis not downloaded until it’s rendered.Suspense
Handles loading states while the component is fetched.Better performance
No unnecessary JavaScript is loaded upfront.User-driven loading
Code is fetched only when the user actually needs it.
Optional: Wrapping More with Suspense
If your application contains multiple lazy-loaded components, you can wrap a larger portion of your component tree with Suspense.
In many cases, it’s common to wrap the entire return statement so all lazy components share the same loading fallback.
<Suspense fallback={<h4>Loading...</h4>}>
{/* one or more lazy-loaded components */}
</Suspense>
This approach keeps your code cleaner and ensures a consistent loading experience across the app, while still benefiting from code splitting.
Conclusion
Before using React.lazy and Suspense, SlowComponent was downloaded even when the toggle was false, meaning users paid the cost for code they might never see.
By using code splitting, we:
- Reduce the initial bundle size
- Improve performance
- Load code only when it’s truly needed
Credits: John Smilga’s course



Top comments (0)