React Router vs React Router DOM: Key Differences and Use Cases for Web Applications

React Router is the platform-agnostic core routing engine that provides the fundamental API for URL matching and navigation, while React Router DOM is a browser-specific wrapper that supplies DOM components like <BrowserRouter> and <Link> for standard web applications.

When building single-page applications with React, understanding the distinction between react-router and react-router-dom is essential for choosing the right architecture. While these packages are often mentioned interchangeably, they serve distinct purposes in the routing ecosystem. This analysis examines the source code and API surface of both libraries to clarify when to use each, with specific reference to how the Vercel Next.js repository implements its own routing layer distinct from these packages.

What Is React Router? (Core Package)

React Router is the core routing engine that defines the routing API (e.g., <Routes>, <Route>, useNavigate, useParams). It contains the logic for matching URLs to components, managing navigation history, and exposing navigation helpers. The core package is platform-agnostic – it does not depend on any specific rendering environment.

The core package exports the fundamental primitives that handle route matching and state management, but it intentionally excludes any environment-specific implementations. This design allows the same routing logic to power web applications, native mobile applications, and server-side rendering environments without modification.

What Is React Router DOM? (Browser Adapter)

React Router DOM is a thin wrapper around the core that adapts the API for browser-based applications. It supplies the DOM-specific components (<BrowserRouter>, <HashRouter>, <Link>) and hooks that interact with the browser’s history API, popstate events, and URL handling.

When you run a React app in the browser you import from react-router-dom; when you run in a non-DOM environment (e.g., React Native, React Router Native, or a custom server-side renderer) you import the core package directly.

React Router vs React Router DOM: Feature Comparison

Feature React Router (core) React Router DOM
Environment Platform-agnostic (can run on web, native, server, or custom environments) Browser-only (relies on the DOM and window.history)
Provided Components None (only <Routes> / <Route> primitives) <BrowserRouter>, <HashRouter>, <MemoryRouter>, <Link>, <NavLink>
History Management Abstract history object that must be supplied by the host Creates and manages a History instance internally using the browser’s History API
Typical Import import { Routes, Route, useNavigate } from 'react-router' import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'
Use Case Library authors building a custom renderer, or projects that need a router without DOM side-effects (e.g., React Native, server-side rendering, or testing with <MemoryRouter>) Standard SPA development targeting browsers, where you need <Link> navigation and automatic URL synchronization

When to Use React Router (Core)

Choose the core react-router package in the following scenarios:

  • Building a library or component set that should work across multiple platforms without assuming a browser environment.
  • Using React Native (react-router-native) or a custom renderer where the DOM isn’t present.
  • Writing unit tests that need a lightweight router without browser dependencies (MemoryRouter from the core).
  • Implementing server-side rendering where you need to control the history object manually without browser APIs.

When to Use React Router DOM

Choose react-router-dom for standard web application development:

  • Developing a classic browser-based single-page application (SPA) where you need declarative navigation via <Link> or <NavLink>.
  • Requiring automatic scroll restoration and URL synchronization with the browser’s address bar.
  • Needing history-type selectors (BrowserRouter, HashRouter) that integrate with the browser’s History API.
  • Building client-side navigation where popstate events and DOM-based URL handling are required.

Code Examples

Standard Browser SPA with React Router DOM

import { BrowserRouter, Routes, Route, Link } from "react-router-dom";

export default function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link> | <Link to="/about">About</Link>
      </nav>
      <Routes>
        <Route path="/" element={<h1>Home page</h1>} />
        <Route path="/about" element={<h1>About page</h1>} />
      </Routes>
    </BrowserRouter>
  );
}

Platform-Agnostic Routing with Core React Router

import {
  RouterProvider,
  createBrowserRouter,
} from "react-router";

const router = createBrowserRouter([
  { path: "/", element: <Home /> },
  { path: "profile", element: <Profile /> },
]);

export default function App() {
  return <RouterProvider router={router} />;
}

Testing with MemoryRouter

import { MemoryRouter, Routes, Route } from "react-router";
import { render, screen } from "@testing-library/react";

test("navigates to /settings in memory router", () => {
  render(
    <MemoryRouter initialEntries={["/settings"]}>
      <Routes>
        <Route path="/settings" element={<div>Settings page</div>} />
      </Routes>
    </MemoryRouter>
  );

  expect(screen.getByText("Settings page")).toBeInTheDocument();
});

Next.js and Routing Considerations

In a Next.js project (the vercel/next.js repository), the framework ships its own routing layer that differs fundamentally from the react-router ecosystem. According to the source code, Next.js implements routing through several key files:

Because this router is tightly coupled to file-system based routing and server-side rendering, you normally do not use react-router or react-router-dom alongside Next.js unless you are building a custom, non-file-system-based navigation layer.

Summary

  • React Router is the platform-agnostic core that provides routing primitives (<Routes>, useNavigate) without environment assumptions.
  • React Router DOM is the browser-specific implementation that adds DOM components (<BrowserRouter>, <Link>) and integrates with the browser History API.
  • Use the core package for React Native, custom renderers, server environments, or library authoring.
  • Use React Router DOM for standard browser SPAs requiring URL synchronization and declarative navigation.
  • Next.js maintains its own routing system in packages/next/src/client/router.ts and packages/next/src/shared/lib/router/router.ts, making React Router unnecessary in most Next.js applications.

Frequently Asked Questions

Can I use React Router DOM in a Next.js application?

While technically possible, you generally should not use React Router DOM in Next.js applications. Next.js implements its own routing layer through files like packages/next/src/client/router.ts and packages/next/src/shared/lib/router/router.ts, which provides file-system based routing, automatic code splitting, and server-side rendering integration. Using React Router DOM would bypass these optimizations and require you to disable Next.js's built-in routing entirely.

What is the difference between BrowserRouter and createBrowserRouter?

BrowserRouter is a component that creates a router instance internally and is the traditional way to set up routing in React Router DOM applications. createBrowserRouter is a function exported from the core react-router package (and re-exported from react-router-dom) that creates a data API router instance which you pass to <RouterProvider>. The createBrowserRouter approach enables the new data APIs like loaders and actions, while BrowserRouter uses the older hooks-based API.

Should I import from react-router or react-router-dom in my web application?

For browser-based web applications, you should import from react-router-dom. This package re-exports everything from the core react-router package while adding DOM-specific components like <BrowserRouter>, <Link>, and <NavLink>. Importing from react-router directly is only necessary when building for non-DOM environments like React Native, creating custom router implementations, or when you need to avoid any browser-specific side effects in your bundle.

Is React Router DOM required for server-side rendering?

React Router DOM is not strictly required for server-side rendering, though it is commonly used in SSR applications running in the browser. For server environments, you typically use the core react-router package with <StaticRouter> (or createStaticRouter in data API mode) to handle routing without DOM dependencies. React Router DOM becomes relevant on the client side when hydrating the application, where you would switch to <BrowserRouter> or createBrowserRouter to enable client-side navigation.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →