Josué Hernández
In React applications, handling complex navigation efficiently is crucial for scalability, performance, and maintainability. Two essential techniques to achieve this are Nested Routes and Lazy Loading. These approaches help structure routes logically and improve load times by deferring unnecessary component loading.
In this blog, we'll explore how Nested Routes and Lazy Loading work, their benefits, and how to implement them in a React Router project.
Nested Routes allow child routes to be rendered inside a parent route, maintaining a structured navigation system. This is useful when dealing with dashboards, multi-step forms, user profiles, and settings pages, where child views depend on a parent context.
For example, instead of defining separate routes like:
/dashboard
/overview
/settings
We can structure them as:
/dashboard (Parent Route)
/dashboard/overview (Child Route)
/dashboard/settings (Child Route)
This makes navigation more organized and intuitive.
We use React Router's Outlet component to render child components dynamically inside a parent layout.
import { BrowserRouter as Router, Routes, Route, Link, Outlet } from "react-router-dom";
import Dashboard from "./Dashboard";
import Overview from "./Overview";
import Settings from "./Settings";
function App() {
return (
<Router>
<nav>
<Link to="/">Home</Link>
<Link to="/dashboard">Dashboard</Link>
</nav>
<Routes>
<Route path="/" element={<h2>Welcome to the App</h2>} />
<Route path="/dashboard" element={<Dashboard />}>
<Route path="overview" element={<Overview />} />
<Route path="settings" element={<Settings />} />
</Route>
</Routes>
</Router>
);
}
export default App;
The Dashboard component acts as a wrapper for nested routes and contains a navigation system to navigate between Overview and Settings.
import { Link, Outlet } from "react-router-dom";
const Dashboard = () => {
return (
<div>
<h2>Dashboard</h2>
<nav>
<Link to="overview">Overview</Link>
<Link to="settings">Settings</Link>
</nav>
{/* Renders the child route component dynamically */}
<Outlet />
</div>
);
};
export default Dashboard;
Each child route will display specific content inside the Dashboard layout.
Overview Component:
const Overview = () => <h3>Dashboard Overview</h3>;
export default Overview;
Settings Component:
const Settings = () => <h3>Account Settings</h3>;
export default Settings;
Lazy Loading (or Code Splitting) delays the loading of components until they are needed, reducing the initial JavaScript bundle size and improving performance.
Instead of loading everything upfront, we can dynamically import components only when a user navigates to them.
React provides React.lazy() to defer component loading. We wrap it in Suspense to show a fallback UI while loading.
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import { lazy, Suspense } from "react";
import Dashboard from "./Dashboard";
const Overview = lazy(() => import("./Overview"));
const Settings = lazy(() => import("./Settings"));
function App() {
return (
<Router>
<nav>
<Link to="/">Home</Link>
<Link to="/dashboard">Dashboard</Link>
</nav>
<Routes>
<Route path="/" element={<h2>Welcome to the App</h2>} />
<Route path="/dashboard" element={<Dashboard />}>
<Route
path="overview"
element={
<Suspense fallback={<p>Loading Overview...</p>}>
<Overview />
</Suspense>
}
/>
<Route
path="settings"
element={
<Suspense fallback={<p>Loading Settings...</p>}>
<Settings />
</Suspense>
}
/>
</Route>
</Routes>
</Router>
);
}
export default App;
✅ Initial Load Optimization: The app loads only essential components.
✅ On-Demand Loading: Additional components load only when needed.
✅ Faster Page Load Time: Improves first meaningful paint (FMP) and time to interactive (TTI).
✅ Efficient for Large Applications: Reduces unused JavaScript, keeping the app responsive.
The Suspense component allows us to display a loading message or spinner while fetching lazy-loaded components:
<Suspense fallback={<p>Loading...</p>}>
<Overview />
</Suspense>
🔥 Use Nested Routes for Modular Design: Helps maintain cleaner, structured navigation.
🔥 Keep Routes Readable: Avoid deep nesting beyond two or three levels.
🔥 Leverage Lazy Loading Smartly: Load large components dynamically to improve performance.
🔥 Use a Meaningful Fallback UI: A spinner or skeleton loader enhances user experience.
🔥 Avoid Overusing Lazy Loading: Too many lazy-loaded components may increase latency.
By combining Nested Routes and Lazy Loading, we can build scalable React applications with an optimized structure and better performance. Nested Routing helps organize navigation, while Lazy Loading ensures only required components load when needed, boosting speed and efficiency.
This is just the beginning! Future blogs will explore protected routes, authentication-based navigation, and performance optimization techniques. 🚀