As your React applications grow, so does the bundle size, and that means slower loading times for users. Route-based code splitting solves this by loading only the JavaScript required for the page a user is viewing. Instead of sending every component in one huge file, React can intelligently load code “on demand,” giving your app blazing-fast speed and a better user experience.
This guide will walk through what route-based code splitting is, how to implement it with React.lazy and Suspense, why it improves performance, and practical examples that beginners can easily follow.
What is Route-Based Code Splitting?
In simple terms, Code Splitting breaks your React app into smaller chunks instead of one big bundle. Route-based code splitting splits your code by routes, meaning only the code needed for that specific route is loaded when a user navigates to it.
Problem (Without Code Splitting)
When a React app grows, all your pages and dependencies are bundled into one large file by Webpack. As a result, even visiting the homepage downloads the entire app — including code for other unused pages like login, profile, and dashboard.
Solution
With route-based code splitting, the homepage only fetches the code it needs. When users navigate to another route, React dynamically loads that route’s code using React.lazy and Suspense.
How to Implement Route-Based Code Splitting (React Router v6)
You can achieve route-based code splitting easily using two features:
- React.lazy() to lazy-load a component
- React.Suspense to show fallback UI while the component loads
Step-by-Step Example
import React, { lazy, Suspense } from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
// Lazy load components
const Home = lazy(() => import("./pages/Home"));
const About = lazy(() => import("./pages/About"));
const Dashboard = lazy(() => import("./pages/Dashboard"));
function App() {
return (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
</Router>
);
}
export default App;
Here:
- React.lazy() ensures that each route component is loaded only when needed.
- Suspense displays a fallback (like a spinner) while the lazy-loaded component is fetched.
React Lazy and Suspense Explained
React.lazy()
const Profile = React.lazy(() => import('./Profile'));
React.Suspense
<Suspense fallback={<div>Loading profile...</div>}>
<Profile />
</Suspense>
Adding a Fallback UI with Suspense
import React from "react";
function Loader() {
return <div className="spinner">Loading...</div>;
}
<Suspense fallback={<Loader />}>
<Dashboard />
</Suspense>
Lazy Loading Components Conditionally
const Chart = lazy(() => import("./Chart"));
{showChart && (
<Suspense fallback={<div>Loading Chart...</div>}>
<Chart />
</Suspense>
)}
Preloading and Prefetching Routes for Faster UX
import("./pages/Dashboard");
Error Boundaries for Lazy-Loaded Routes
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
return this.state.hasError ? <h3>Something went wrong!</h3> : this.props.children;
}
}
<ErrorBoundary>
<Suspense fallback={<Loader />}>
<Routes>{/* routes here */}</Routes>
</Suspense>
</ErrorBoundary>
Code Splitting with Webpack and React Router
import("./Dashboard").then(module => {
const Dashboard = module.default;
});
How Code Splitting Improves Performance
- Reduces initial load time: Only essential JavaScript for the current view loads.
- Optimizes network usage: Large bundles aren’t downloaded all at once.
- Improves SEO and Core Web Vitals: Faster render time lowers page bounce rate.
- Enhances perceived performance: Fallbacks and skeletons maintain fluid UX.
Best Practices for Route-Based Code Splitting
- Split code only where necessary — don’t overuse lazy loading.
- Provide meaningful and responsive fallback UIs.
- Avoid lazy loading very small components; the overhead may not be worth it.
- Preload frequently used pages for smooth transitions.
- Monitor bundle sizes using tools like Webpack Bundle Analyzer.
- Combine route-based and component-based splitting for best results.
