Back to Blog
Lesson 18 of the Intermediate React: Hooks, State & Data Patterns course
ReactJune 26, 20264 min read

Nested Routes and Layouts: Mastering React Router UI Design

Learn how to use nested routes and the Outlet component in React Router to build consistent page layouts, reduce code duplication, and improve UI design.

ReactReact RouterUI DesignFrontend ArchitectureRoutingjavascriptfrontend

Previously in this course, we explored Introduction to React Router: Building Multi-Page SPAs and expanded that knowledge by implementing Dynamic Routing with URL Parameters in React Router. While we can now navigate between pages, our current approach forces us to manually include navigation bars or sidebars in every single component.

Today, we move beyond simple page-to-page navigation. We’ll learn how to structure our application using nested routes to create persistent UI layouts, ensuring our dashboard feels like a cohesive single-page application rather than a collection of disconnected files.

Understanding Nested Routes from First Principles

In a standard web application, "layout" usually refers to the parts of the page that don't change when you navigate—think of a sidebar, a header, or a footer. Without nested routes, you would find yourself importing these components into every page component, which is a maintenance nightmare.

Nested routes allow us to define a parent route that acts as a container for shared UI, while "child" routes represent the specific content that changes within that container.

The mechanism that powers this is the Outlet component. When you define a route hierarchy, the Outlet tells React Router: "Render the child route component right here." It’s essentially a placeholder that gets swapped out dynamically as the URL changes, while the parent layout stays mounted and interactive.

Implementing a Consistent Dashboard Layout

Let’s advance our dashboard project. We want a persistent sidebar that stays on screen while the main content area updates.

Step 1: Create the Parent Layout

First, we define a component that contains the common UI.

JSX
import { Outlet, Link } from CE9178">'react-router-dom';

export default function DashboardLayout() {
  return (
    <div className="dashboard-container">
      <nav className="sidebar">
        <ul>
          <li><Link to="/dashboard">Overview</Link></li>
          <li><Link to="/dashboard/settings">Settings</Link></li>
        </ul>
      </nav>
      
      <main className="content">
        {/* This is where child routes will render */}
        <Outlet />
      </main>
    </div>
  );
}

Step 2: Configure the Route Hierarchy

Now, we inform react-router-dom about this relationship. We nest the specific dashboard pages inside the parent DashboardLayout route.

JSX
import { BrowserRouter, Routes, Route } from CE9178">'react-router-dom';
import DashboardLayout from CE9178">'./DashboardLayout';
import Overview from CE9178">'./Overview';
import Settings from CE9178">'./Settings';

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

In this configuration, when the user visits /dashboard, the DashboardLayout renders, and the Overview component is injected into the Outlet. When they visit /dashboard/settings, the DashboardLayout remains mounted, but the Outlet swaps Overview for Settings.

Hands-on Exercise

  1. Open your current project and create a new file DashboardLayout.jsx following the example above.
  2. Update your App.jsx routing configuration to nest at least two child routes under a single parent layout.
  3. Add a "User Profile" link to your sidebar and create a corresponding route.
  4. Verify that the sidebar does not re-render (e.g., add a console.log in DashboardLayout and navigate between pages—it should only log once).

Common Pitfalls

  • Forgetting the <Outlet />: If you define nested routes but forget to place the Outlet component in your parent layout, the child routes will simply fail to render. The parent will show up, but the main content area will remain empty.
  • Absolute vs. Relative Paths: In your Link components inside the sidebar, ensure you are using relative paths (e.g., to="settings") or absolute paths that match the route tree. Mixing them up is the most common cause of "404 Not Found" errors during navigation.
  • Over-nesting: While nesting is powerful, don't nest for the sake of nesting. Deeply nested route trees become difficult to read and manage. Keep your layout hierarchy shallow—usually 2 or 3 levels is plenty for a complex dashboard.

Recap

By using nested routes, we've successfully decoupled our UI structure from our page content. This pattern not only cleans up our component files by removing repeated navigation logic but also enables smoother UI transitions, as the parent layout component isn't destroyed and re-mounted during navigation. This is a foundational step toward building the Advanced React Patterns: Scaling Your Architecture for Production we’ll need for the rest of our dashboard.

Up next: We will secure our dashboard by creating Protected Routes that redirect unauthenticated users away from private content.

Similar Posts