VOOZH about

URL: https://blog.logrocket.com/react-router-v7-modes/

⇱ Choosing the right React Router v7 mode for your project - LogRocket Blog


2025-09-23
390
#react
Amazing Enyichi Agu
207641
102
👁 Image

See how LogRocket's Galileo AI surfaces the most severe issues for you

No signup required

Check it out

Choosing the right routing strategy is one of the first big decisions when building a React app. With the release of React Router v7, routing has evolved beyond simple navigation into a set of modes that can also manage data, handle mutations, and even power full-stack applications.

👁 Image

In this guide, we’ll break down the three modes React Router v7 offers: declarative, data, and framework and, explore when to use each. By the end, you’ll not only understand how these modes differ, but you’ll also know exactly which one fits your project’s needs.

🚀 Sign up for The Replay newsletter

The Replay is a weekly newsletter for dev and engineering leaders.

Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.

Why are there different modes?

React Router started as a routing library for Single-Page Applications (SPAs). In v4, the authors added support for routing React Native apps, which meant there were now two ways to use the library. To distinguish between these use cases, the team released two different libraries: react-router-dom for Web Apps and react-router-native for React Native.

React Router kept evolving, but maintained that distinction between DOM and React Native. However, at the same time, the React Router team was developing a full-stack React framework called Remix. Remix used React Router to manage its routing. The team also kept pushing the capabilities of the Router.

Eventually, in the v6 release, React Router became more than just a routing library. It also started to handle data loading and mutations in routes. Data handling patterns from Remix were ported to React Router as the authors thought those patterns would be beneficial to the routing library.

In v7, the React Router team decided to combine Remix and React Router, releasing them as just one library: react-router. They did this mainly because the two libraries had grown very similar over time, and they wanted to free up the Remix brand for something more ambitious. React Router was transformed from a simple routing library to a full-stack framework.

Due to the multiple use cases of React Router v7, the team grouped its functionalities into different modes. This was to make it more intuitive for developers to adopt. This differentiation was also important to anyone migrating from Remix to React Router. It was clearer what the migration path looked like and what features were made for the full-stack framework.

Note: The v7 release also dropped support for React Native, so the library is now only imported as react-router.

Modes Declarative Mode Data Mode Framework Mode
Previous version React Router v5/v6 React Router v6.4+ Remix v2
Server-side rendering Not supported Supported (with DIY setup) Supported
Typesafety Not supported Not supported Supported
Recommended use case Simple routing Routing and data handling Full-stack development

Understanding React Router modes

In the sections below, we’ll walk through each of the three React Router v7 modes: declarative, data, and framework. For each, we’ll cover what it does, its pros and cons, and the scenarios where it’s the best fit.

Declarative mode

The declarative mode of React Router allows simple declarative routing for SPAs. With this mode, one can set up routes with JSX, use React Router’s built-in components like <Link> and <NavLink>, nest routes with <Outlet />, and has access to hooks like useNavigate and useLocation.

This example illustrates how to create a simple index route in declarative mode:

import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter, Routes, Route } from "react-router";
import App from "./app";

const root = document.getElementById("root");

ReactDOM.createRoot(root).render(
 
 
 } />
 
 ,
);

Pros of using the declarative mode in React Router are:

  • Efficient SPA routing – This mode works well for the basic routing needs of React apps. As mentioned earlier, it offers built-in components and functions that abstract away frequently needed utilities. It then presents them with declarative and easy-to-use APIs
  • Simple learning curve – Out of all the modes in React Router, this is the most easy to get started with. It has mostly simple APIs that solve routing problems in a straightforward and intuitive manner
  • Small bundle footprint – Due to tree-shaking by bundlers, using React Router’s declarative mode leaves a small footprint on the resulting build bundle. You end up getting excellent routing functionalities without having a bloated application

Cons of using the declarative mode in React Router are:

  • Custom data loading – Because the authors of React Router intended this mode to be for basic routing, developers have to look to other solutions to handle data loading for their routes. Other React Router modes, however, handle this optimally with route loaders
  • Limited functionality – Other modes of React Router provide a lot more built-in components and functions for users. One example is the <Form/> component and its associated hooks and functions

Choose React Router’s declarative mode when:

  • You want to get familiar with the React Router ecosystem
  • You need simple routing for your SPA that just works

Data mode

Think of data mode in React Router as an extended declarative mode. While declarative mode is minimal, data mode gives access to more tools. This mode configures routes outside of React’s rendering, which allows React Router to perform services like data loading for routes, handling data mutations with actions, robust form management, and many more.

The example below shows how to create a simple route in data mode:

import { createBrowserRouter } from "react-router";

function Root() {
 return <h1>Hello world</h1>;
}

const router = createBrowserRouter([
 { path: "/", Component: Root },
]);

