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.
| Mode | You get | Use when |
|---|---|---|
| Declarative | Routing only, fetch data yourself | Adding routes to an existing SPA |
| Data | Loaders, actions, pending states, no bundler opinion | You want the data APIs but own your build |
| Framework | Data mode plus a Vite plugin, SSR, code splitting, typegen | Starting 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
| Router | Runs where | Notes |
|---|---|---|
createBrowserRouter | Browser | Real URLs via the History API. The default choice. |
createHashRouter | Browser | URLs like /#/about. For static hosts that cannot rewrite. |
createMemoryRouter | Anywhere | Keeps history in memory. Tests and React Native. |
createStaticRouter | Server | SSR on the server side. |
BrowserRouter | Browser | Declarative mode, no data APIs. |
HashRouter | Browser | Declarative, hash URLs. |
MemoryRouter | Anywhere | Declarative, in memory. Best for tests. |
StaticRouter | Server | Declarative 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
| Term | Means |
|---|---|
| Route | A URL pattern plus what to render for it |
| Segment | One piece of a path between slashes |
| Dynamic segment | :id, captured into params |
| Splat | *, matches the rest of the path |
| Index route | Renders at the parent's exact path |
| Layout route | Has children and an <Outlet />, but no path of its own |
| Loader | Runs before render, returns the route's data |
| Action | Handles a non-GET submission |
| Navigation | A client-side transition, no full page reload |
| Revalidation | Re-running loaders after an action |