Redux First History

Makes Redux the single source of truth for navigation state, syncing react-router, @reach/router, and wouter through one dispatch-driven history.

Library
npm
v5.2.0
454stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
46/100Fair
Development Activity20
Maintenance32
Community52
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
63/100Good
Architecture78
Code Quality62
Innovation68
Learning Curve45

Redux First History replaces the usual two-way sync between Redux and a router library with a one-way data flow: every navigation event becomes a Redux action, and every component reads location from the store instead of from router context, withRouter, or hooks scattered across the tree. It ships a reducer, a middleware, and a factory (createReduxHistoryContext) that wires them together against any history v4/v5 instance, plus adapters (reachify, a wouter hook, an HistoryRouter for React Router v6) so teams can migrate from react-router-redux or connected-react-router with minimal code changes.

Because location lives in the store, shallowCompare/connect optimizations work correctly again, Redux DevTools time-travel can play back navigation, and apps can mix react-router, @reach/router, and wouter in the same codebase without synchronization bugs. The library has no runtime dependencies of its own — only peer dependencies on history and redux — keeping the integration surface small and predictable.

What You Get

  • createReduxHistoryContext({ history, ... }) — a factory that returns a wired-up routerReducer, routerMiddleware, and createReduxHistory(store) in one call
  • Action creators (push, replace, go, goBack, goForward) that dispatch through Redux instead of calling history directly
  • A routerReducer slice storing location, action, and optionally the last N previousLocations
  • Adapters for @reach/router (reachify), wouter (createWouterHook), and React Router v6 (HistoryRouter from redux-first-history/rr6)
  • Options for basename support, a custom batch function (e.g. unstable_batchedUpdates), and a custom selectRouterState selector for non-standard state shapes like redux-immutable

Common Use Cases

  • Migrating an app off react-router-redux or connected-react-router with the same LOCATION_CHANGE action shape and push/replace actions
  • Driving navigation from a Redux Saga or Thunk (yield put(push('/path')), dispatch(push('/path'))) instead of injecting history into business logic
  • Reading state.router.location from any connected component so shallow-compare re-render optimizations aren’t broken by router context updates
  • Supporting Redux DevTools time-travel debugging where navigating backward/forward through recorded actions correctly replays route changes
  • Running react-router, @reach/router, and wouter side by side during an incremental router migration

Under The Hood

Architecture The library is organized as small, single-purpose modules composed by one factory: actions.ts defines the CALL_HISTORY_METHOD action creators, middleware.ts intercepts those actions and calls the real history object, reducer.ts writes LOCATION_CHANGE payloads into state.router, and create.ts (createReduxHistoryContext) wires all three together plus a proxy history-shaped object whose push/replace/go methods dispatch through Redux and whose location/action getters read from the store. reachify.ts layers a @reach/router-compatible navigate/listen API on top of that same proxy. The result is a clear one-way flow — dispatch, middleware, real history call, history.listen callback, LOCATION_CHANGE reducer, store update — with no component ever reading location from anywhere but the store.

Tech Stack Written in TypeScript targeting both ES5 (CommonJS, for main) and ES6 (for module) via two tsc builds (tsconfig.json / tsconfigEs6.json). It has zero runtime dependencies, declaring history and redux only as peer dependencies, and keeps optional adapters (@reach/router, wouter, React Router v6’s HistoryRouter) as separate entry points so consumers who don’t use them don’t pull in extra code. Tooling is ESLint (Airbnb config) plus Prettier, Jest with ts-jest for tests, and rimraf for clean builds.

Code Quality A dedicated __tests__ directory covers the middleware, reducer, actions, create.ts context, and the @reach/router adapter, using a fake history object with Jest spies to assert on dispatch counts and history-method calls — a reasonably thorough suite for a library this size, with a coverage badge generated from jest --coverage. TypeScript is configured in strict mode, but the source makes liberal use of @ts-ignore comments around the history v4/v5 API surface (which changed shape between versions), so type safety is partial rather than complete. No CI workflow configuration (e.g. .github/workflows) is present in the repository, so it’s unclear whether tests and lint run automatically on PRs.

API Design The public API deliberately mirrors the native history object’s method names (push, replace, go, goBack, goForward) so migrating from connected-react-router or direct history usage is close to a drop-in import swap, and the README documents every createReduxHistoryContext option in a single table. Getting started requires exactly three wiring steps — add routerReducer to combineReducers, add routerMiddleware to applyMiddleware, call createReduxHistory(store) once — with no additional boilerplate for the common case, though picking the right adapter (reachify, wouter hook, or rr6) for a non-react-router setup requires reading past the main usage section.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search