React Router vs React Router-DOM: What’s the Real Difference?

When I first started working with React for building single-page applications (SPAs), one of the essential libraries that kept popping up in every tutorial and documentation was React Router. And every time I installed it using npm, I found myself typing:

npm install react-router-dom

But then I started wondering: if React Router is the actual routing library, why are we installing react-router-dom instead? And even more confusing — there’s also something called react-router separately. So what exactly is the difference between the two? Is react-router-dom just a fancy wrapper? Why do we need both?

If you’ve had the same questions or even used react-router-dom without thinking too much about what react-router does behind the scenes, don’t worry — you’re not alone. It took me some digging, experimentation, and reading official docs (and GitHub issues) to get a solid understanding of what’s really going on. So let’s break it all down.


Understanding the Basics

Let’s start with the basics. At its core, React Router is a routing library built for React. It allows you to:

  • Navigate between different components/pages
  • Handle dynamic routing based on URL
  • Protect routes (e.g., private routes requiring login)
  • Handle nested routing
  • Pass data through routes (via params, query, or state)

React Router is one of the most commonly used libraries for client-side routing in React apps.

But here’s where things start to split: React Router is built in a platform-agnostic way.

That means the core react-router library doesn’t care whether you are using it in a browser (like Chrome), in React Native (for mobile apps), or even in an Electron app. It just provides the core routing logic. The actual implementation — like how to manipulate the browser’s history or how to render links — is delegated to platform-specific packages.

And this is where react-router-dom and react-router-native come in.


React Router: The Core Package

The react-router package is the heart of the routing logic. It includes all the core APIs, like:

  • createBrowserRouter
  • createRoutesFromElements
  • RouteObject
  • Navigation logic
  • Matchers
  • Loaders
  • Actions

But it does not include any browser-specific implementation. That means, if you try to use it on its own in a browser app, you won’t have things like:

  • <BrowserRouter>
  • <Link>
  • <NavLink>

Because those are all browser-specific — they rely on the DOM and the browser’s history API.

To put it simply:

react-router is like the engine of a car. It powers everything but can’t run on its own unless you install it in the right body — like a browser or a mobile shell.


React Router-DOM: The Browser Shell

react-router-dom is the package that glues the core react-router logic with the browser’s capabilities. It includes everything from react-router, plus DOM-specific components like:

  • <BrowserRouter>
  • <HashRouter>
  • <Link>
  • <NavLink>
  • <Prompt> (in v5)
  • useNavigate
  • useLocation
  • useParams

These components and hooks know how to interact with the browser’s window.location, the history stack, and how to render actual clickable <a> tags that users interact with.

If you inspect the source code or the dependencies of react-router-dom, you’ll notice it imports a lot from the react-router core. That’s because react-router-dom is essentially a higher layer built on top of the core routing engine.


A Real-World Analogy

Imagine react-router as a generic navigation system — like Google Maps APIs. It knows how to calculate routes, give turn-by-turn directions, and estimate time — but it doesn’t care if you’re driving a car, biking, or walking.

Now, react-router-dom is like a car dashboard with Google Maps installed. It takes the routing engine and connects it to real-world controls — steering, acceleration, display.

Similarly, react-router-native is like the same Google Maps API but integrated into a mobile app — maybe with gestures, touch input, and native transitions.


Installation: Why Do We Install react-router-dom?

In most cases, especially when building web apps, you don’t install react-router manually. You just install react-router-dom, which includes everything you need — including the core.

npm install react-router-dom

Under the hood, this package already includes the correct version of react-router, so you don’t need to add both.

However, if you’re building a cross-platform routing library or writing something very custom, you might directly depend on react-router to access core APIs only.


Versioning Note: React Router v6+

Before React Router v6, react-router and react-router-dom were more loosely coupled, and their version numbers sometimes differed slightly. But starting from v6, the React Router team has unified the API more cleanly and made sure that react-router-dom is just an extended layer over the shared react-router core.

This makes it easier to understand and upgrade because:

  • react-router and react-router-dom will typically be on the same version
  • Most of the logic is shared
  • Documentation is centralized

Let’s Look at Some Code

Let me show you a simple example that highlights where the difference lies.

Example 1: Core Logic (from react-router)

import { createBrowserRouter, RouterProvider } from 'react-router-dom';

const router = createBrowserRouter([
  {
    path: "/",
    element: <HomePage />,
    children: [
      {
        path: "about",
        element: <AboutPage />,
      },
    ],
  },
]);

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

Here we are using the Data Router APIs introduced in v6.4+, which are part of the core react-router, but surfaced through react-router-dom for browser use.

Example 2: Traditional Browser Router (DOM-specific)

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

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<HomePage />} />
        <Route path="/about" element={<AboutPage />} />
      </Routes>
    </BrowserRouter>
  );
}

Notice how BrowserRouter is tied directly to the DOM — it uses the HTML5 History API under the hood.


When Should You Use Each?

Use react-router-dom When:

  • You’re building a React app for the browser
  • You want DOM-based components like <Link>, <BrowserRouter>, etc.
  • You want access to the complete routing experience out of the box

Use react-router Only When:

  • You’re building a platform-specific routing solution (e.g., for React Native, Electron, or custom environments)
  • You’re writing a custom wrapper or library that builds on routing logic
  • You want to tightly control how navigation is handled (not typical for most developers)

Common Misconceptions Cleared

Here are a few things I misunderstood at first — maybe you have too.

❌ Misconception 1: You only need one or the other.

✅ Truth: You typically only install react-router-dom, but it internally uses react-router. Think of them as parts of the same system — the core and the interface.


❌ Misconception 2: They are interchangeable.

✅ Truth: No, they serve different purposes. react-router-dom is for web apps. react-router-native is for mobile. react-router is the shared foundation.


❌ Misconception 3: You need to install both manually.

✅ Truth: Installing react-router-dom is enough for web apps. It includes everything from the core.


Conclusion

So what’s the real difference between react-router and react-router-dom?

  • react-router: platform-independent routing core.
  • react-router-dom: DOM-specific implementation that enables React Router to work in the browser.

In my journey of learning React, I found that truly understanding the ecosystem often means looking past the surface. At first, everything might feel abstract or redundant — but when you understand the architecture behind it, it becomes crystal clear.

If you’re working on a browser-based React project (which most of us are), just stick with react-router-dom. But if you’re exploring advanced use cases or want to know how the tools you’re using are structured, digging into the distinction between these packages is a great place to start.

Hope this clears it up for you like it did for me!

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top