connected-react-router vs mobx-react-router vs react-router vs react-router-dom
Routing Architecture and State Integration in React
connected-react-routermobx-react-routerreact-routerreact-router-domSimilar Packages:

Routing Architecture and State Integration in React

react-router provides the core routing logic for React applications, while react-router-dom adds specific bindings for web browsers including components like BrowserRouter and Link. connected-react-router and mobx-react-router are third-party bridges that synchronize routing state with Redux and MobX stores, respectively. These integration packages allow developers to access routing information directly from their global state management system, though modern patterns often favor direct hook usage.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
connected-react-router04,687444 kB176-MIT
mobx-react-router043720.9 kB72 years agoMIT
react-router056,5722.86 MB1697 hours agoMIT
react-router-dom056,5725.4 kB1698 hours agoMIT

Routing Architecture and State Integration in React

When building complex React applications, routing is rarely just about changing URLs. It involves managing history, protecting routes, and often syncing navigation state with global data stores. The ecosystem splits into core logic, platform bindings, and state management integrations. Let's break down how react-router, react-router-dom, connected-react-router, and mobx-react-router fit together — and where the industry is moving.

🏗️ Core Logic vs. DOM Bindings

The foundation of routing in React is split between platform-agnostic logic and web-specific implementations.

react-router contains the core components and hooks that work across different environments.

  • It defines the matching algorithms and context providers.
  • It does not include DOM-specific history management.
// react-router: Core components only
import { Router, Routes, Route } from 'react-router';

// You must provide your own history object
function App({ history }) {
  return (
    <Router history={history}>
      <Routes>...</Routes>
    </Router>
  );
}

react-router-dom builds on top of react-router for web browsers.

  • It exports everything from react-router.
  • It adds BrowserRouter, HashRouter, Link, and NavLink.
  • It handles the browser's history API automatically.
// react-router-dom: Web-specific bindings
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <nav><Link to="/about">About</Link></nav>
      <Routes>...</Routes>
    </BrowserRouter>
  );
}

🔗 Syncing Router State with Global Stores

In large applications, you might need to access the current path or navigation actions from outside the component tree — for example, inside a Redux saga or a MobX action. This is where integration packages come in.

connected-react-router connects React Router to a Redux store.

  • It pushes router state (location, action) into your Redux state tree.
  • It requires wrapping your app with ConnectedRouter.
  • Note: Primarily designed for React Router v5. Support for v6 is limited and often discouraged in modern architectures.
// connected-react-router: Redux integration
import { ConnectedRouter } from 'connected-react-router';
import { useSelector } from 'react-redux';

function App({ history }) {
  return (
    <ConnectedRouter history={history}>
      {/* Router state is now in redux state.router */}
    </ConnectedRouter>
  );
}

function Nav() {
  // Accessing location from Redux store
  const location = useSelector(state => state.router.location);
  return <div>{location.pathname}</div>;
}

mobx-react-router connects React Router to a MobX store.

  • It provides a router store that observes navigation changes.
  • Components must be wrapped in observer to react to route changes.
  • Note: Like the Redux counterpart, this is often tied to older React Router versions and adds complexity.
// mobx-react-router: MobX integration
import { RouterStore } from 'mobx-react-router';
import { observer } from 'mobx-react';

const routingStore = new RouterStore();

const Nav = observer(() => {
  // Accessing location from MobX store
  return <div>{routingStore.location.pathname}</div>;
});

// Setup requires syncing history to the store
routingStore.syncHistoryWithStore(history);

🔄 Modern Patterns: Hooks vs. Store Sync

The industry has shifted away from syncing router state to global stores unless absolutely necessary. React Router v6 introduced powerful hooks that reduce the need for connected-react-router or mobx-react-router.

Direct Hook Usage (Recommended)

  • Use useLocation, useNavigate, and useParams directly in components.
  • Even with Redux or MobX, you can call these hooks inside connected components.
  • This removes the need for middleware and reduces bundle size.
