react-router vs react-router-dom
Core Routing Logic vs DOM Bindings in React
react-routerreact-router-domSimilar Packages:

Core Routing Logic vs DOM Bindings in React

react-router provides the core routing logic and environment-agnostic components for React applications, serving as the foundation for routing behavior. react-router-dom builds on top of react-router by adding specific bindings for web browsers, such as HTML5 history integration and clickable link components. While react-router handles the universal state and matching logic, react-router-dom implements the actual interaction with the browser's address bar and navigation events. Most web developers will install react-router-dom, which automatically includes react-router as a dependency, but understanding the distinction is vital for testing and cross-platform sharing.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
react-router056,4264.31 MB1355 days agoMIT
react-router-dom056,4265.46 kB1355 days agoMIT

react-router vs react-router-dom: Core Logic vs Web Bindings

When working with routing in React, it is common to see both react-router and react-router-dom listed in dependencies. Understanding the relationship between these two packages is critical for architectural clarity, especially when setting up testing environments or sharing code across platforms. While they work together, they serve distinct layers of the application stack.

๐Ÿ—๏ธ Package Relationship: Core Engine vs Browser Adapter

react-router is the core engine.

  • It contains the universal logic for route matching, state management, and context providers.
  • It does not know about the browser, the DOM, or the URL bar.
  • It is designed to be environment-agnostic.
// react-router: Core components
import { Routes, Route, Navigate } from 'react-router';

// Works in any environment (Web, Native, Server)
<Routes>
  <Route path="/home" element={<Home />} />
</Routes>

react-router-dom is the browser adapter.

  • It depends on react-router and re-exports all its components.
  • It adds specific implementations for the web, like HTML5 history and click handlers.
  • It is the package you install for standard web apps.
// react-router-dom: Web-specific components
import { BrowserRouter, Link } from 'react-router-dom';

// Wraps the core with browser history
<BrowserRouter>
  <Link to="/home">Home</Link>
</BrowserRouter>

๐Ÿงญ Setting Up the Router Provider

The most visible difference is how you initialize the routing context. The core package provides generic providers, while the DOM package provides the one that syncs with the browser.

react-router uses MemoryRouter or the low-level Router.

  • MemoryRouter keeps history in memory without changing the URL.
  • Useful for tests or environments without a URL bar.
  • Does not update the browser address bar.
// react-router: Memory-based history
import { MemoryRouter } from 'react-router';

<MemoryRouter initialEntries={['/dashboard']}>
  <App />
</MemoryRouter>

react-router-dom uses BrowserRouter.

  • BrowserRouter uses the HTML5 history API to keep UI in sync with the URL.
  • Updates the browser address bar on navigation.
  • Required for standard web navigation (back/forward buttons work).
// react-router-dom: Browser history
import { BrowserRouter } from 'react-router-dom';

<BrowserRouter>
  <App />
</BrowserRouter>

๐Ÿ”— Navigation: Links vs Redirects

Navigation components differ based on whether the environment supports user clicks or requires programmatic control.

react-router relies on Navigate for movement.

  • Navigate is a component that renders nothing but changes location.
  • Best for redirects after actions (like form submission).
  • No built-in clickable link component.
// react-router: Programmatic redirect
import { Navigate } from 'react-router';

// Redirects user immediately upon render
{!isLoggedIn ? <Navigate to="/login" /> : <Dashboard />}

react-router-dom adds Link for user interaction.

  • Link renders an <a> tag that prevents full page reloads.
  • Handles clicks to navigate within the single-page app.
  • Also includes Navigate (re-exported from core).
// react-router-dom: Declarative link
import { Link } from 'react-router-dom';

// Renders a clickable anchor tag
<Link to="/dashboard">Go to Dashboard</Link>

๐Ÿช Hooks and Shared Logic

Hooks are where the packages overlap the most. The DOM package re-exports the core hooks, so they behave identically in a web environment.

react-router exports the base hooks.

  • Hooks like useNavigate, useParams, and useLocation live here.
  • Importing from here ensures code is portable to non-DOM environments.
  • Preferred for shared libraries or monorepos.
// react-router: Importing from core
import { useNavigate } from 'react-router';

