React Router Cheatsheet

Routes and Nesting

Use this React Router reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Path Patterns

PatternMatchesparams
/about/about only{}
/users/:id/users/42{ id: "42" }
/users/:id/posts/:postId/users/1/posts/9{ id: "1", postId: "9" }
/files/*/files/a/b/c.txt{ "*": "a/b/c.txt" }
/:lang?/about/about and /en/about{ lang: undefined | "en" }
/ with index: trueThe parent's exact path{}
*Anything unmatched (404){ "*": "…" }

Params are always strings, or undefined for an optional segment that did not match. Convert them yourself: Number(params.id) and validate before using it.

<Route path="users/:id" element={<User />} />
<Route path="files/*" element={<Files />} />
<Route path=":lang?/settings" element={<Settings />} />

Ranking, Not Order

React Router scores routes by specificity and matches the best one, so the order you declare them in does not matter. This is the biggest behavioral difference from v5.

/users/new        static segment wins
/users/:id        dynamic segment
/users/*          splat loses to both

So /users/new matches the static route even if the dynamic one is declared first. A single * route is always the last resort, which makes it the natural 404.

Nested Routes

Nesting composes both the URL and the UI. A child's path is appended to its parent's, and a child renders into its parent's <Outlet />.

const router = createBrowserRouter([
  {
    path: "/dashboard",
    element: <DashboardLayout />,     // renders at every /dashboard/* URL
    children: [
      { index: true, element: <Overview /> },        // /dashboard
      { path: "settings", element: <Settings /> },   // /dashboard/settings
      {
        path: "projects",
        element: <ProjectsLayout />,                 // /dashboard/projects/*
        children: [
          { index: true, element: <ProjectList /> }, // /dashboard/projects
          { path: ":id", element: <Project /> },     // /dashboard/projects/7
        ],
      },
    ],
  },
]);

At /dashboard/projects/7 three components are mounted at once: DashboardLayoutProjectsLayoutProject. The layouts keep their state across child navigations, which is what makes a persistent sidebar or an open form survive a URL change.

Index Routes

An index route is the child that renders when the URL is exactly the parent's path. Without one, the parent's <Outlet /> renders nothing.

{ path: "projects", element: <Layout />, children: [
  { index: true, element: <Empty /> },     // /projects
  { path: ":id", element: <Project /> },   // /projects/7
]}

Layout Routes

A route with children and an element but no path wraps its children in shared UI without touching the URL.

[
  {
    element: <MarketingLayout />,          // no path
    children: [
      { path: "/", element: <Home /> },
      { path: "/pricing", element: <Pricing /> },
    ],
  },
  {
    element: <AppLayout />,                // different chrome
    children: [
      { path: "/app", element: <App /> },
    ],
  },
]

Pathless and Absolute Paths

TrickDoes
No path on a parentShared layout, no URL segment
Child path starting with /Absolute, ignores the parent prefix
Parent path with no elementGroups children under a URL prefix with no wrapper UI
{
  path: "/users",
  children: [                        // no element: URL grouping only
    { index: true, element: <UserList /> },
    { path: ":id", element: <User /> },
  ],
}

Reading Params

import { useParams } from "react-router";

function Project() {
  const { id } = useParams();        // always a string
  const projectId = Number(id);
  if (!Number.isFinite(projectId)) throw new Response("Bad id", { status: 400 });
  // …
}

In framework mode, params are typed from the route path:

import type { Route } from "./+types/project";

export default function Project({ params }: Route.ComponentProps) {
  params.id;   // typed as string, inferred from the file's route path
}

Route Matching Utilities

import {
  matchPath,
  matchRoutes,
  useMatch,
  useMatches,
  generatePath,
  resolvePath,
} from "react-router";

matchPath("/users/:id", "/users/42");
// { params: { id: "42" }, pathname: "/users/42", pattern: {…} }

matchPath({ path: "/users/:id", end: false }, "/users/42/posts");

matchRoutes(routes, "/users/42");    // the full matched branch

generatePath("/users/:id/posts/:postId", { id: 1, postId: 9 });
// "/users/1/posts/9"

generatePath("/files/*", { "*": "a/b.txt" });   // "/files/a/b.txt"
function Nav() {
  const match = useMatch("/users/:id");      // null when it does not match
  const matches = useMatches();              // every matched route in the branch
  // matches[i]: { id, pathname, params, data, handle }
}

useMatches plus a handle on each route is the standard way to build breadcrumbs, since it gives you the whole active branch with each route's own metadata.

{ path: "projects", element: <Projects />, handle: { crumb: () => "Projects" } }

function Breadcrumbs() {
  const matches = useMatches();
  const crumbs = matches
    .filter((m) => Boolean(m.handle?.crumb))
    .map((m) => m.handle.crumb(m.data));
  return <ol>{crumbs.map((c, i) => <li key={i}>{c}</li>)}</ol>;
}

Route Object Reference

PropertyPurpose
pathThe URL pattern
indexThis is the parent's index route
element / ComponentWhat to render
lazyAsync import of the route's module
childrenNested routes
loaderFetch data before render
actionHandle a submission
errorElement / ErrorBoundaryCatch errors from this subtree
hydrateFallbackElementShown during SSR hydration with no data
shouldRevalidateOpt out of revalidation
handleArbitrary metadata for useMatches
caseSensitiveMatch the path case-sensitively
idA stable id for useRouteLoaderData

File-Based Routes (Framework Mode)

Framework mode's routes.ts is explicit by default, but a file-convention plugin is available.

import { type RouteConfig, index, route, layout, prefix } from "@react-router/dev/routes";

export default [
  index("routes/home.tsx"),                       // /
  route("login", "routes/login.tsx"),             // /login
  layout("routes/app-layout.tsx", [
    ...prefix("app", [
      index("routes/app-home.tsx"),               // /app
      route(":id", "routes/app-detail.tsx"),      // /app/:id
      route("*", "routes/app-404.tsx"),
    ]),
  ]),
] satisfies RouteConfig;
// or the flat file convention
import { flatRoutes } from "@react-router/fs-routes";
export default flatRoutes();
HelperAdds
index(file)An index route
route(path, file, children?)A path route
layout(file, children)A pathless layout
prefix(path, children)A URL prefix with no layout