Modern React applications are rarely flat. As soon as your project scales beyond a handful of pages or features like dashboards, user profiles, and detail pages, you need to structure your routes thoughtfully. This module covers nested routes, dynamic route parameters, and advanced navigation patterns in React Router v6, giving you architectural clarity and full control over your page transitions.
1. What Are Nested Routes? Why Are They Important?
❖ Nested Routes = Layout Reuse
Nested routes allow you to render child routes within a shared layout, without duplicating the layout code across each route.
❖ Nested Routes = Cleaner URLs
Instead of flat routes like /dashboard-settings
, you can organize routes hierarchically:
/dashboard
/dashboard/settings
/dashboard/profile
❖ Nested Routes = Component Composition
React Router v6 embraces the component tree by matching it to the route tree, enabling composition at both layout and logic levels.
2. Creating Nested Routes in React Router v6
Let’s build a nested dashboard.
Routes Setup
jsxCopyEditimport { Routes, Route } from 'react-router-dom';
import Dashboard from './pages/Dashboard';
import Profile from './pages/Profile';
import Settings from './pages/Settings';
export default function AppRoutes() {
return (
<Routes>
<Route path="/dashboard" element={<Dashboard />}>
<Route path="profile" element={<Profile />} />
<Route path="settings" element={<Settings />} />
</Route>
</Routes>
);
}
Dashboard Layout with <Outlet />
In Dashboard.jsx
:
jsxCopyEditimport { Outlet } from 'react-router-dom';
export default function Dashboard() {
return (
<div>
<h1>Dashboard Layout</h1>
<nav>
<Link to="profile">Profile</Link>
<Link to="settings">Settings</Link>
</nav>
<section>
<Outlet /> {/* Renders Profile or Settings here */}
</section>
</div>
);
}
Key points:
- The
Outlet
component is where child routes will be rendered. - The
path
inside children is relative, i.e.,profile
is treated as/dashboard/profile
.
3. Using Index Routes for Defaults
React Router allows index routes for rendering a default component when a parent path is matched.
jsxCopyEdit<Route path="/dashboard" element={<Dashboard />}>
<Route index element={<Welcome />} />
<Route path="profile" element={<Profile />} />
<Route path="settings" element={<Settings />} />
</Route>
When users visit /dashboard
, the Welcome
component renders by default.
4. Route Parameters: Dynamic Routing Made Simple
What Are Route Params?
Route parameters capture dynamic values from the URL.
jsxCopyEdit<Route path="/users/:userId" element={<UserProfile />} />
/users/42
will renderUserProfile
withuserId = 42
Accessing Parameters
jsxCopyEditimport { useParams } from 'react-router-dom';
const UserProfile = () => {
const { userId } = useParams(); // { userId: '42' }
return <div>User ID: {userId}</div>;
};
You can have multiple params like /teams/:teamId/members/:memberId
.
5. Nested Dynamic Routes
You can even nest dynamic segments:
jsxCopyEdit<Route path="/products/:productId" element={<ProductLayout />}>
<Route path="reviews" element={<ProductReviews />} />
<Route path="specs" element={<ProductSpecs />} />
</Route>
Access both productId
and the child routes via useParams()
in child components.
6. Navigation Techniques
React Router offers declarative and programmatic navigation methods.
a. <Link />
Component
jsxCopyEdit<Link to="/dashboard/profile">Profile</Link>
<Link to="settings">Settings</Link> {/* relative to current route */}
b. useNavigate()
Hook
jsxCopyEditimport { useNavigate } from 'react-router-dom';
const SubmitButton = () => {
const navigate = useNavigate();
const handleSubmit = () => {
// Do something
navigate('/dashboard');
};
return <button onClick={handleSubmit}>Submit</button>;
};
c. Navigating With State
You can pass additional data:
jsxCopyEditnavigate('/checkout', { state: { total: 999 } });
Retrieve it in the destination component:
jsxCopyEditconst { state } = useLocation();
console.log(state.total);
7. Wildcard & Fallback Routes
For 404 pages or catching all unmatched routes:
jsxCopyEdit<Route path="*" element={<NotFound />} />
For nested wildcards:
jsxCopyEdit<Route path="docs/*" element={<Documentation />} />
The *
matches any path after /docs
.
8. Shared Layout Pattern (Reusable Containers)
Use a Layout.jsx
to apply shared navigation, headers, or sidebars:
jsxCopyEdit<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="contact" element={<Contact />} />
</Route>
In Layout.jsx
:
jsxCopyEdit<>
<Header />
<main>
<Outlet />
</main>
<Footer />
</>
9. Structuring Routes in Larger Applications
Organize routes in files:
plaintextCopyEditsrc/
routes/
index.jsx
dashboardRoutes.jsx
authRoutes.jsx
Inside dashboardRoutes.jsx
:
jsxCopyEditexport default [
{
path: 'overview',
element: <Overview />
},
{
path: 'settings',
element: <Settings />
}
];
This modular approach improves maintainability.
10. Performance Optimization with Lazy Loading
jsxCopyEditimport { lazy, Suspense } from 'react';
const Settings = lazy(() => import('./pages/Settings'));
<Route path="settings" element={
<Suspense fallback={<Loading />}>
<Settings />
</Suspense>
} />
This reduces initial bundle size and improves perceived performance.
11. Relative Links and Deep Routing UX
When inside a nested route like /dashboard/profile
, use relative paths:
jsxCopyEdit<Link to="settings">Go to Settings</Link> // resolves to /dashboard/settings
Or for parent path:
jsxCopyEdit<Link to="..">Back to Dashboard</Link> // resolves one level up
React Router v6 handles relative paths much more intuitively.
Conclusion
Nested routes, route parameters, and flexible navigation patterns are foundational for creating sophisticated, scalable React applications. Understanding how to build layout-aware routing, manage dynamic segments, and navigate with both user interaction and programmatically gives you full control over your app’s structure and flow.