React Router has three modes: Declarative, Data, and Framework. They are additive layers, so each mode adds features while asking you to give up a little architectural control. The right mode depends on how much you want the router to do for you.
The three modes at a glance
| Mode | Route setup | Data features | Server rendering |
|---|---|---|---|
| Declarative | Components in JSX | No | No |
| Data | Route objects | Loaders and actions | No |
| Framework | File routes with a Vite plugin | Loaders and actions | SPA, SSR, static |
Declarative mode only matches URLs to components. Data mode adds loaders and actions that run in the browser. Framework mode wraps data mode with a Vite plugin and can move loaders and actions to the server.
Each step up adds capabilities at the cost of some control, so the decision is about that tradeoff.
Declarative mode: components in JSX
Declarative mode is the smallest layer. You render the router components directly in JSX, with no route objects and no data functions.
import { BrowserRouter, Routes, Route } from "react-router";
import Home from "./pages/Home.jsx";
import About from "./pages/About.jsx";
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</BrowserRouter>
);
}This covers URL matching, navigation, and active link styling, and nothing else. There is no loader, no pending state, and no server rendering. It is the setup from the React Router tutorial, and it fits a marketing site or a simple client-side app whose data layer already lives outside React.
Because there are no loaders, the initial HTML has no data, and each page fetches on the client.
Data mode: route objects with loaders
Data mode moves the route configuration out of JSX and into objects passed to createBrowserRouter. Each route can carry a loader and an action.
createBrowserRouter([
{
path: "/",
Component: Home,
loader: async () => ({ posts: await fetchPosts() }),
},
]);The loader runs before the component renders, and useLoaderData reads its result. Actions handle form submissions with automatic revalidation. You still own the bundler and the server, so the router never renders on the server in this mode.
It is the middle ground, with the data features of framework mode but none of its file conventions or server rendering. The full data flow is in React Router loaders and actions explained.
Framework mode: file routes and type safety
Framework mode is data mode plus a Vite plugin. Routes live in a routes.ts file, and each route module exports its loader, action, and component with generated types.
import { route } from "@react-router/dev/routes";
export default [
route("products/:pid", "./product.tsx"),
];The route helper maps a URL pattern to a file. Layouts and index routes use the layout and index helpers, and a catch-all uses the star pattern.
import type { Route } from "./+types/product.tsx";
export async function loader({ params }: Route.LoaderArgs) {
return { product: await getProduct(params.pid) };
}
export default function Product({ loaderData }: Route.ComponentProps) {
return <div>{loaderData.product.name}</div>;
}Route.LoaderArgs and Route.ComponentProps are generated from the route config, so params and loader data stay typed. Framework mode also renders on the server and splits code automatically, unlike the manual split in how to lazy load routes in React. Scaffold a new project with npx create-react-router@latest.
A special root.tsx module wraps every route, holding the document shell and global providers.
Which mode should you use?
- Framework mode: choose it when you are new, migrating from Next.js, or want server rendering, code splitting, and type-safe routes without wiring them yourself.
- Data mode: choose it when you want loaders and actions but prefer to control the bundler and the server yourself.
- Declarative mode: choose it when you only need URL matching, links, and navigation with no data layer.
The modes are not tiers of quality, just different amounts of built-in behavior.
One common confusion
None of the modes is deprecated, and every mode can target any deployment. Declarative mode is not a legacy fallback, it is the correct choice when there is no data to load. The layers are additive, so an app can start declarative and adopt data or framework features later without rewriting its pages.
Rune AI
Key Insights
- Declarative mode renders routes in JSX with no data features.
- Data mode adds loaders and actions through route objects.
- Framework mode adds file routes, type safety, and server rendering.
- Pick the mode by how much control you want to keep.
Frequently Asked Questions
Which mode should a beginner choose?
Is declarative mode deprecated?
Can I switch modes later?
Conclusion
React Router's three modes are additive layers. Declarative mode matches URLs to components, Data mode adds loaders and actions, and Framework mode wraps it all in a Vite plugin with type safety and server rendering. Pick the mode that matches how much the router should do for you.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.