function SubmitButton() {
  const navigate = useNavigate();
  return <button onClick={() => navigate('/success')} />;
}

react-router-dom re-exports the same hooks.

  • You can import useNavigate from here with no loss of functionality.
  • More convenient for web-only projects since you already import from here.
  • Functionally identical in the browser.
// react-router-dom: Importing from DOM package
import { useNavigate } from 'react-router-dom';

function SubmitButton() {
  const navigate = useNavigate();
  return <button onClick={() => navigate('/success')} />;
}

๐Ÿงช Testing Scenarios

Testing is the primary use case where you might interact with react-router directly, even in a web project.

react-router is ideal for unit tests.

  • Tests should not depend on browser APIs like window.history.
  • MemoryRouter allows you to set initial paths without side effects.
  • Faster and more isolated than full DOM rendering.
// react-router: Testing setup
import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router';

render(
  <MemoryRouter initialEntries={['/profile']}>
    <UserProfile />
  </MemoryRouter>
);

react-router-dom is used for integration tests.

  • Use BrowserRouter when testing full user flows with URL changes.
  • Required if your code explicitly checks window.location.
  • Slower but more realistic for end-to-end checks.
// react-router-dom: Integration setup
import { render } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';

render(
  <BrowserRouter>
    <UserProfile />
  </BrowserRouter>
);

๐Ÿค Similarities: Shared Ground

Despite the separation, the two packages share a massive amount of DNA. In a web app, they function as a single unit.

1. โš›๏ธ Same Component API

  • Components like Routes, Route, and Outlet work exactly the same.
  • You can swap imports between packages for these components without breaking logic.
// Both packages support this identically
import { Routes, Route } from 'react-router'; // or react-router-dom
<Routes>
  <Route path="*" element={<NoMatch />} />
</Routes>

2. ๐Ÿช Identical Hook Behavior

  • useParams, useSearchParams, and useMatch return the same data.
  • No performance difference between importing from core vs DOM.
// Both return the same URL parameters
import { useParams } from 'react-router'; // or react-router-dom
const { id } = useParams();

3. ๐Ÿ”’ Same Context System

  • Both rely on the same React Context to pass router state down the tree.
  • Mixing imports does not create multiple router contexts.
// Context is shared regardless of import source
import { useLocation } from 'react-router';
import { Link } from 'react-router-dom';
// These work together seamlessly

๐Ÿ“Š Summary: Key Differences

Featurereact-routerreact-router-dom
Environment๐ŸŒ Universal (Web, Native, Server)๐ŸŒ Web Browser Only
Router ComponentMemoryRouter, RouterBrowserRouter, HashRouter
NavigationNavigate (Programmatic)Link, NavLink (Declarative)
HistoryIn-Memory or CustomHTML5 History API
Primary UseLibraries, Tests, Shared LogicStandard Web Applications
DependencyZero external depsDepends on react-router

๐Ÿ’ก The Big Picture

react-router is the foundation ๐Ÿ—๏ธ โ€” it holds the logic but doesn't touch the browser. Use it when you need portability, are writing tests, or building tools that other developers will use across different platforms.

react-router-dom is the implementation ๐Ÿ–ฅ๏ธ โ€” it connects that logic to the web. Use it for your actual website. It includes everything in react-router plus the tools needed to make the URL bar and back button work.

Final Thought: For 99% of web projects, install react-router-dom and import everything from there. Only reach for react-router directly if you have a specific need to decouple your logic from the browser environment.

How to Choose: react-router vs react-router-dom

  • react-router:

    Choose react-router if you are building a custom router implementation, writing unit tests that require a memory-based history, or sharing routing logic across different React environments like web and native in a monorepo. It is also the correct choice if you need to access low-level routing primitives without pulling in browser-specific dependencies. However, for standard web applications, this package alone is insufficient because it lacks browser history integration.

  • react-router-dom:

    Choose react-router-dom for virtually all standard web applications running in a browser environment. It re-exports all functionality from react-router and adds essential components like BrowserRouter, Link, and NavLink that interact with the DOM. This package handles the synchronization between your UI and the browser's URL bar, making it the complete solution for client-side routing on the web.

README for react-router

react-router is the primary package in the React Router project.

Installation

npm i react-router