React Router 8 139,386 ships three modes (Router Modes). Framework mode is the third: you add the @react-router/dev Vite 25,978 plugin, declare routes in app/routes.ts, and the plugin takes over building, code splitting, type generation and server rendering. The upgrade guide says it plainly — React Router v7 was "the next major version of Remix 205,393 after v2" — so framework mode is Remix's conventions under a new name, at 8.4.0. Scaffold with npx create-react-router@latest storefront.
A route is a module, not a component: it exports a server-side loader, an action that handles form posts, the component, and optionally meta, headers and ErrorBoundary. Types come from a generated ./+types/<route> module, so loaderData is typed from the loader's return value.
import type { Route } from "./+types/order";
import { Form, redirect } from "react-router";
export async function loader({ params }: Route.LoaderArgs) {
const order = await getOrder(params.id); // runs on the server only
if (!order) throw new Response("Not found", { status: 404 });
return { order };
}
export async function action({ request, params }: Route.ActionArgs) {
const reason = (await request.formData()).get("reason");
await cancelOrder(params.id, reason);
return redirect(`/orders/${params.id}`);
}
export default function Order({ loaderData }: Route.ComponentProps) {
return <Form method="post"><h1>Order {loaderData.order.id}</h1>
<input name="reason" /><button>Cancel order</button></Form>;
}The rendering model is ordinary client React 7,897 rendered once on the server and then hydrated — the renderToPipeableStream and hydrateRoot pairing of Server Rendering. There are no Server Components, no "use server" and no "use cache". Everything in your tree is a client component that happens to have been prerendered, which makes the mental model far smaller than the App Router's.
Deployment is a one-line decision in react-router.config.ts that leaves your route modules alone: ssr: true builds a server bundle, ssr: false builds a pure SPA with no Node.js 2,131 process, and prerender: ["/", "/pricing"] renders chosen paths to HTML at build time.
Pick framework mode when you want server rendering, nested-route data loading and progressive-enhancement forms, but not Server Components, a caching directive to reason about, or a framework whose happiest path runs on one company's platform. It is also the shortest migration from an existing React Router SPA: your <Link>, useParams and useNavigate code is already correct, and you convert screen by screen by moving fetches into loaders. The cost is a smaller ecosystem of ready-made integrations than Next.js 10,514 has.