React Nested Routes with Outlet, Layouts, and Index Routes

Build nested routes in React Router with Outlet, shared layouts, relative child paths, and index routes. Includes a minimal example and fixes for blank child pages.

Last reviewed: November 2026

·React 18React 19React Router v6React Router v7
✅
Quick Understanding: Nested routes map parent and child URLs to nested UI. A parent layout renders <Outlet />, which React Router fills with the matched child page. The shared layout can stay mounted across sibling navigation, preserving its state; it can still re-render.

🔍 What Is It?

Nested routes are routes defined as children of a parent route. The parent renders a layout component that includes an <Outlet /> placeholder. When a child route matches, React Router renders the child's component inside the <Outlet />.

<Route path="/dashboard" element={<DashboardLayout />}>
  <Route path="overview" element={<OverviewPage />} />
  <Route path="claims" element={<ClaimsPage />} />
  <Route path="settings" element={<SettingsPage />} />
</Route>

Navigating between /dashboard/overview and /dashboard/claims:

  • →DashboardLayout stays mounted (sidebar, header persist)
  • →Only the content inside <Outlet /> changes

🎯 Why Do We Need It?

Without nested routes, every page component would need to include its own header, sidebar, and navigation. This means:

  • →Duplicated layout code across every page
  • →Layout components remounting on navigation (losing scroll position, state)
  • →Inconsistent UI if any page forgets a layout element

Nested routes separate shared layout from child content. The layout can remain mounted as matched children change; it can still re-render in response to navigation or other React updates.

🏗️ What Happens in Real Projects?

Every enterprise app uses nested routes:

  • →Dashboard apps — sidebar + header layout wraps all dashboard pages
  • →Settings pages — settings nav on the left, specific settings on the right
  • →Admin panels — admin layout with breadcrumbs wraps all admin pages
  • →Multi-step wizards — progress indicator persists across all steps

⚙️ How It Works

  1. Define parent route with a layout component that includes <Outlet />
  2. Define child routes nested inside the parent in the route config
  3. When a child route matches, React Router renders the child's element in place of <Outlet />
  4. The parent layout can stay mounted during sibling navigation. It can still re-render when location, context, props, or state change; re-rendering does not imply remounting.

Index routes:

<Route index element={<DefaultPage />} /> — renders when the parent path matches exactly (no child path). Used to show a default view at the parent URL. An index route has no path and cannot have children.

Layout routes versus path prefixes

A route with an element and no path shares UI without adding a URL segment. A route with a path and no element adds a URL prefix without introducing a layout component. If a parent element renders UI, it needs an <Outlet /> for its child elements to appear.

