Framework Mode

Framework mode is data mode plus a build. The @react-router/dev Vite 25,978 plugin takes over routing at compile time: routes are declared in app/routes.ts, each route is a module whose exports are the loader, action, component and error boundary, and the plugin generates per-route types, code-splits and renders on the server by default. Start one with npx create-react-router@latest my-app.

app/routes.ts and one route moduleTypeScript
import { type RouteConfig, index, route } from '@react-router/dev/routes';
export default [index('routes/home.tsx'),
  route('products/:id', 'routes/product.tsx')] satisfies RouteConfig;
// app/routes/product.tsx
import type { Route } from './+types/product';
export async function loader({ params }: Route.LoaderArgs) {   // runs on the server
  return { product: await db.products.find(params.id) };
}
export default function Product({ loaderData }: Route.ComponentProps) {
  return <h1>{loaderData.product.name}</h1>;               // typed, no hook needed
}

The generated ./+types/product module is the payoff: params.id, loaderData and actionData are typed from the route path and the loader's return value, with no hand-written interfaces. A route module can also export clientLoader, clientAction, ErrorBoundary, HydrateFallback, headers, links, meta, handle, shouldRevalidate and the middleware arrays of Route Middleware.

react-router.config.ts switches the app between server rendering (ssr: true, the default), a single-page build (ssr: false) and static prerendering — the row Router Modes's mode table gives framework mode. That makes it a direct alternative to Next.js 10,514 (Next.js) for a React-only front end: it is a router that grew a build, so moving up costs a routes.ts file rather than a rewrite.