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

> Explore react router vs react router dom differences for web apps. Understand the core engine vs. DOM wrapper and their specific use cases to optimize your routing strategy.

- Repository: [Vercel/next.js](https://github.com/vercel/next.js)
- Tags: deep-dive
- Published: 2026-02-16

---

**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

```tsx
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

```tsx
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

```tsx
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`](https://github.com/vercel/next.js/blob/main/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:

- [`packages/next/src/client/router.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/router.ts) exposes the singleton Next.js router used on the client side, defining public methods (`push`, `replace`, etc.) and the `useRouter` hook.
- [`packages/next/src/shared/lib/router/router.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/shared/lib/router/router.ts) contains the core router implementation that tracks route changes, prefetching, and navigation state.
- [`packages/next/src/shared/lib/router-context.shared-runtime.ts`](https://github.com/vercel/next.js/blob/main/packages/next/src/shared/lib/router-context.shared-runtime.ts) provides the React context that supplies the router instance to the component tree via `useRouter`.

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`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/router.ts) and [`packages/next/src/shared/lib/router/router.ts`](https://github.com/vercel/next.js/blob/main/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`](https://github.com/vercel/next.js/blob/main/packages/next/src/client/router.ts) and [`packages/next/src/shared/lib/router/router.ts`](https://github.com/vercel/next.js/blob/main/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.