Understanding the Different Router Modes in React Router: Declarative, Data, and Framework Explained
React Router offers three primary router modes—Declarative, Data, and Framework—that are architecturally additive, allowing you to choose between minimal setup for simple SPAs and full-stack features for complex applications.
The remix-run/react-router repository provides these distinct APIs to match your application's complexity and your team's infrastructure needs. Each mode builds upon the previous one, creating a clear upgrade path from basic client-side routing to fully type-safe, file-system based routing with automatic code-splitting.
The Three Router Modes
React Router organizes its capabilities into three layers. The relationship is hierarchical: Framework mode contains Data mode, which contains Declarative mode. According to the official documentation in /docs/start/modes.md, features are strictly additive, meaning you choose based on how much architectural control you want to retain versus how much infrastructure you want the library to handle.
Declarative Mode: Component-Based Routing
Declarative mode uses the <BrowserRouter> or <HashRouter> components to create a plain router instance directly in your React tree. This is the traditional approach that maps URLs to components without built-in data loading.
In /packages/react-router/lib/dom/lib.tsx (lines 818-828), the BrowserRouter component is implemented as a thin wrapper that instantiates a history object and provides it via context. This mode supports essential navigation via <Link> and useNavigate, plus location state access through useLocation.
When to use: Choose Declarative mode for small applications, prototypes, or Create React App projects where you need basic URL-to-component matching without route-level data fetching or pending UI states. It is ideal when you already manage data loading separately from routing.
Data Mode: Object-Based Routers with Loaders and Actions
Data mode introduces the createBrowserRouter() factory function and <RouterProvider> component. Unlike Declarative mode, the router object lives outside React rendering, enabling the router to drive data fetching before components render.
Implemented in /packages/react-router/lib/dom/lib.tsx (lines 645-665), createBrowserRouter creates a data router that supports route-level loader functions for fetching data, action functions for mutations, and useFetcher for pending UI without navigation. This architecture exposes hooks like useLoaderData and useActionData that connect your components to resolved route data.
When to use: Select Data mode when you need data fetching tied to routes but want to retain control over your bundler, server-side rendering implementation, or caching strategies. It is perfect for existing applications using React Router v6.4 data APIs that require custom adapters or specific build pipelines.
Framework Mode: Zero-Config Full-Stack Routing
Framework mode wraps Data mode with a Vite plugin (@react-router/dev/routes) that provides file-system routing, type-safe route parameters, and intelligent code-splitting. Instead of manually defining route objects, you export a route configuration from routes.ts.
The Vite plugin (located in /packages/react-router-dev/vite/plugin.ts) reads your route manifest, generates type-safe Route types, and automatically code-splits route modules. This mode implements the Route Module API, where files export loader, action, ErrorBoundary, and component functions that the framework wires together automatically.
When to use: Framework mode is optimal for full-stack applications requiring SSR/SSG, teams seeking a Remix-like developer experience without boilerplate, or projects where type-safe links and automatic bundle optimization are priorities.
Implementation Examples
Declarative Mode Setup
Mount the router as a component wrapper:
// src/main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router";
import App from "./App";
ReactDOM.createRoot(document.getElementById("root")!).render(
<BrowserRouter>
<App />
</BrowserRouter>,
);
Data Mode Configuration
Define routes as objects with data capabilities:
// src/router.ts
import {
createBrowserRouter,
RouterProvider,
} from "react-router";
const router = createBrowserRouter([
{
path: "/",
Component: () => <h1>Home</h1>,
loader: async () => {
const data = await fetch("/api/home");
return data.json();
},
},
]);
// src/main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider } from "react-router";
import router from "./router";
ReactDOM.createRoot(document.getElementById("root")!).render(
<RouterProvider router={router} />,
);
Framework Mode with Route Modules
Configure routes in routes.ts and implement route modules:
// routes.ts
import { index, route } from "@react-router/dev/routes";
export default [
index("./home.tsx"),
route("products/:pid", "./product.tsx"),
];
// app/product.tsx
import { Route } from "./+types/product.tsx";
export async function loader({ params }: Route.LoaderArgs) {
const product = await getProduct(params.pid);
return { product };
}
export default function Product({ loaderData }: Route.ComponentProps) {
return <div>{loaderData.product.name}</div>;
}
The Framework mode implementation details are documented in /docs/start/framework/route-module.md, which defines the Route Module API surface.
Decision Guide: Selecting the Right Mode
Choose Framework mode when you want the "zero-config" experience of file-system routing, automatic type generation for route parameters, and seamless SPA/SSR/SSG strategies. The documentation in /docs/start/modes.md (lines 95-102) identifies this as the best choice for teams new to React Router or projects requiring server-side rendering without custom infrastructure.
Choose Data mode when you have an existing build pipeline and want data router capabilities—loaders, actions, and pending states—while maintaining full control over server adapters and bundler configuration. As noted in /docs/start/modes.md (lines 106-110), this suits existing v6.4 data router applications that are currently working well.
Choose Declarative mode when you need minimal routing for simple SPAs or when your application already implements a separate data layer that handles its own loading states. The documentation specifies this for basic route matching needs in /docs/start/modes.md (lines 113-118).
Summary
- Declarative mode (
<BrowserRouter>) provides basic URL-to-component matching via React context in/packages/react-router/lib/dom/lib.tsx, suitable for simple SPAs without data loading. - Data mode (
createBrowserRouter) adds object-based route configuration withloaderandactionfunctions, enabling pre-render data fetching and mutation handling throughRouterProvider. - Framework mode leverages the Vite plugin in
/packages/react-router-dev/vite/plugin.tsto provide type-safe, file-system based routing with automatic code-splitting and the Route Module API. - The modes are additive: Framework includes all Data features, which include all Declarative features.
Frequently Asked Questions
Can I migrate from Declarative mode to Data mode incrementally?
Yes, migration is incremental because the modes are architecturally additive. You can replace <BrowserRouter> with createBrowserRouter and <RouterProvider> in your entry point, then gradually add loader and action functions to specific routes while keeping existing components unchanged.
Does Framework mode require using Vite?
Yes, Framework mode is currently implemented through the @react-router/dev Vite plugin located in /packages/react-router-dev/vite/plugin.ts. The plugin handles route manifest generation, type generation, and code-splitting. If you use a different bundler, you should use Data mode instead.
What happens to my existing React Router v5 code in these modes?
Declarative mode maintains compatibility with v5-style component-based routing. However, v5's <Switch> and useHistory APIs are replaced by <Routes> and useNavigate. Data and Framework modes require v6.4+ APIs and do not support legacy v5 patterns like static <Route> children outside of <Routes>.
Is Data mode required to use the loader and action functions?
Yes, loader and action functions are only available in Data mode and Framework mode (which builds on Data mode). These functions depend on the router object created by createBrowserRouter to handle data fetching outside the React render cycle, as implemented in /packages/react-router/lib/dom/lib.tsx.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →