Skip to content

React Tutorial Routing and Application Structure

Nested Routes and Layouts with Outlet in React Router

Lesson 108 of 156 2 min read By Deepak Paswan Updated Oct 10, 2026

Full-project lesson · needs Node.js, npm and real project files, so it does not run in the in-page playground.

Most sites have parts that stay the same while the content changes: a header and menu around every page, a sidebar around account pages. Nested routes model that. A parent route renders the shared layout, and its <Outlet /> shows the matching child.

The idea

code
PLAIN
/                 Layout (header + <Outlet />) -> Home
/products         Layout (header + <Outlet />) -> Products list
/products/42      Layout (header + <Outlet />) -> Product detail

The layout renders once and stays mounted while the pages inside it change. State inside the layout (an open menu, a search box) survives navigation.

A layout route with Outlet

Here a layout wraps every page, and a second layout nests inside it for the product section:

src/App.jsx
JSX
import { Routes, Route, NavLink, Link, Outlet, useParams } from 'react-router'

const PRODUCTS = [
  { id: 1, name: 'Keyboard' },
  { id: 2, name: 'Mouse' },
]

// The outer layout: shown on every page
function SiteLayout() {
  return (
    <div>
      <header>
        <strong>Site header</strong>
        <nav style={{ display: 'flex', gap: 16 }}>
          <NavLink to="/" end>Home</NavLink>
          <NavLink to="/products">Products</NavLink>
        </nav>
      </header>
      <main>
        <Outlet /> {/* the matching child route renders here */}
      </main>
    </div>
  )
}

// A second layout, only for the products section
function ShopLayout() {
  return (
    <section>
      <p>Shop layout</p>
      <Outlet />
    </section>
  )
}

function Home() {
  return <h1>Home page</h1>
}

function ProductList() {
  return (
    <div>
      <h1>Our products</h1>
      <ul>
        {PRODUCTS.map((p) => (
          <li key={p.id}><Link to={String(p.id)}>{p.name}</Link></li>
        ))}
      </ul>
    </div>
  )
}

function ProductDetail() {
  const { id } = useParams()
  const product = PRODUCTS.find((p) => p.id === Number(id))
  return product ? <h1>Product {product.id}: {product.name}</h1> : <h1>No product {id}</h1>
}

function App() {
  return (
    <Routes>
      <Route element={<SiteLayout />}>
        <Route index element={<Home />} />
        <Route path="products" element={<ShopLayout />}>
          <Route index element={<ProductList />} />
          <Route path=":id" element={<ProductDetail />} />
        </Route>
      </Route>
    </Routes>
  )
}

export default App

Built and tested in a browser: both layouts stay on screen while the pages change, and a direct visit to /products/2 shows the whole stack.

How the pieces fit

  • A route without a path (the outer one) is a pathless layout route. It adds a layout but does not add anything to the URL.
  • path="products" on the nested group is relative to its parent. Child paths are relative too: :id here means /products/:id.
  • index marks the child to show when the URL is exactly the parent’s.
  • <Outlet /> is the placeholder where the child route renders. A parent without an Outlet shows no children.
  • The Link to={String(p.id)} is relative, so from /products it goes to /products/1.

Sharing data with child routes

A layout can pass values to its children through the Outlet’s context:

App.jsx
JSX
function ShopLayout() {
  const [cart, setCart] = useState([])
  return <Outlet context={{ cart, setCart }} />
}

function ProductDetail() {
  const { cart, setCart } = useOutletContext()
  // ...
}

For data used by many unrelated components, use React Context (Context API). useOutletContext is handy when only the children of one layout need it.

Layouts for different sections

Different parts of the site often need different frames: marketing pages with a big header, an app area with a sidebar, a login page with no frame at all. Give each its own layout route:

App.jsx
JSX
<Routes>
  <Route element={<MarketingLayout />}>
    <Route index element={<Home />} />
    <Route path="pricing" element={<Pricing />} />
  </Route>
  <Route element={<AppLayout />}>
    <Route path="dashboard" element={<Dashboard />} />
  </Route>
  <Route path="login" element={<Login />} />
</Routes>

Layout components and accessibility

  • Use landmarks: <header>, <nav>, <main>, <footer>, one <main> per page.
  • After navigation, move focus to the page heading or the main region, because the browser does not announce a route change; see Accessible Focus States.
  • Update document.title per page. React 19 lets any component render a <title> element, which React places in the document head.

Key points

  • A parent route renders the shared layout and an <Outlet /> for its children.
  • Pathless layout routes add structure without changing the URL; index is the default child.
  • Child paths are relative to their parent; keep one <main> and update the title per page.