# How `replaceReducer` Enables Code Splitting and Hot Reloading in Redux

> Learn how Redux replaceReducer facilitates code splitting and hot reloading by swapping reducers at runtime You can dynamically load new state slices and update reducer logic without losing app state

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

---

**The `replaceReducer` method swaps the root reducer at runtime, allowing Redux to dynamically load new state slices for code splitting and instantly update reducer logic during development for hot module replacement without losing application state.**

`replaceReducer` is a core API method in the `reduxjs/redux` repository that removes the limitation of having a single, static root reducer. By enabling runtime reducer replacement, it powers two essential modern development techniques: lazy-loading reducers for code-split applications and hot reloading for rapid iteration.

## Understanding `replaceReducer`

In [`src/createStore.js`](https://github.com/reduxjs/redux/blob/main/src/createStore.js), the Redux store factory exposes `replaceReducer` as a method that accepts a new reducer function and immediately swaps the internal `currentReducer` reference. After the swap, it dispatches a dummy action (`@@redux/REPLACE`) to ensure any newly added state slices initialize with their default values. This mechanism preserves the existing state tree while allowing the logic that processes actions to change completely.

## Code Splitting with `replaceReducer`

### The Challenge of Dynamic Reducers

Large applications often use route-based or feature-based code splitting to reduce initial bundle size. However, Redux traditionally requires all reducers to be known at store creation time via `combineReducers`. When a lazy-loaded chunk arrives with its own state slice, the store must incorporate the new reducer without recreation.

### Implementing Lazy-Loaded Reducers

The Universal example in the Redux repository demonstrates the standard pattern. In [`examples/universal/common/store/configureStore.js`](https://github.com/reduxjs/redux/blob/main/examples/universal/common/store/configureStore.js), the store exposes an `injectReducer` helper that maintains a registry of async reducers:

```javascript
// examples/universal/common/store/configureStore.js
import { createStore, combineReducers } from 'redux'
import staticReducers from './staticReducers'

export default function configureStore(initialState) {
  const store = createStore(createRootReducer(), initialState)
  
  // Registry for dynamically added reducers
  store.asyncReducers = {}
  
  // Helper to inject new reducers
  store.injectReducer = (key, asyncReducer) => {
    store.asyncReducers[key] = asyncReducer
    store.replaceReducer(createRootReducer(store.asyncReducers))
  }
  
  return store
}

function createRootReducer(asyncReducers = {}) {
  return combineReducers({
    ...staticReducers,
    ...asyncReducers,
  })
}

```

When a code-split chunk loads, it calls `store.injectReducer('feature', featureReducer)`, triggering `replaceReducer` to merge the new slice into the root reducer without losing existing state.

## Hot Module Replacement with `replaceReducer`

### Webpack HMR Integration

During development, Webpack’s Hot Module Replacement (HMR) allows reducer code to be updated without a full page refresh. The Real-World example in [`examples/real-world/src/store/configureStore.dev.js`](https://github.com/reduxjs/redux/blob/main/examples/real-world/src/store/configureStore.dev.js) implements this pattern:

```javascript
// examples/real-world/src/store/configureStore.dev.js
import { createStore } from 'redux'
import rootReducer from '../reducers'

export default function configureStore(initialState) {
  const store = createStore(rootReducer, initialState)

  if (module.hot) {
    module.hot.accept('../reducers', () => {
      const nextRootReducer = require('../reducers').default
      store.replaceReducer(nextRootReducer)
    })
  }

  return store
}

```

When the reducer files change, Webpack triggers the `accept` callback, which imports the updated reducer module and calls `replaceReducer`. The store immediately begins using the new logic while preserving all existing state and subscribed listeners.

## Internal Mechanics and State Initialization

According to the Redux source in [`src/createStore.js`](https://github.com/reduxjs/redux/blob/main/src/createStore.js), `replaceReducer` performs two critical operations:

1. **Reference Swap**: It updates the internal `currentReducer` variable to the new function passed as an argument.
2. **Re-initialization Dispatch**: It dispatches an action with type `@@redux/REPLACE` (or similar internal type) to force the new reducer to populate any default state for newly added slices.

This ensures that when code splitting adds a new state slice, the UI can immediately access the new slice's initial state without manual intervention.

## Summary

- **`replaceReducer`** swaps the root reducer function at runtime without recreating the store or losing state.
- **Code splitting** relies on `replaceReducer` to inject lazy-loaded reducers into the existing store, typically via a helper like `injectReducer` that rebuilds the combined reducer and calls `replaceReducer`.
- **Hot reloading** uses `replaceReducer` in Webpack HMR accept handlers to swap updated reducer code instantly during development.
- The method dispatches an internal initialization action to ensure new state slices receive their default values.

## Frequently Asked Questions

### What happens to existing state when `replaceReducer` is called?

Existing state is preserved. `replaceReducer` only updates the function used to process actions. However, if the new reducer handles different state keys than the previous one, any state for keys no longer handled will be removed from the state tree, while new keys will be initialized with their default values.

### Can `replaceReducer` be used with Redux Toolkit?

Yes. While Redux Toolkit's `configureStore` simplifies store setup, it returns a standard Redux store that includes `replaceReducer`. You can access it directly or use the `injectReducer` pattern with RTK's `combineSlices` or traditional `combineReducers` to enable code splitting in applications using Redux Toolkit.

### How does `replaceReducer` differ from creating a new store?

Creating a new store with `createStore` initializes a fresh state tree and requires re-subscribing all listeners, which breaks the application connection. `replaceReducer` maintains the existing store instance, state reference, and subscriber registry, enabling seamless updates without component unmounting or state loss.

### Is `replaceReducer` safe for production applications?

Yes, `replaceReducer` is a stable, public API used safely in production for code-splitting scenarios. However, it should be used carefully to ensure the new reducer maintains compatibility with existing state shapes. For hot reloading, it is typically disabled in production builds since HMR is a development-only feature.