How to fix The return value of the reducer must be a new object, not the draft in React

ReactINTERMEDIATEMEDIUMUpdated Dec 10, 2025ยท 96 views

This error occurs in Redux Toolkit when using Immer incorrectly in reducers. It happens when you try to both mutate the draft state AND return a new value, which violates Immer's rules. The fix involves understanding how Immer works with Redux Toolkit's createReducer or createSlice.

Quick fix

Fixes The return value of the reducer must be a new object, not the draft

// Incorrect - both mutates AND returns
const reducer = createReducer(initialState, (builder) => {
  builder.addCase(someAction, (state, action) => {
    state.value = action.payload; // Mutation
    return state; // Explicit return - ERROR!
  });
});

// Correct - only mutate
const reducer = createReducer(initialState, (builder) => {
  builder.addCase(someAction, (state, action) => {
    state.value = action.payload; // Just mutate, no return
  });
});

// Also correct - only return (no mutation)
const reducer = createReducer(initialState, (builder) => {
  builder.addCase(someAction, (state, action) => {
    return { ...state, value: action.payload }; // Return new object
  });
});
  1. Fix arrow functions with implicit returns
  2. Avoid mixing mutation and return patterns
  3. Check for Immer version conflicts
  4. Use current() utility for debugging
react โ€” error
// Incorrect - both mutates AND returns
const reducer = createReducer(initialState, (builder) => {
builder.addCase(someAction, (state, action) => {
state.value = action.payload; // Mutation
return state; // Explicit return - ERROR!
});
});
// Correct - only mutate
const reducer = createReducer(initialState, (builder) => {
builder.addCase(someAction, (state, action) => {
state.value = action.payload; // Just mutate, no return
});
});
// Also correct - only return (no mutation)
const reducer = createReducer(initialState, (builder) => {
builder.addCase(someAction, (state, action) => {
return { ...state, value: action.payload }; // Return new object
});
});
The return value of the reducer must be a new object, not the draft

What this error means

The "return value must be a new object, not the draft" error is thrown by Immer when used with Redux Toolkit. Immer allows you to write reducers that appear to mutate state directly, but internally it tracks changes to produce an immutable update. This error occurs when you violate Immer's core rule: you must either mutate the draft state OR return a new state value, but not both. Redux Toolkit uses Immer internally with createReducer and createSlice, so this error typically appears when writing reducer logic incorrectly.

Typical symptoms

  • Redux Toolkit throws error: "The return value of the reducer must be a new object, not the draft"
  • Application crashes when dispatching certain actions
  • Error appears in console when using arrow functions with implicit returns in reducers
  • State updates fail to apply correctly
  • Error occurs after updating to newer versions of Immer or Redux Toolkit

Common causes

  1. Using arrow functions with implicit returns that both mutate and return
  2. Attempting to return the draft state object instead of letting Immer handle it
  3. Mixing mutation syntax with return statements in the same reducer
  4. Version mismatch between Immer and Redux Toolkit
  5. Manually importing and using Immer's produce function inside Redux Toolkit reducers

How to fix "The return value of the reducer must be a new object, not the draft"

1Fix arrow functions with implicit returns

Change arrow functions that implicitly return to either mutate OR return explicitly:

javascript
// Incorrect - both mutates AND returns
const reducer = createReducer(initialState, (builder) => {
  builder.addCase(someAction, (state, action) => {
    state.value = action.payload; // Mutation
    return state; // Explicit return - ERROR!
  });
});

// Correct - only mutate
const reducer = createReducer(initialState, (builder) => {
  builder.addCase(someAction, (state, action) => {
    state.value = action.payload; // Just mutate, no return
  });
});

// Also correct - only return (no mutation)
const reducer = createReducer(initialState, (builder) => {
  builder.addCase(someAction, (state, action) => {
    return { ...state, value: action.payload }; // Return new object
  });
});
2Avoid mixing mutation and return patterns

Choose one pattern and stick with it throughout your reducer:

javascript
// Choose EITHER mutation style:
case 'UPDATE_VALUE':
  draft.value = action.payload;
  // No return statement - Immer handles it
  break;

// OR return style:
case 'UPDATE_VALUE':
  return {
    ...state,
    value: action.payload
  };

// NOT both:
case 'UPDATE_VALUE':
  draft.value = action.payload; // Mutation
  return draft; // Return - ERROR!
3Check for Immer version conflicts

Ensure Redux Toolkit and Immer versions are compatible:

bash
# Check installed versions
npm list immer redux @reduxjs/toolkit

# If there's a version mismatch, let Redux Toolkit manage Immer:
npm uninstall immer
npm install @reduxjs/toolkit

# Redux Toolkit will bring its own compatible Immer version

Redux Toolkit ships with a specific Immer version. Manually installing a different Immer version can cause this error.

4Use current() utility for debugging

When you need to inspect draft state during debugging, use Immer's current() utility:

javascript
import { current } from '@reduxjs/toolkit';

const reducer = createReducer(initialState, (builder) => {
  builder.addCase(someAction, (state, action) => {
    console.log('Current draft state:', current(state));
    state.value = action.payload;
    // No return needed
  });
});

// current() creates a plain copy for inspection without affecting the draft
5Convert between mutation and return styles

If you need to convert existing code, here's how to switch patterns:

javascript
// From mutation to return style:
// Before:
state.items.push(action.payload);

// After:
return {
  ...state,
  items: [...state.items, action.payload]
};

// From return to mutation style:
// Before:
return { ...state, count: state.count + 1 };

// After:
state.count += 1;

Remember: With mutation style, you modify properties directly on the draft. With return style, you create and return a new object.

Advanced notes

Redux Toolkit's createReducer and createSlice use Immer internally to enable "mutative" syntax while maintaining immutability. Immer works by creating a draft proxy that records mutations. When the reducer finishes, Immer produces the next immutable state based on those mutations. The error occurs when Immer detects both mutations AND a return value, which creates ambiguity about which state should be used. This is a protective measure to prevent subtle bugs. In complex reducers, you might need to use Immer's produce function directly for nested updates, but be careful not to mix patterns.

Frequently asked questions

What does "The return value of the reducer must be a new object, not the draft" mean?
The "return value must be a new object, not the draft" error is thrown by Immer when used with Redux Toolkit. Immer allows you to write reducers that appear to mutate state directly, but internally it tracks changes to produce an immutable update. This error occurs when you violate Immer's core rule: you must either mutate the draft state OR return a new state value, but not both.
How do I fix "The return value of the reducer must be a new object, not the draft"?
This error occurs in Redux Toolkit when using Immer incorrectly in reducers. It happens when you try to both mutate the draft state AND return a new value, which violates Immer's rules. The fix involves understanding how Immer works with Redux Toolkit's createReducer or createSlice.
What causes "The return value of the reducer must be a new object, not the draft"?
Using arrow functions with implicit returns that both mutate and return Attempting to return the draft state object instead of letting Immer handle it Mixing mutation syntax with return statements in the same reducer
Is the "The return value of the reducer must be a new object, not the draft" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading