connected-react-router vs react-router vs react-router-redux vs redux-first-history vs redux-logger
Routing State Management and Debugging in Redux Applications
connected-react-routerreact-routerreact-router-reduxredux-first-historyredux-loggerSimilar Packages:

Routing State Management and Debugging in Redux Applications

react-router is the core library for handling navigation in React applications. connected-react-router, react-router-redux, and redux-first-history are bindings that synchronize routing state with a Redux store, allowing navigation to be managed via Redux actions or state selectors. redux-logger is a middleware utility used to log Redux actions and state changes to the console, aiding in debugging the flow of data including routing updates. Together, these tools represent different approaches to managing location state and observing changes within the Redux ecosystem.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
connected-react-router04,688444 kB176-MIT
react-router056,5522.79 MB17123 days agoMIT
react-router-redux07,747-110 years agoMIT
redux-first-history045499.3 kB183 years agoMIT
redux-logger05,719-589 years agoMIT

Routing State Management and Debugging in Redux Applications

Building complex React applications often involves deciding how to handle navigation state. While react-router is the standard for rendering views based on URL, teams using Redux sometimes need to sync that location state with their global store. This comparison breaks down the core routing library, the Redux bindings, and the debugging tools available.

🧭 Core Routing: The Foundation

react-router is the essential package for any React app needing navigation. It provides the components and hooks to render different views based on the browser's URL. You do not need Redux to use this library effectively in most modern applications.

// react-router: Standard usage with hooks
import { BrowserRouter, Routes, Route, useNavigate } from 'react-router-dom';

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

function Home() {
  const navigate = useNavigate();
  return <button onClick={() => navigate('/about')}>Go</button>;
}

πŸ”— Syncing Routing with Redux

When you need to trigger navigation from Redux thunks or access location state in selectors, you need a binding library. Here is how the options differ.

connected-react-router

This is the standard solution for React Router v4 and v5. It wraps your router and syncs history changes to the Redux store automatically.

// connected-react-router: v5 setup
import { ConnectedRouter } from 'connected-react-router';
import { push } from 'connected-react-router';

// In your component
<ConnectedRouter history={history}>
  <Routes>...</Routes>
</ConnectedRouter>

// In your Redux action
dispatch(push('/new-path'));

react-router-redux

This package is deprecated. It was the original attempt to bind React Router and Redux but is no longer safe or compatible with modern versions. You should not use this in any new code.

// react-router-redux: DEPRECATED - Do not use
// Legacy code might look like this, but it is obsolete
import { routerReducer } from 'react-router-redux';
// This API is no longer maintained and lacks v6 support

redux-first-history

This library flips the model. Instead of listening to history changes, it lets you dispatch actions to change the route. It treats navigation purely as a state update.

// redux-first-history: Action-driven navigation
import { push } from 'redux-first-history';

// In your Redux thunk
export const goToDashboard = () => async (dispatch) => {
  await dispatch(push('/dashboard'));
};

// In your store setup
import { createReduxHistoryContext } from 'redux-first-history';
// Requires specific store configuration to link history

πŸ› οΈ Debugging State Changes

redux-logger is not a routing library. It is a middleware that prints every Redux action and the resulting state to the console. It helps you see when routing actions fire.

// redux-logger: Middleware setup
import { createLogger } from 'redux-logger';
import { configureStore } from '@reduxjs/toolkit';

const logger = createLogger();

const store = configureStore({
  reducer: rootReducer,
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger)
});

πŸ“Š Feature Comparison

Featurereact-routerconnected-react-routerreact-router-reduxredux-first-historyredux-logger
Primary RoleCore RoutingRedux Sync (v5)Redux Sync (Legacy)Action-Driven RoutingDebugging
Maintenanceβœ… Active⚠️ Maintenance (v5)❌ Deprecated⚠️ Low Activityβœ… Active
Redux Required❌ Noβœ… Yesβœ… Yesβœ… Yesβœ… Yes
React Router v6βœ… Native Support⚠️ Complex/Unofficial❌ No Support⚠️ Limited Supportβœ… Compatible
Navigation TriggerHooks/ComponentsActions/SelectorsActions (Legacy)Actions OnlyN/A