// Modern React Router v6 approach (No store sync needed)
import { useLocation, useNavigate } from 'react-router-dom';

function Nav() {
  const location = useLocation();
  const navigate = useNavigate();

  return (
    <button onClick={() => navigate('/home')}>
      Current: {location.pathname}
    </button>
  );
}

When Store Sync Is Still Useful

  • You need to trigger side effects in Redux Sagas or MobX reactions based on route changes.
  • You require time-travel debugging for navigation events.
  • You have legacy code that relies on state.router selectors.
// Legacy/Specific Case: Accessing navigation in Redux Saga
// Requires connected-react-router to populate state
function* watchLocation() {
  yield takeLatest('@@router/LOCATION_CHANGE', function*(action) {
    // Perform side effect based on route change
    yield call(fetchData, action.payload.location.pathname);
  });
}

⚠️ Maintenance and Version Compatibility

One of the biggest risks with integration packages is version drift. react-router-dom moves fast, while integration libraries often lag behind.

  • react-router-dom: Actively maintained. Supports React 18 and Concurrent features.
  • react-router: Actively maintained. Core logic updates alongside DOM package.
  • connected-react-router: Maintenance has slowed. Many teams are migrating away from it due to v6 incompatibilities.
  • mobx-react-router: Niche maintenance. Verify compatibility with your specific MobX and React Router versions before adopting.

📊 Summary: Architecture Comparison

Featurereact-router-domreact-routerconnected-react-routermobx-react-router
Primary UseWeb ApplicationsCore/LibraryRedux SyncMobX Sync
Includes DOM✅ Yes❌ No❌ No (Requires history)❌ No (Requires history)
State Sync❌ Local Context❌ Local Context✅ Redux Store✅ MobX Store
React Router v6✅ Full Support✅ Full Support⚠️ Limited/Legacy⚠️ Limited/Legacy
BoilerplateLowMediumHigh (Reducers/Middleware)Medium (Store Setup)

💡 The Big Picture

react-router-dom is the standard choice for 95% of web projects. It provides all the tools you need without external dependencies. Start here.

react-router is a dependency of react-router-dom. You only install it separately if you are building a custom history implementation or targeting non-DOM platforms.

connected-react-router and mobx-react-router solve a specific problem — global access to routing state — but they introduce coupling and maintenance overhead. In modern React development, we prefer lifting routing logic into components using hooks, or using event listeners for side effects, rather than syncing the entire router state to a global store.

Final Thought: Keep your routing local unless you have a proven need for global access. This keeps your architecture flexible and your dependencies minimal.

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

  • connected-react-router:

    Choose connected-react-router only if you have a legacy React Router v5 codebase with heavy Redux dependencies that require time-travel debugging or complex selectors based on route state. For new projects, avoid this package as React Router v6 encourages keeping routing state local to the router.

  • mobx-react-router:

    Choose mobx-react-router if you are maintaining an existing MobX-based application that relies on observable router state. For new architectures, consider using standard React Router hooks within MobX observers instead, as this reduces external dependencies and alignment issues.

  • react-router:

    Choose react-router only if you are building a non-DOM environment (like React Native via react-router-native) or authoring a library that needs to remain platform-agnostic. For standard web apps, this package is implicitly included via react-router-dom and rarely installed directly.

  • react-router-dom:

    Choose react-router-dom for virtually all standard web applications. It includes everything from react-router plus the necessary DOM-specific components like BrowserRouter, Link, and useNavigate. This is the default starting point for any new React web project.

README for connected-react-router

Breaking change in v5.0.0! Please read How to migrate from v4 to v5/v6.

v6.0.0 requires React v16.4.0 and React Redux v6.0 / v7.0.

Connected React Router Build Status Open Source Helpers

A Redux binding for React Router v4 and v5

Main features