Blank child page checklist

  • →Put <Outlet /> in every layout or authentication wrapper that has child route elements.
  • →Nest child <Route> elements inside the intended parent route.
  • →Use relative child paths such as settings, which inherit the parent prefix.
  • →Add an index route if the parent URL should show a default child view.
  • →If children are declared in a separate descendant <Routes>, the ancestor route needs a trailing /* to match deeper URLs. Directly nested route declarations do not need that splat.

💻 Example

Nested Routes in a Dashboard Application — Layout component with Outlet and nested route configuration.

tsx
import { BrowserRouter, NavLink, Outlet, Route, Routes } from 'react-router-dom';

function DashboardLayout() {
  return (
    <section>
      <h1>Dashboard</h1>
      <nav>
        <NavLink to="/dashboard" end>Overview</NavLink>
        <NavLink to="settings">Settings</NavLink>
      </nav>
      <Outlet />
    </section>
  );
}

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

// /dashboard          -> DashboardLayout + Overview page
// /dashboard/settings -> DashboardLayout + Settings page

🤔 Common Confusions

❓ What is the difference between index routes and default routes?

An index route (<Route index />) renders at the parent path exactly (/dashboard). A redirect (<Navigate to="overview" />) sends the user to a different path. Use index routes when you have meaningful content for the parent URL. Use redirects when the parent URL should always show a specific child.

❓ Why does my layout re-render on every navigation?

A layout may re-render when location, state, context, or props change, even when it stays mounted. Unexpected remounts can come from changing keys or component types, or placing the layout in separate route branches. React.memo does not determine component identity or guarantee that a layout never renders again.

🚨 Common Production Issues

**Relative vs absolute paths in nested routes** — Child route paths are relative to the parent by default in v6. <Route path="claims"> inside a /dashboard parent matches /dashboard/claims. Do not add a leading slash to child paths.

**Outlet not rendering** — If the layout renders but content is blank, check that <Outlet /> is in the layout and child routes are nested inside the parent <Route> (not siblings).

✅ Best Practices

Use layout routes for any UI that persists across multiple pages

Use index routes for the default view at a parent URL

Keep child route paths relative (no leading slash)

Use NavLink in layout navigation for automatic active state

Use Outlet context for passing layout-level data to children

🏛️ Architect's Note

Senior Architect's Perspective

Nested routes are the correct model for any multi-page app with shared layouts. The key insight: route nesting should mirror UI nesting. If the sidebar persists across pages, those pages should be nested inside a layout route that contains the sidebar. This keeps layouts DRY, prevents remounting, and makes the route structure a readable map of the application's UI.

💬 Frequently Asked Questions

How do I create nested routes in React Router?

Place child Route elements inside a parent Route and render Outlet in the parent layout. A child path such as settings beneath /dashboard matches /dashboard/settings. Wrap the route tree in a router such as BrowserRouter.

Why is my nested route not rendering inside Outlet?

Check that the parent layout renders Outlet and the child Route is actually nested beneath that parent. If the URL is exactly the parent path, add an index route. An authentication wrapper also needs to render Outlet when access is allowed.

What is an index route?

An index route renders the default child at the parent URL. Declare Route with index and an element, without a path. It does not add a URL segment and cannot have child routes.

Should nested child paths start with a slash?

Prefer relative paths such as settings so the child inherits its parent URL prefix. React Router also supports correctly matched absolute child paths, but they must include the complete parent path and are less convenient when moving a route subtree.

Does a shared layout never re-render?

No. A shared layout may remain mounted during navigation between its children, preserving its state. It can still re-render when location, context, state, or props change. Mounting and rendering are different events.

Do nested routes require a wildcard path?

A directly nested Route tree does not require a trailing wildcard on its parent. If the parent renders a component containing a separate descendant Routes tree, use a parent path such as /dashboard/* so deeper URLs continue matching.

Can I pass data from a layout to child routes via Outlet?

Yes. Use the context prop on Outlet: <Outlet context={{ user }} />. Then in the child, use the useOutletContext hook: const { user } = useOutletContext<{ user: User }>(). This is useful for passing layout-level data (like the current section's data) to all child pages.

🎯 Key Takeaways

1Shared layouts can stay mounted across sibling navigation, but may still re-render
2<Outlet /> in the parent layout is where child routes render
3Index routes render at the parent URL exactly
4Child route paths are relative to the parent — no leading slash
5Use NavLink for navigation menus to get automatic active state
6Route nesting should mirror UI nesting

🎯 Practice this topic in interviews

↗ Where this topic fits

Sources

Sources / References

Use these React references to verify the concept in official docs and continue into the most relevant SplashRide pages.

Related SplashRide pages

SplashRide React learning path

Browse the full React topic collection and learning order on SplashRide.

React Interview Prep

Practice React interview questions and scenario-based answers.

Frontend Engineer Career Path

A beginner-friendly journey through web foundations, React, production frontend engineering, and interview preparation.

Python Developer Career Path

A practical route through Python fundamentals, automation, backend basics, testing, projects, interview preparation, and career growth.

React Router

Master React Router v6 — the standard routing library for React SPAs. Learn route definitions, navigation, URL params, and the patterns senior engineers use in production applications.

Protected Routes

Implement protected routes in React — the pattern for restricting access to authenticated users and specific roles. Learn the correct implementation pattern used in production applications.