⚠️ Critical Considerations for React Router v6

The landscape changed significantly with React Router v6. The core library now uses React Context heavily, which is fast enough for most apps without Redux sync.

  • react-router is the only package with full, native support for v6 features like useNavigate and loader data APIs.
  • connected-react-router has limited v6 support. Many teams are moving away from syncing router state to Redux in v6 because it adds complexity without much benefit.
  • react-router-redux should never be used. It is dead software.
  • redux-first-history can work with v6 but requires careful configuration to match the new history API.
  • redux-logger works with any version but adds bundle size. Only include it in development builds.

🌐 Real-World Scenarios

Scenario 1: Standard Content Site

You are building a marketing site or blog with simple navigation.

  • βœ… Best choice: react-router
  • Why? You do not need Redux for routing. Keep it simple.
// Standard react-router implementation
<Routes>
  <Route path="/" element={<Home />} />
</Routes>

Scenario 2: Legacy Dashboard with Redux v5

You maintain an existing app using React Router v5 and Redux, needing to track history in state for undo/redo features.

  • βœ… Best choice: connected-react-router
  • Why? It provides stable syncing for v5 and allows reducers to react to location changes.
// connected-react-router usage
const location = useSelector((state) => state.router.location);

Scenario 3: Strict Unidirectional Flow

Your team requires every state change, including navigation, to happen via explicit dispatches for auditing.

  • βœ… Best choice: redux-first-history
  • Why? It enforces navigation through actions rather than direct history manipulation.
// redux-first-history usage
dispatch(push('/audit-log'));

Scenario 4: Debugging Complex Flows

You are troubleshooting why a route change isn't triggering a data fetch.

  • βœ… Best choice: redux-logger
  • Why? It shows the exact sequence of actions in the console.
// redux-logger output
// ACTION: @router/LOCATION_CHANGE
// PREV STATE: { location: '/' }
// NEXT STATE: { location: '/about' }

πŸ’‘ Summary and Recommendation

react-router is the mandatory foundation for navigation in React. Use it for all projects.

connected-react-router is the viable option if you must sync routing with Redux on React Router v5. For v6, reconsider if you really need Redux sync at all.

react-router-redux is deprecated and must be avoided. Replace it with connected-react-router or remove Redux routing bindings entirely.

redux-first-history is a niche tool for action-driven architectures. Use it only if your team specifically requires navigation to be dispatched as actions.

redux-logger is a development tool. Use it to watch the flow of routing actions but exclude it from production builds to save performance.

Final Thought: Modern React development favors keeping routing state local to the router library unless you have a specific need for global access. Start with react-router alone and only add Redux bindings if your architecture demands it.

How to Choose: connected-react-router vs react-router vs react-router-redux vs redux-first-history vs redux-logger

  • connected-react-router:

    Choose connected-react-router if you are using React Router v5 and need to access routing state directly from Redux reducers or middleware. It is the maintained successor to react-router-redux and provides stable bindings for syncing history with the store. Avoid this for new React Router v6 projects unless you have a specific need to store location state in Redux, as v6 hooks often make this unnecessary.

  • react-router:

    Choose react-router for all standard React applications that require navigation, regardless of whether you use Redux. It is the foundational library that provides components like BrowserRouter and hooks like useNavigate. You should always install this package as it is the core dependency for the other routing bindings listed here.

  • react-router-redux:

    Do not choose react-router-redux for any new project as it is officially deprecated and no longer maintained. It was the original binding library but was superseded by connected-react-router years ago. Using it introduces security risks and compatibility issues with modern React versions.

  • redux-first-history:

    Choose redux-first-history if you prefer a unidirectional data flow where navigation is triggered strictly by dispatching Redux actions rather than listening to history changes. This approach is useful if your architecture requires all state changes, including route changes, to originate from explicit dispatches. It is less common than connected-react-router but offers tighter control over action-driven navigation.

  • redux-logger:

    Choose redux-logger during development to inspect Redux actions and state changes in the browser console. It is not required for production builds but helps debug how routing actions or other state updates flow through your middleware pipeline. Pair this with any of the routing bindings to visualize when navigation events occur.

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