:sparkles: Synchronize router state with redux store through uni-directional flow (i.e. history -> store -> router -> components).

:gift: Supports React Router v4 and v5.

:sunny: Supports functional component hot reloading while preserving state (with react-hot-reload).

:tada: Dispatching of history methods (push, replace, go, goBack, goForward) works for both redux-thunk and redux-saga.

:snowman: Nested children can access routing state such as the current location directly with react-redux's connect.

:clock9: Supports time traveling in Redux DevTools.

:gem: Supports Immutable.js

:muscle: Supports TypeScript

Installation

Connected React Router requires React 16.4 and React Redux 6.0 or later.

npm install --save connected-react-router

Or

yarn add connected-react-router

Usage

Step 1

In your root reducer file,

  • Create a function that takes history as an argument and returns a root reducer.
  • Add router reducer into root reducer by passing history to connectRouter.
  • Note: The key MUST be router.
// reducers.js
import { combineReducers } from 'redux'
import { connectRouter } from 'connected-react-router'

const createRootReducer = (history) => combineReducers({
  router: connectRouter(history),
  ... // rest of your reducers
})
export default createRootReducer

Step 2

When creating a Redux store,

  • Create a history object.
  • Provide the created history to the root reducer creator.
  • Use routerMiddleware(history) if you want to dispatch history actions (e.g. to change URL with push('/path/to/somewhere')).
// configureStore.js
...
import { createBrowserHistory } from 'history'
import { applyMiddleware, compose, createStore } from 'redux'
import { routerMiddleware } from 'connected-react-router'
import createRootReducer from './reducers'
...
export const history = createBrowserHistory()

export default function configureStore(preloadedState) {
  const store = createStore(
    createRootReducer(history), // root reducer with router state
    preloadedState,
    compose(
      applyMiddleware(
        routerMiddleware(history), // for dispatching history actions
        // ... other middlewares ...
      ),
    ),
  )

  return store
}

Step 3

  • Wrap your react-router v4/v5 routing with ConnectedRouter and pass the history object as a prop. Remember to delete any usage of BrowserRouter or NativeRouter as leaving this in will cause problems synchronising the state.
  • Place ConnectedRouter as a child of react-redux's Provider.
  • N.B. If doing server-side rendering, you should still use the StaticRouter from react-router on the server.
// index.js
...
import { Provider } from 'react-redux'
import { Route, Switch } from 'react-router' // react-router v4/v5
import { ConnectedRouter } from 'connected-react-router'
import configureStore, { history } from './configureStore'
...
const store = configureStore(/* provide initial state if any */)

ReactDOM.render(
  <Provider store={store}>
    <ConnectedRouter history={history}> { /* place ConnectedRouter under Provider */ }
      <> { /* your usual react-router v4/v5 routing */ }
        <Switch>
          <Route exact path="/" render={() => (<div>Match</div>)} />
          <Route render={() => (<div>Miss</div>)} />
        </Switch>
      </>
    </ConnectedRouter>
  </Provider>,
  document.getElementById('react-root')
)

Note: the history object provided to router reducer, routerMiddleware, and ConnectedRouter component must be the same history object.

Now, it's ready to work!

Examples

See the examples folder

FAQ

Build

npm run build

Generated files will be in the lib folder.

Development

When testing the example apps with npm link or yarn link, you should explicitly provide the same Context to both Provider and ConnectedRouter to make sure that the ConnectedRouter doesn't pick up a different ReactReduxContext from a different node_modules folder.

In index.js.

...
import { Provider, ReactReduxContext } from 'react-redux'
...
      <Provider store={store} context={ReactReduxContext}>
        <App history={history} context={ReactReduxContext} />
      </Provider>
...

In App.js,

...
const App = ({ history, context }) => {
  return (
    <ConnectedRouter history={history} context={context}>
      { routes }
    </ConnectedRouter>
  )
}
...

Contributors

See Contributors and Acknowledge.

License

MIT License