The Pros of using data mode are:

  • Extra components and APIs – This mode provides the user with more built-in components, in addition to most of the functionality in declarative mode. This makes it an even more robust routing solution
  • Allows further customization – Developers can use the React Router data mode as a foundation to build their own framework. If, for some reason, they like data mode and want to create a custom framework around it, they are welcome to do so
  • Support for Server-Side Rendering (SSR) – With declarative mode, the docs are not clear on how to use it to build SSR apps. However, there is documentation on how to combine this mode with SSR in case that is what a user wants

The cons of using data mode are:

  • Slight learning curve – This mode takes a lot more time to get used to than declarative mode. It has a bit of a learning curve before one can get the benefits from it
  • Users may not need all the features – The user may not make full use of the data mode. In that case, they are probably better off fixing some of those problems themselves and working with declarative mode instead of trying to learn the data mode

Choose data mode when:

  • You need a robust way to handle data between routes
  • You want to build a custom framework with a React Router base

Framework mode

The framework mode of React Router is essentially a data mode as a packaged and polished full-stack React framework. Along with the benefits of data mode, framework mode adds enhancements like typesafety, straightforward config for SPAs, SSRs, or Static generation.

A major addition in this mode is file-based routing, which allows you to define routes directly from the file system rather than manually configuring them. This makes organizing routes more intuitive and helps scale larger applications.



Framework mode is also positioned as an alternative to other full-stack React frameworks like Next.js and TanStack Start.

Below is an example that shows how to define routes in framework mode:

// src/routes.ts

import {
 type RouteConfig,
 route,
 index,
 layout,
 prefix,
} from "@react-router/dev/routes";

export default [
 index("./home.tsx"),
 route("about", "./about.tsx"),

 layout("./auth/layout.tsx", [
 route("login", "./auth/login.tsx"),
 route("register", "./auth/register.tsx"),
 ]),

 ...prefix("concerts", [
 index("./concerts/home.tsx"),
 route(":city", "./concerts/city.tsx"),
 route("trending", "./concerts/trending.tsx"),
 ]),
] satisfies RouteConfig;

The pros of framework mode are:

  • All-in-one package – The framework mode is the most robust and versatile in React Router. Apart from its well-mapped rendering strategies, one can also perform more operations like prefetching links, among others
  • Better organization – Using the framework mode generally creates a much more organized codebase than using the data mode. A data mode codebase can sometimes feel improvised, while framework mode has a cleaner structure
  • Type safety – In all the modes of React Router, framework mode is the only one that can guarantee type safety. Framework mode generates important types for every route. This is a huge advantage of using framework mode over the others

On the other hand, the cons of framework mode are:

  • Relatively steep learning curve – Out of all the modes, framework mode has a slightly steeper learning curve. Note that this curve would be reduced if the user has had prior experience with data mode
  • Not always the right tool – Framework mode is not always the right mode for the job. For example, if a user prioritizes control, then data mode might be the better tool for that job. If they prioritize simplicity, then declarative mode might be the better tool

Choose framework mode when:

  • You want to build React apps with a well-structured framework
  • You want to use React Router with typesafety
  • need an alternative to Next.js, or TanStack Start

Conclusion

React Router has grown from a simple client-side routing tool into a flexible framework with multiple modes. While the options in v7 can feel overwhelming at first, the key is matching the mode to your project’s needs: declarative for simple SPAs, data for apps that need routing plus data handling, and framework for full-stack development with type safety and SSR support.

With these distinctions in mind, you can approach new projects confidently, knowing which mode to reach for and why.

Get set up with LogRocket's modern React error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID
  2. Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not server-side

    $ npm i --save logrocket 
    
    // Code:
    
    import LogRocket from 'logrocket'; 
    LogRocket.init('app/id');
     
    // Add to your HTML:
    
    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
     
  3. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • NgRx middleware
    • Vuex plugin
Get started now
👁 Image
👁 Image
👁 Image

Stop guessing about your digital experience with LogRocket

Get started for free

Recent posts:

What is TSRX?: What JSX would look like if it were designed today

TSRX adds first-class control flow, conditional hooks, and scoped styles to React via a TypeScript compiler extension — no new framework required.

👁 Image
Ikeh Akinyemi
Jun 12, 2026 ⋅ 6 min read

How to add authentication to a React Native app with Better Auth

Learn how to build a full React Native auth system using Better Auth and Expo — with email/password login, Google OAuth, session persistence, and protected routes.

👁 Image
Chinwike Maduabuchi
Jun 9, 2026 ⋅ 13 min read

AI dev tool power rankings & comparison [June 2026]

Compare the top AI development tools and models of June 2026. View updated rankings, feature breakdowns, and find the best fit for you.

👁 Image
Chizaram Ken
Jun 8, 2026 ⋅ 11 min read

How to check username availability at scale with Bloom filters

Learn how Bloom filters reduce database lookups for username availability checks while preserving correctness at scale.

👁 Image
Rosario De Chiara
Jun 8, 2026 ⋅ 6 min read
View all posts

Would you be interested in joining LogRocket's developer community?

Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now