React Router Cheatsheet
Framework Mode and SSR
Use this React Router reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
SSR and Framework Mode Exports
// app/root.tsx import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router"; export function Layout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <head> <meta charSet="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <Meta /> <Links /> </head> <body> {children} <ScrollRestoration /> <Scripts /> </body> </html> ); } export default function Root() { return <Outlet />; }
| Route export | Runs |
|---|---|
default | The component, client and server |
loader | Server only (framework mode) |
clientLoader | Browser only |
action | Server only |
clientAction | Browser only |
ErrorBoundary | On an error in this subtree |
HydrateFallback | While a clientLoader runs on first load |
meta | Document <title> and <meta> tags |
links | <link> tags, including preloads |
headers | Response headers for this route |
handle | Metadata for useMatches |
shouldRevalidate | Revalidation control |
export function meta({ data }: Route.MetaArgs) { return [ { title: data.product.name }, { name: "description", content: data.product.blurb }, ]; } export function headers() { return { "Cache-Control": "public, max-age=300, s-maxage=3600" }; }
Client-Only Loaders
// Skip the server entirely for this route's data export async function clientLoader({ params, serverLoader }) { const cached = sessionStorage.getItem(`user:${params.id}`); if (cached) return JSON.parse(cached); const data = await serverLoader(); // fall through to the server loader sessionStorage.setItem(`user:${params.id}`, JSON.stringify(data)); return data; } clientLoader.hydrate = true as const; // run it on the first load too export function HydrateFallback() { return <p>Loading…</p>; }
HydrateFallback is required when clientLoader.hydrate is true, because on the first render there is no server data to show.
SPA Mode
// react-router.config.ts export default { ssr: false } satisfies Config;
With ssr: false you still get the route config, loaders (as clientLoader), code splitting, and typegen, but the build produces a static index.html. This is the migration path for an existing SPA that wants the data APIs without a server.
Prerendering
export default { async prerender() { const slugs = await getPublishedSlugs(); return ["/", "/about", ...slugs.map((s) => `/posts/${s}`)]; }, } satisfies Config;
Prerendered routes are written to disk at build time and served as static files, so they cost nothing to serve and are fully crawlable.
Deploy Targets
| Adapter | For |
|---|---|
@react-router/node | A Node server (Express, Fastify, or the built-in one) |
@react-router/cloudflare | Cloudflare Workers and Pages |
@react-router/architect | AWS Lambda via Architect |
@react-router/serve | The zero-config production server |
npm run build # -> build/client and build/server
npx react-router-serve ./build/server/index.jsServer Context
// A value your adapter injects, available in every loader and action export async function loader({ context }: Route.LoaderArgs) { const user = await context.session.get("user"); return { user }; }