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.
Repository Health
Technical Analysis
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-uprouterReducer,routerMiddleware, andcreateReduxHistory(store)in one call- Action creators (
push,replace,go,goBack,goForward) that dispatch through Redux instead of callinghistorydirectly - A
routerReducerslice storinglocation,action, and optionally the last NpreviousLocations - Adapters for
@reach/router(reachify),wouter(createWouterHook), and React Router v6 (HistoryRouterfromredux-first-history/rr6) - Options for
basenamesupport, a custombatchfunction (e.g.unstable_batchedUpdates), and a customselectRouterStateselector for non-standard state shapes likeredux-immutable
Common Use Cases
- Migrating an app off
react-router-reduxorconnected-react-routerwith the sameLOCATION_CHANGEaction shape and push/replace actions - Driving navigation from a Redux Saga or Thunk (
yield put(push('/path')),dispatch(push('/path'))) instead of injectinghistoryinto business logic - Reading
state.router.locationfrom 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, andwouterside 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.