Performance evaluation of Zustand vs Redux for state management in React Applications

Performance evaluation of Zustand vs Redux for state management in React Applications


React Zustand Redux Profiler

While choosing between state management libraries in React-based web applications, a brief internet search will show various blogs and official documentations describing factors like simplicity, performance, standardization and data flow. To the best of my knowledge, no article conducts a thorough quantitative performance evaluation between the libraries. Different sources list conflicting viewpoints where some state that Zustand is faster than Redux while others mention there is no notable difference.

In this blog, we will design a simple experiment to conduct a thorough rendering performance evaluation with both the libraries of simple actions like changing state individually, group change and deep nested changes for complex data structures.

Experimental Setup

  1. Setup parallel state management implementations in Redux & Zustand.
export const useZustandStore = create<State>()(
  immer((set, get) => ({
    ...initialState,
    increment: () => set((state) => { state.count++; }),
    batchUpdate: () => set((state) => { state.count += 100; }),
    incrementDeepNested: () => set((state) => { state.nested.level1.level2.level3.value++; }),
    reset: () => set(() => ({ ...initialState })),
  }))
);
const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => { state.count++; },
    batchUpdate: (state) => { state.count += 100; },
    incrementDeepNested: (state) => { state.nested.level1.level2.level3.value++; },
    reset: () => ({ ...initialState }),
  },
});

Note that immer is used in Zustand to maintain consistency with Redux’s createSlice which internally uses immer.

  1. Define common actions in both implementations:
    • increment: Increases count by 1.
    • batchUpdate: Increases count by 100.
    • incrementDeepNested: Increments variable value with the structure: state.nested.level1.level2.level3.value by 1.
    • reset: Resets entire store to 1.
  2. Create an experiment to run each action n times to track mean and standard deviation in rendering time. n is set to 5000 for this blog.
  3. Setup Profiler to track time for each action.
  4. Add a CSV export option to download results for each experiment.
  5. Analyze results in a Jupyer Notebook.

Steps 1-5 are packaged inside a React App which can be accessed here. The figure below shows a screenshot of the app.

Profiler Dashboard

Understanding Profiler Metrics

In the experiment, I ran each action 5000 times and downloaded results from React Profiler in CSV format for each action. Data columns are as follows:

  • framework: React or Redux
  • action: Mount, Increment, Batch Update, Deep Nested
  • phase: mount or update
  • actualDuration: The number of milliseconds spent rendering the <Profiler> and its descendants for the current update.
  • baseDuration: The number of milliseconds estimating how much time it would take to re-render the entire <Profiler> subtree without any optimizations.
  • startTime: A numeric timestamp for when React began rendering the current update.
  • commitTime: A numeric timestamp for when React committed the current update.

Mean Rendering Time

Render Time

Mean render duration for all three actions is consistently lower for Zustand indicating it is a superior choice for small applications. However, a high standard deviation can be seen for deep nested updates using Zustand.

Memoization Efficiency

Memoization Graph

Memoization is a performance optimization technique used to speed up programs by caching the results of expensive actions. baseDuration estimates a worst-case cost of rendering the component (i.e. without memoization). I compare it with actualDuration using the formula 1 - (actual/base) to compute memoization efficiency.

It can be clearly seen from the plot that Redux is significantly better than Zustand for selecting re-rendering. In fact, Zustand has negative efficiency for Deep Nested Updates indicating that a full subtree re-rendering might be occuring in some cases. This shows that for complex, large applications React might be a better choice because it automatically deduplicates updates and minimizes reactivity scope. Redux scales better in large apps with many components subscribing to different slices of state.

Mount Time

Mount Time Graph

Zustand has a significantly faster mount performance than React with a lightweight setup like mine. While initial mount time might be less important for most applications, it is still relevant to high frequency, single screen apps.

Conclusion

Based on the above experimentation, analysis and results, it can be concluded that Zustand is superior to Redux for small, light-weight applications in terms of rendering performance. However, for large complex applications or deep nested data structures, Redux can deliver more consistent results with high memoization efficiency.

© 2025 Hitesh Agarwal