mrkeyoor.com_
Sun 20 Sept 07:01 UTC
npmWeb Frontendupdated 20 Sept 2026

redux review

Redux 5.0.1 is the framework-independent core behind a reducer store. Code dispatches plain actions with string types, reducers calculate the next immutable state, subscribers run after dispatch, and middleware can wrap the dispatch path. React bindings, async conventions, selector memoization, query caching, and persistence live elsewhere. Version 5.0.1 accepts Object.create(null) values in isPlainObject and restores nullable state in the Store.getState TypeScript type after a 5.0 regression. Our full browser import was 3.4 KB minified and 1.4 KB gzipped, with bundled declarations.

31.4Mdownloads / wk
Verdict

Redux 5.0.1 added 1.4 KB gzipped in our browser build and installed as one 1 MB package with zero audit findings. Keep core Redux for existing stores and framework-neutral reducers; start new React Redux code with the maintainers' Toolkit package.

We installed it

Lab card: what happened when we installed reduxScreenshot of redux documentation
Install✓ · 0.3s1 package on disk · 1 MB
ImportESM import works · require() works · CommonJS package with exports map
Browser1.4 KBgzipped (3.4 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does redux install cleanly?

Yes. In a fresh container with an empty cache, npm install redux finished in 0.3s, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.

How much does redux add to a browser bundle?

1.4 KB gzipped (3.4 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does redux work with both ESM and CommonJS?

Yes. Both import 'redux' and require('redux') worked in Node 22 in our run. The package is published as CommonJS with an exports map.

Does redux include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

redux or @reduxjs/toolkit: which should you use?

@reduxjs/toolkit: Choose it for new Redux applications that need slices, configured middleware, DevTools, development checks, and RTK Query. Redux 5.0.1 added 1.4 KB gzipped in our browser build and installed as one 1 MB package with zero audit findings.

When should you not use redux?

You are starting a React application; the repository tells new projects to install @reduxjs/toolkit and react-redux rather than wire core Redux by hand

API stability5/5dispatch, getState, subscribe, combineReducers, applyMiddleware, compose, bindActionCreators, and replaceReducer still define the same reducer-store flow used by older applications. Redux 5 tightened action and middleware types but retained the runtime model. The 5.0.1 patch only adjusts Object.create(null) handling and a getState type regression, so ordinary 5.0 code does not need a new setup.
Docs5/5redux.js.org separates an Essentials course based on Redux Toolkit from a Fundamentals course that explains core Redux directly. It also has API references, usage guides, an FAQ, and a version 5 migration page. The README says Toolkit is the recommended approach and asks readers to judge whether Redux is needed, which prevents the core API reference from becoming an automatic architecture recommendation.
Maintenance4/5GitHub showed an unarchived repository pushed on August 20, 2026, with 49 open issues and pull requests. Core version 5.0.1 was released in December 2023, while ongoing work across the Redux organization includes Toolkit, React Redux, Reselect, templates, and documentation. The core is intentionally settled, but teams seeking new application features will find them in companion packages rather than frequent redux releases.
Ecosystem5/5The npm API recorded 42,348,808 downloads from August 19 through August 25, 2026, and GitHub reported 61,507 stars. Redux Toolkit, React Redux, Reselect, DevTools, persistence packages, sagas, and many framework examples share the store contract. Toolkit depends on the core, so download volume proves broad use of the contract without proving that manual createStore setup is popular in new projects.

Discussed on

  1. hnGuerrilla Public Service Redux (2017)421 points
  2. hnBuild Yourself a Redux395 points
  3. hnBuild your front end in React, then let ChatGPT be your Redux reducer395 points
  4. hnThe SaaS CTO Security Checklist Redux395 points
  5. hnThings to learn in React before using Redux370 points

Use it if

  • An existing Redux or Redux Toolkit application needs the core store contracts it already depends on
  • A browser, Node process, worker, or test tool needs the same reducer and action model without React
  • State transitions must be inspectable as serializable events for logging, replay, or deterministic tests
  • You are implementing or teaching middleware, enhancers, reducer composition, and subscriptions at the core level
Skip it if

Setup reality

We installed Redux 5.0.1 in a fresh Node 22 sandbox in 0.3 seconds. It left one package and 1 MB on disk. Redux declares zero direct dependencies and zero peer dependencies, and its own unpacked files measured 372 KB. npm audit found zero known vulnerabilities. The package metadata is CommonJS with an exports map; require() and ESM import both worked. TypeScript declarations are included. Our complete esbuild import produced 3.4 KB minified and 1.4 KB gzipped.

There are no credentials or required config files. For new React work, the project's documented install is @reduxjs/toolkit plus react-redux. configureStore includes core Redux, thunk, DevTools wiring, and development checks. Direct createStore calls still execute, but TypeScript marks that name deprecated. legacy_createStore exposes the same runtime function without the editor warning for code that deliberately owns all middleware and enhancer setup.

Redux 5 requires every action.type to be a string. Custom middleware sees both action and next as unknown in TypeScript, so narrow with isAction() or a typed action creator before reading fields. UnknownAction replaces AnyAction as the safer default, and the former PreloadedState type is gone. The package also stopped shipping its old UMD files. These are migration details even though the 5.0.1 runtime patch itself only changes plain-object detection and a getState type.

A reducer must return its old state for unknown actions and cannot mutate that existing value. subscribe() receives no state argument and runs after each dispatch, so listeners call getState() and compare the slice they care about. Middleware order changes which dispatched values a layer observes. The 1.4 KB gzipped core contains no async request policy, query cache, persistence, React provider, or selector cache. Add those deliberately, or use Toolkit when the list starts growing.

Patterns

Build a bare Redux store create-reducer-store

import { legacy_createStore } from 'redux'

function counter(state = { value: 0 }, action) {
  if (action.type === 'counter/incremented') {
    return { ...state, value: state.value + 1 }
  }
  return state
}

const store = legacy_createStore(counter)

legacy_createStore is the same runtime function as createStore without its TypeScript deprecation marker; new app setup belongs in configureStore.

Observe completed dispatches dispatch-and-subscribe

const unsubscribe = store.subscribe(() => {
  console.log(store.getState())
})

store.dispatch({ type: 'counter/incremented' })
unsubscribe()

Subscribers receive no arguments and run after every dispatch; call getState() and compare the relevant branch.

Map reducers to state keys combine-state-branches

import { combineReducers } from 'redux'

const rootReducer = combineReducers({
  session: sessionReducer,
  todos: todosReducer,
})

Each reducer receives only its own branch, although the same action can be handled independently by both branches.

Narrow an unknown middleware value write-version-five-middleware

import { isAction } from 'redux'

const logger = ({ getState }) => next => value => {
  if (isAction(value)) console.log(value.type)
  const result = next(value)
  console.log(getState())
  return result
}

Redux 5 types middleware inputs as unknown; isAction verifies an action object with a string type before field access.

Apply middleware in order install-middleware

import { applyMiddleware, legacy_createStore } from 'redux'

const store = legacy_createStore(
  rootReducer,
  applyMiddleware(thunk, logger),
)

Ordering is visible: middleware before thunk can see dispatched functions, while later layers see only values thunk forwards.

Combine custom enhancers compose-store-enhancers

import { applyMiddleware, compose, legacy_createStore } from 'redux'

const enhancer = compose(
  applyMiddleware(logger),
  monitorEnhancer,
)
const store = legacy_createStore(rootReducer, enhancer)

Enhancers wrap store creation; configureStore handles standard middleware and DevTools composition for new Redux applications.

Hydrate the reducer tree load-preexisting-state

const preloadedState = {
  session: { user: null },
  todos: serverTodos,
}

const store = legacy_createStore(rootReducer, preloadedState)

The object must match the reducer keys, and server rendering needs a separate store instance per request.

Inject a code-split reducer replace-root-reducer

const dynamic = {}

export function injectReducer(name, reducer) {
  dynamic[name] = reducer
  store.replaceReducer(combineReducers({
    ...staticReducers,
    ...dynamic,
  }))
}

replaceReducer preserves existing state; decide whether state for a removed dynamic key should be retained or deleted.

Create dispatching helper functions bind-action-creators

import { bindActionCreators } from 'redux'

const actions = bindActionCreators({
  increment: () => ({ type: 'counter/incremented' }),
  add: amount => ({ type: 'counter/added', payload: amount }),
}, store.dispatch)

actions.add(5)

Do not bind on every UI render, because each call creates new function identities.

Check a message before dispatch validate-external-action

import { isAction } from 'redux'

const value = JSON.parse(message.data)
if (!isAction(value)) throw new TypeError('invalid Redux action')
store.dispatch(value)

isAction validates the general shape and string type; it does not verify the payload schema for a specific action.

Add a Reselect selector memoize-derived-state

import { createSelector } from 'reselect'

const selectTodos = state => state.todos
export const selectCompleted = createSelector(
  [selectTodos],
  todos => todos.filter(todo => todo.done),
)

Reselect is a separate package. Its cached result is reused until an input selector returns a different reference.

Use the recommended application setup start-with-redux-toolkit

import { configureStore, createSlice } from '@reduxjs/toolkit'

const counter = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    incremented(state) { state.value += 1 },
  },
})

const store = configureStore({
  reducer: { counter: counter.reducer },
})

Toolkit wraps this Redux core and adds action creators, Immer-based reducers, thunk, DevTools, and development checks.

Alternatives

PackageRegistryPick it when
@reduxjs/toolkitnpmChoose it for new Redux applications that need slices, configured middleware, DevTools, development checks, and RTK Query
zustandnpmChoose it when a hook-based store with selector subscriptions fits better than reducers and explicit action objects
jotainpmChoose it when independent atoms and dependency tracking match the way state is consumed

More web frontend guides

postcss · react · react-dom · tailwindcss · htmlparser2 · tailwind-merge · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.