LearnContact
Lesson 4122 min read

Nested Routes

Learn how to nest routes inside a shared layout using React Router's <Outlet /> so a persistent sidebar or header stays in place while the inner page content changes.

Introduction

Most real applications are not a flat list of unrelated pages. A dashboard usually has a sidebar and a header that stay on screen while only the main content area changes as you click between "Overview," "Settings," and "Profile." Rebuilding that shared sidebar and header on every single route would be wasteful and error-prone.

React Router solves this with nested routes: a parent "layout" route renders the shared chrome once, and child routes render inside it. This lesson shows you how to structure routes this way and how the <Outlet /> component makes it work.

What You Will Learn
  • Why nested routes exist and what problem they solve.
  • How the <Outlet /> component renders the currently matched child route.
  • How to define a parent layout route with nested children.
  • A worked example of a dashboard layout with multiple nested pages.
  • What an index route is and when to use one.

Why Nested Routes?

Without nesting, every route component would need to manually import and render the sidebar and header itself, duplicating that markup across every page and re-mounting it every time you navigate. Nested routes let you declare the shared layout exactly once, as a parent route, and only swap out the part of the page that actually differs between routes.

Shared Layout

Sidebars, headers, and footers are written once in a parent route instead of every page.

No Remounting

The layout stays mounted across navigations, so it does not flicker or lose its state.

Matches Real UI

Nested routes mirror how UIs are actually structured — layouts containing pages.

Composable

Child routes can themselves have further nested children, any number of levels deep.

The <Outlet /> Component

A parent route renders its own JSX just like any other component, but wherever it wants the matched child route to appear, it renders <Outlet />. React Router looks at the current URL, figures out which child route matches, and renders that child's element in place of the <Outlet />.

Layout.jsx
import { Outlet } from 'react-router-dom';

function DashboardLayout() {
  return (
    <div className="dashboard">
      <Sidebar />
      <main className="dashboard-content">
        {/* The matched child route renders here */}
        <Outlet />
      </main>
    </div>
  );
}

export default DashboardLayout;
Think of <Outlet /> as a Placeholder

It works like the {children} prop pattern, except React Router decides what to put there based on the current URL instead of you passing it explicitly.

Setting Up a Layout Route

To nest routes, give a parent <Route> its own "children" routes instead of an "element" that renders a full page. The parent's element becomes the layout, and each nested <Route> becomes a page that fills the <Outlet />.

App.jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import DashboardLayout from './DashboardLayout';
import Overview from './pages/Overview';
import Settings from './pages/Settings';
import Profile from './pages/Profile';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/dashboard" element={<DashboardLayout />}>
          <Route index element={<Overview />} />
          <Route path="settings" element={<Settings />} />
          <Route path="profile" element={<Profile />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

export default App;

Notice the child paths are relative — "settings" matches /dashboard/settings, and "profile" matches /dashboard/profile — because they are nested inside the "/dashboard" parent path.

Example: A Dashboard Layout

Putting it together, here is a small sidebar with links to each nested page. Clicking a link changes the URL, React Router re-matches the nested routes, and only the <Outlet /> content swaps — the sidebar never unmounts.

Sidebar.jsx
import { Link } from 'react-router-dom';

function Sidebar() {
  return (
    <nav className="sidebar">
      <Link to="/dashboard">Overview</Link>
      <Link to="/dashboard/settings">Settings</Link>
      <Link to="/dashboard/profile">Profile</Link>
    </nav>
  );
}

export default Sidebar;
Visiting /dashboard/settings
[Sidebar: Overview | Settings | Profile]
[Main content: Settings page]

The sidebar stays on screen; only the main content changed.

Index Routes

What should render when someone visits "/dashboard" exactly, with no extra path segment? That is what an index route is for — a child route marked with the "index" prop instead of a "path". It renders inside the parent's <Outlet /> only when the parent's own path is matched exactly.

Index Route
<Route path="/dashboard" element={<DashboardLayout />}>
  <Route index element={<Overview />} />
  <Route path="settings" element={<Settings />} />
</Route>

Index Route Rules

  • An index route has no "path" prop — it uses the boolean "index" prop instead.
  • A route can have at most one index route among its children.
  • It renders as the default content for the parent's exact URL, similar to a home page for that section.

Common Mistakes

Avoid These Mistakes
  • Forgetting to render <Outlet /> in the layout component — the child routes will match but nothing will appear on screen.
  • Using a leading slash on nested child paths (e.g. "/settings" instead of "settings"), which breaks relative nesting.
  • Rebuilding the sidebar or header inside every child page instead of putting it once in the parent layout.
  • Forgetting the "index" prop for the default child route and instead giving it path="" which is not valid.

Best Practices

  • Use nested routes any time multiple pages share the same visual shell, like a sidebar or top nav.
  • Keep layout components focused only on structure — headers, sidebars, and the <Outlet /> — not page-specific logic.
  • Use an index route to define what shows up at the parent's bare URL.
  • Nest routes as deeply as your UI actually requires — a layout inside a layout is completely valid.
  • Name layout components clearly, like DashboardLayout or SettingsLayout, so their purpose is obvious.

Frequently Asked Questions

Can I nest routes more than one level deep?

Yes. A child route can itself have its own nested children with another <Outlet />, allowing layouts within layouts, as deep as your application needs.

What happens if a layout route forgets <Outlet />?

The child route still matches internally, but since there is no <Outlet /> to render it into, nothing from the child route appears on the page.

Do nested route paths need a leading slash?

No. Child route paths are relative to their parent, so you write "settings" rather than "/dashboard/settings".

Is an index route required?

No, it is optional. Without one, visiting the parent's exact path just renders the layout with an empty <Outlet />.

Can each nested page have completely different data or state?

Yes. Each child route's component manages its own state and data fetching independently — only the surrounding layout is shared.

Key Takeaways

  • Nested routes let a parent layout render shared UI once while child routes fill in the changing content.
  • <Outlet /> is the placeholder where React Router renders the currently matched child route.
  • Nested child route paths are relative to their parent's path.
  • An index route defines the default content shown at the parent's exact URL.
  • The layout component stays mounted across child route changes, avoiding flicker and lost state.

Summary

Nested routes are how React Router models the reality that most applications share layout across many pages. A parent route renders the persistent chrome and an <Outlet />, while child routes provide the content that fills it in as the URL changes.

In this lesson, you learned why nested routes matter, how <Outlet /> works, how to declare parent and child routes, and how index routes provide sensible defaults. Next, you will learn how to build links and navigate programmatically between these routes.

Lesson 41 Completed
  • You understand why nested routes avoid duplicating shared layout.
  • You can use <Outlet /> to render matched child routes.
  • You can set up a parent layout route with nested children.
  • You know what an index route does and when to use it.
Next Lesson →

Navigation