Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Layouts and nested routes

Blog pages share a header and a footer. To avoid repeating them in every file you use a layout — a route that renders the shared part and leaves a slot for the nested page. That slot is marked with the Outlet component.

The layout component

Create app/routes/layout.tsx:

// app/routes/layout.tsx
import { Outlet } from "react-router";
 
export default function Layout() {
  return (
    <>
      <header>My blog</header>
      <main>
        <Outlet />
      </main>
      <footer>© 2026</footer>
    </>
  );
}

The nested page whose address is currently open is rendered in place of Outlet.

Connecting the layout

layout(file, [routes]) wraps routes in a layout:

// app/routes.ts
import { type RouteConfig, index, layout, route } from "@react-router/dev/routes";
 
export default [
  layout("routes/layout.tsx", [
    index("routes/home.tsx"),
    route("about", "routes/about.tsx"),
  ]),
] satisfies RouteConfig;

Now both / and /about show the "My blog" header and the footer. layout itself adds nothing to the address.

A nested route with an address

If you pass an array as the third argument of route, the route becomes a parent: its address is the beginning of the children's addresses, and its component is their layout. An index route inside is the page for the parent's own address:

// app/routes.ts
route("dashboard", "routes/dashboard.tsx", [
  index("routes/dashboard-home.tsx"),       // /dashboard
  route("stats", "routes/dashboard-stats.tsx"), // /dashboard/stats
]),

app/routes/dashboard.tsx must also contain an Outlet, otherwise the child pages never appear.

Prefix only

When you need a shared address without a shared component, use prefix:

// app/routes.ts
import { prefix, route } from "@react-router/dev/routes";
 
// inside the routes array:
...prefix("admin", [
  route("posts", "routes/admin-posts.tsx"), // /admin/posts
  route("users", "routes/admin-users.tsx"), // /admin/users
]),

prefix returns an array, so it is spread with the ... operator.

Summary

FunctionAddressComponent
route with childrenAdds a segmentLayout with Outlet
layoutUnchangedLayout with Outlet
prefixAdds a segmentNone
indexThe parent's addressA page

Common mistake

Routes are placed inside layout, but the page shows only the header. The cause is almost always the same — the layout component is missing <Outlet />. The layout decides where the child page goes, and without Outlet it is simply not rendered.

Thanks for reading!

Donate to the site — we work hard for you.