How to Extend Redux Store with Custom Methods via Enhancers

A store enhancer is a higher-order function that wraps the store creator to augment the returned store object with custom methods, extra state, or additional behavior while preserving the core Redux API contract.

Extending a Redux store with custom methods requires understanding the store enhancer pattern implemented in the reduxjs/redux repository. While middleware intercepts actions, store enhancers modify the store creation process itself to inject additional functionality. This approach allows you to add methods like reset() or merge extra state fields while maintaining full type safety through TypeScript generics.

Understanding Store Enhancers

A store enhancer is officially typed as StoreEnhancer<Ext, StateExt> defined in [src/types/store.ts](https://github.com/reduxjs/redux/blob/master/src/types/store.ts). This higher-order function receives a next store creator and returns a new creator that produces a store with merged extensions.

The type definition (lines 200-226) uses generics to track both method extensions (Ext) and state extensions (StateExt), ensuring TypeScript recognizes your custom additions. When createStore detects an enhancer argument, it delegates creation entirely to that enhancer.

From [src/createStore.ts](https://github.com/reduxjs/redux/blob/master/src/createStore.ts) (lines 31-43):

if (typeof enhancer !== 'undefined') {
  return enhancer(createStore)(reducer, preloadedState)
}

This delegation allows the enhancer to invoke the original createStore, receive the base store object, and return an augmented version that spreads the original store plus your custom extensions.

Adding Custom Methods to the Store

Creating a Reset Method Enhancer

To extend the Redux store with a custom reset method, define an enhancer that captures the initial state and returns a new store object with the added functionality:

import { createStore, StoreEnhancer } from 'redux'

const resetEnhancer: StoreEnhancer<{ reset(): void }> = createStore => (
  reducer,
  preloadedState
) => {
  const store = createStore(reducer, preloadedState)
  const initialState = store.getState()

  return {
    ...store,
    reset() {
      store.replaceReducer(() => initialState)
      store.dispatch({ type: '@@RESET' })
    }
  }
}

const store = createStore(counterReducer, resetEnhancer)
store.reset() // TypeScript recognizes this method

The generic <{ reset(): void }> tells TypeScript that store.reset exists on the enhanced store type. The original store methods (dispatch, getState, subscribe) remain unchanged and fully functional.

Extending Store State Shape

Injecting Additional State Fields

You can also use enhancers to ensure specific state fields always exist by wrapping the reducer and preloaded state. This pattern uses the second generic parameter StateExt to extend the state type:

import { createStore, StoreEnhancer, Reducer } from 'redux'

interface ExtraState {
  extraFlag: boolean
}

function withExtraState<S, A extends { type: string }, PreloadedState = S>(
  reducer: Reducer<S, A, PreloadedState>
): Reducer<S & ExtraState, A, PreloadedState & ExtraState> {
  return (state, action) => ({
    ...reducer(state as any, action),
    extraFlag: true
  })
}

const extraStateEnhancer: StoreEnhancer<{}, ExtraState> = createStore => (
  reducer,
  preloadedState
) => {
  const wrappedReducer = withExtraState(reducer as any)
  const wrappedPreloaded = preloadedState
    ? { ...(preloadedState as any), extraFlag: true }
    : undefined

  return createStore(wrappedReducer, wrappedPreloaded as any)
}

const store = createStore(rootReducer, extraStateEnhancer)
const state = store.getState()
state.extraFlag // boolean (TypeScript knows this exists)

The enhancer wraps the original reducer to merge extraFlag into every state update, ensuring the property persists regardless of user actions.

Composing Multiple Enhancers

You can stack multiple enhancers using the compose utility (the same function used for middleware), applying them right-to-left:

import { compose, applyMiddleware, createStore } from 'redux'
import thunk from 'redux-thunk'

const composedEnhancer = compose(
  resetEnhancer,
  extraStateEnhancer,
  applyMiddleware(thunk)
)

const store = createStore(rootReducer, composedEnhancer)

The resulting store includes both the reset method and the extraFlag state field while maintaining thunk middleware support. TypeScript correctly infers the intersection of all extensions through the composed StoreEnhancer type.

Key Implementation Files

Summary

  • Store enhancers are higher-order functions that wrap createStore to return augmented store objects with custom methods or state.
  • The StoreEnhancer<Ext, StateExt> type in src/types/store.ts provides compile-time safety for your extensions.
  • Composition via compose() allows stacking multiple enhancers, including applyMiddleware, in a single store creation call.
  • TypeScript integration requires specifying method extensions in the first generic parameter and state extensions in the second to maintain full type inference.
  • Core Redux behavior remains intact because enhancers spread the original store object before adding custom properties.

Frequently Asked Questions

What is the difference between a store enhancer and middleware?

Middleware intercepts dispatched actions to add logging, async handling, or side effects, but cannot add methods to the store object itself. Store enhancers wrap the entire store creation process to add custom methods like reset() or modify the state shape at the root level. According to the reduxjs/redux source code, applyMiddleware is actually implemented as a store enhancer in src/applyMiddleware.ts, demonstrating that enhancers are the lower-level primitive.

Can I use multiple store enhancers together?

Yes. Multiple enhancers compose naturally using the compose utility function, applying them right-to-left. Each enhancer receives the output of the previous one, allowing you to stack functionality such as custom methods, state extensions, and middleware in a single createStore call.

How do I add TypeScript types for custom store methods?

Define your enhancer with the StoreEnhancer<Ext, StateExt> type from src/types/store.ts. Pass an object type describing your custom methods as the first generic argument (e.g., StoreEnhancer<{ reset(): void }>). This propagates through the composition chain so TypeScript recognizes the methods on the final store instance, as verified in test/typescript/enhancers.test-d.ts.

Does adding custom methods break Redux DevTools or other plugins?

No. Because enhancers spread the original store object (...store) before adding properties, all standard Redux APIs (dispatch, getState, subscribe, replaceReducer) remain intact and accessible. Redux DevTools and other libraries that expect a standard Redux store will continue to function normally, though they will not automatically recognize your custom methods unless explicitly typed.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →