Code Splitting in React with `lazy` and `Suspense`
Source: Dev.to
Code Splitting in React
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 (
Loading...}>
);
}
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 and should only load when the user clicks a button.useTransitionis used to 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) => (

));
setItems(newItems);
});
};
return (
#### Items Below
{isPending ? (
'Loading...'
) : (
{items}
)}
setShow(!show)} className="btn">
Toggle
{show && (
)}
);
};
export default App;
What Changed in the Network Tab?
After Applying lazy
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 the 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:
{/* one or more lazy‑loaded components */}
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


