# How Redux Store Enhancers Work and When to Use applyMiddleware

> Discover how Redux store enhancers work and learn when to use applyMiddleware for async logic, logging, and side effects in your Redux app.

- Repository: [Redux/redux](https://github.com/reduxjs/redux)
- Tags: deep-dive
- Published: 2026-03-05

---

**Redux store enhancers are higher-order functions that wrap the `createStore` factory to add capabilities like middleware or DevTools integration, and you should use `applyMiddleware` when you need to handle asynchronous logic, logging, or side effects in your action dispatching pipeline.**

Redux store enhancers provide the architectural extension point for modifying core store behavior in the reduxjs/redux repository. While the base `createStore` function only handles synchronous state updates, enhancers allow you to inject middleware, time-travel debugging, and other cross-cutting concerns without modifying your reducer logic.

## What Is a Redux Store Enhancer?

A **store enhancer** is fundamentally a higher-order function that accepts a store creator and returns a new, augmented store creator. According to the type definitions in [`src/types/store.ts`](https://github.com/reduxjs/redux/blob/main/src/types/store.ts), the signature follows this pattern:

```typescript
type StoreEnhancer<Ext = {}, StateExt = {}> = (
  next: StoreEnhancerStoreCreator<Ext, StateExt>
) => StoreEnhancerStoreCreator<Ext, StateExt>

```

When invoked, the enhancer receives the original `createStore` as the `next` argument. It can then:

- Add extra methods or state properties to the returned store object
- Replace or wrap the `dispatch` function to intercept actions
- Modify the subscription behavior or add external integrations

## How applyMiddleware Implements the Enhancer Pattern

The `applyMiddleware` function in [`src/applyMiddleware.ts`](https://github.com/reduxjs/redux/blob/main/src/applyMiddleware.ts) is the canonical implementation of a store enhancer. It transforms the dispatch pipeline by composing middleware functions into a chain. The implementation follows five distinct steps:

### 1. Create the Base Store

First, `applyMiddleware` invokes the original `createStore` to generate the base store object containing `dispatch`, `getState`, and `subscribe`.

### 2. Build the Middleware API

It constructs a **middleware API** object containing `getState` and a temporary `dispatch` function. This temporary `dispatch` throws if called during middleware setup, preventing circular dispatches before the chain is fully constructed.

### 3. Map Middlewares to the API

Each middleware function receives the middleware API, producing a chain of functions that accept `next` and return `action => result`.

### 4. Compose the Chain

Using the `compose` utility from [`src/compose.ts`](https://github.com/reduxjs/redux/blob/main/src/compose.ts), the array of middleware functions is composed right-to-left into a single function. This composition wraps the original `store.dispatch` with each middleware layer.

### 5. Return the Enhanced Store

Finally, `applyMiddleware` returns a new store object that spreads the original store properties and replaces `dispatch` with the composed middleware chain.

## When to Use applyMiddleware in Your Redux Application

You should use `applyMiddleware` when you need to intercept, transform, or handle side effects in the action dispatching pipeline. Common scenarios include:

- **Asynchronous logic**: Middleware like **Redux Thunk** allows dispatching functions or promises instead of plain objects, enabling API calls and delayed dispatches.
- **Logging and instrumentation**: A logger middleware can inspect every action and the resulting state for debugging purposes.
- **Error handling**: Middleware can catch exceptions from action creators or reducers and report them to external monitoring services.
- **Conditional behavior**: You can dynamically build the middleware array based on `process.env.NODE_ENV` to include development-only tools.

If you are using **Redux Toolkit**, you typically do not call `applyMiddleware` directly. The `configureStore` function handles this automatically while adding sensible defaults, though you can still pass a custom middleware array to `configureStore` which internally uses `applyMiddleware`.

## Correct Enhancer Composition Order

When composing multiple store enhancers, **applyMiddleware must be the first enhancer in the composition chain**. This ordering is critical because middleware can be asynchronous—if DevTools or other enhancers wrap the store first, they will only see the raw dispatch function and miss any asynchronous actions processed by middleware.

The correct pattern uses `compose` from [`src/compose.ts`](https://github.com/reduxjs/redux/blob/main/src/compose.ts) to chain enhancers right-to-left:

```javascript
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
import { devToolsEnhancer } from 'redux-devtools-extension';

const enhancer = compose(
  applyMiddleware(thunk), // Must be first
  devToolsEnhancer()
);

const store = createStore(rootReducer, enhancer);

```

## Practical Code Examples

### Basic Logger Middleware

This example demonstrates a simple logging middleware that prints actions and state changes:

```javascript
import { createStore, applyMiddleware } from 'redux';
import rootReducer from './reducers';

function logger({ getState }) {
  return next => action => {
    console.log('dispatching', action);
    const result = next(action);
    console.log('next state', getState());
    return result;
  };
}

const store = createStore(rootReducer, applyMiddleware(logger));
store.dispatch({ type: 'ADD_TODO', text: 'Learn Redux enhancers' });

```

### Async Thunk Middleware

Using Redux Thunk for asynchronous API calls:

```javascript
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const store = createStore(rootReducer, applyMiddleware(thunk));

function fetchUser(id) {
  return dispatch => {
    dispatch({ type: 'FETCH_USER_REQUEST', id });
    return fetch(`/api/users/${id}`)
      .then(response => response.json())
      .then(user => dispatch({ type: 'FETCH_USER_SUCCESS', user }))
      .catch(error => dispatch({ type: 'FETCH_USER_FAILURE', error }));
  };
}

store.dispatch(fetchUser(42));

```

### Composing Multiple Enhancers

Combining middleware with Redux DevTools:

```javascript
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

const store = createStore(
  rootReducer,
  composeEnhancers(
    applyMiddleware(thunk),
    // other enhancers here
  )
);

```

## Summary

- A **Redux store enhancer** is a higher-order function that wraps `createStore` to add capabilities like middleware or DevTools integration.
- **`applyMiddleware`** is itself a store enhancer that composes middleware functions to intercept and transform dispatched actions.
- The implementation in [`src/applyMiddleware.ts`](https://github.com/reduxjs/redux/blob/main/src/applyMiddleware.ts) creates a middleware API, maps middlewares, composes them using [`src/compose.ts`](https://github.com/reduxjs/redux/blob/main/src/compose.ts), and returns an enhanced store with a wrapped `dispatch`.
- Use `applyMiddleware` when handling **asynchronous logic**, **logging**, **error handling**, or other cross-cutting concerns in the dispatch pipeline.
- When composing multiple enhancers, always place **`applyMiddleware` first** in the chain to ensure asynchronous middleware works correctly with DevTools and other enhancers.

## Frequently Asked Questions

### What is the difference between a Redux store enhancer and middleware?

A **store enhancer** is a higher-order function that wraps the entire `createStore` function to modify the store instance itself, such as adding new methods or replacing `dispatch`. **Middleware** is a specific pattern used within the dispatch pipeline that intercepts individual actions. `applyMiddleware` is the store enhancer that enables middleware by composing middleware functions into the dispatch chain.

### Can I use multiple store enhancers together?

Yes, you can compose multiple enhancers using the `compose` utility from [`src/compose.ts`](https://github.com/reduxjs/redux/blob/main/src/compose.ts). When combining enhancers, always place `applyMiddleware` first in the composition chain (rightmost in the `compose` arguments). This ensures that asynchronous middleware processes actions before DevTools or other enhancers observe the dispatch.

### Do I need to use applyMiddleware with Redux Toolkit?

No, you typically do not call `applyMiddleware` directly when using **Redux Toolkit**. The `configureStore` function automatically sets up `applyMiddleware` with sensible defaults, including the Redux Thunk middleware. However, you can still customize the middleware array by passing a `middleware` option to `configureStore`, which internally uses `applyMiddleware` to compose your custom middleware chain.

### Why does applyMiddleware throw if dispatch is called during setup?

The `applyMiddleware` implementation in [`src/applyMiddleware.ts`](https://github.com/reduxjs/redux/blob/main/src/applyMiddleware.ts) provides a temporary `dispatch` function to the middleware API during initialization. This temporary function throws an error if invoked because the middleware chain has not yet been fully composed. This prevents **circular dispatch** scenarios where middleware might attempt to dispatch actions during the setup phase before the final store is ready.