React Router Cheatsheet

Basics

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

What React Router Is

React Router maps URLs to React components. It keeps the URL and the UI in sync, so the browser's address bar, back button, bookmarks, and deep links all work in a single-page app.

Version 6.4 added a data layer (loaders, actions, useFetcher) that moved data fetching into the router. Version 7 merged the Remix framework into React Router, so the same package can run in three modes.

ModeYou getUse when
DeclarativeRouting only, fetch data yourselfAdding routes to an existing SPA
DataLoaders, actions, pending states, no bundler opinionYou want the data APIs but own your build
FrameworkData mode plus a Vite plugin, SSR, code splitting, typegenStarting a new app

This reference covers all three, and marks where an API is data or framework mode only.

Install

npm i react-router            # v7: one package for web
npm i react-router-dom        # v6 and earlier: the web bindings
npx create-react-router@latest my-app   # v7 framework mode

In v7 the package is just react-router. react-router-dom still exists as a re-export shim so v6 code keeps working, but new imports should come from react-router.

// v6
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
// v7
import { BrowserRouter, Routes, Route, Link } from "react-router";

Minimal Declarative Setup

import { BrowserRouter, Routes, Route, Link, Outlet } from "react-router";

function Layout() {
  return (
    <div>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>
      <Outlet />
    </div>
  );
}

export default function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route element={<Layout />}>
          <Route index element={<Home />} />
          <Route path="about" element={<About />} />
          <Route path="users/:id" element={<User />} />
          <Route path="*" element={<NotFound />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

<Outlet /> is where a parent route renders its matched child. A layout route with no path groups children under shared chrome without adding a URL segment.

Router Types

RouterRuns whereNotes
createBrowserRouterBrowserReal URLs via the History API. The default choice.
createHashRouterBrowserURLs like /#/about. For static hosts that cannot rewrite.
createMemoryRouterAnywhereKeeps history in memory. Tests and React Native.
createStaticRouterServerSSR on the server side.
BrowserRouterBrowserDeclarative mode, no data APIs.
HashRouterBrowserDeclarative, hash URLs.
MemoryRouterAnywhereDeclarative, in memory. Best for tests.
StaticRouterServerDeclarative SSR.

Only the create*Router functions support loaders, actions, and useFetcher. If a data API throws "may be used only in the context of a data router", this is why.

Data Router Setup

import { createBrowserRouter, RouterProvider } from "react-router";

const router = createBrowserRouter([
  {
    path: "/",
    element: <Layout />,
    errorElement: <ErrorPage />,
    children: [
      { index: true, element: <Home />, loader: homeLoader },
      {
        path: "users/:id",
        element: <User />,
        loader: userLoader,
        action: userAction,
      },
      { path: "*", element: <NotFound /> },
    ],
  },
]);

createRoot(document.getElementById("root")).render(
  <RouterProvider router={router} />
);

The same tree can be written with JSX and converted, which is convenient when migrating from <Routes>:

import { createRoutesFromElements, Route } from "react-router";

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path="/" element={<Layout />} errorElement={<ErrorPage />}>
      <Route index element={<Home />} loader={homeLoader} />
      <Route path="users/:id" element={<User />} loader={userLoader} />
    </Route>
  )
);

Framework Mode Setup

// vite.config.ts
import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [reactRouter()],
});
// app/routes.ts
import { type RouteConfig, index, route, layout } from "@react-router/dev/routes";

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

export default {
  ssr: true,          // false for a pure SPA build
  prerender: ["/", "/about"],
} satisfies Config;

In framework mode each route file exports its own loader, action, default component, ErrorBoundary, and meta, and the dev plugin generates types for them.

Core Concepts

TermMeans
RouteA URL pattern plus what to render for it
SegmentOne piece of a path between slashes
Dynamic segment:id, captured into params
Splat*, matches the rest of the path
Index routeRenders at the parent's exact path
Layout routeHas children and an <Outlet />, but no path of its own
LoaderRuns before render, returns the route's data
ActionHandles a non-GET submission
NavigationA client-side transition, no full page reload
RevalidationRe-running loaders after an action