React Native2025-02-20|6 min read

Redux vs Zustand vs Jotai: React Native State Management in 2024

By Vipul Kaushik

State management is one of the most debated topics in React Native. The answer has shifted considerably in the last two years: Context API scaled further than people expected, Redux lost ground to lighter alternatives, and Zustand and Jotai emerged as genuine production options. Here's how I evaluate each for mobile projects today, with real code to back it up.

The Landscape in 2025

Three tools dominate new project decisions: Redux Toolkit (the modern Redux experience), Zustand (lightweight, store-based), and Jotai (atomic, bottom-up). All three are actively maintained and work in React Native without modification. The choice is rarely about capability — it's about team mental model and app complexity.

Redux Toolkit

Redux Toolkit (RTK) is Redux without the boilerplate. createSlice generates actions and reducers together, and createAsyncThunk handles async flows cleanly.

import { createSlice, createAsyncThunk, configureStore } from '@reduxjs/toolkit';

export const fetchUser = createAsyncThunk('user/fetch', async (userId) => {
  const response = await api.getUser(userId);
  return response.data;
});

const userSlice = createSlice({
  name: 'user',
  initialState: { data: null, loading: false, error: null },
  reducers: {
    clearUser: (state) => { state.data = null; },
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchUser.pending, (state) => { state.loading = true; })
      .addCase(fetchUser.fulfilled, (state, action) => {
        state.loading = false;
        state.data = action.payload;
      })
      .addCase(fetchUser.rejected, (state, action) => {
        state.loading = false;
        state.error = action.error.message;
      });
  },
});

export const store = configureStore({
  reducer: { user: userSlice.reducer },
});

Use RTK when: you have a large team, complex async flows, need Redux DevTools time-travel debugging, or are maintaining an existing Redux codebase.

Zustand

Zustand is a single-store solution with a minimal API. The entire store and actions live in one create call, and components only re-render when the specific slice they consume changes:

import { create } from 'zustand';

const useAuthStore = create((set, get) => ({
  user: null,
  isAuthenticated: false,

  login: async (credentials) => {
    const user = await api.login(credentials);
    set({ user, isAuthenticated: true });
  },

  logout: () => {
    set({ user: null, isAuthenticated: false });
    api.clearSession();
  },

  getToken: () => get().user?.token,
}));

// In a component
function ProfileScreen() {
  const { user, logout } = useAuthStore();
  return <Button title="Sign out" onPress={logout} />;
}

Use Zustand when: you want a Redux-like model without the ceremony, or for small-to-medium apps where RTK feels heavyweight.

Jotai

Jotai takes the atomic approach: state lives in small, composable atoms rather than a single store. Derived state is computed lazily and cached automatically:

import { atom, useAtom, useAtomValue } from 'jotai';

const userAtom = atom(null);
const themeAtom = atom('dark');

// Derived atom — recomputes only when userAtom changes
const displayNameAtom = atom((get) => {
  const user = get(userAtom);
  return user ? `${user.firstName} ${user.lastName}` : 'Guest';
});

function Header() {
  const displayName = useAtomValue(displayNameAtom);
  return <Text>Welcome, {displayName}</Text>;
}

Use Jotai when: your state is naturally granular — UI state, form fields, feature flags — or you're coming from a Recoil background.

Bundle Size Comparison

  • Zustand: ~1.2 KB minified + gzipped
  • Jotai: ~3 KB minified + gzipped
  • Redux Toolkit (core): ~11 KB minified + gzipped (includes Immer and Redux)

For mobile, bundle size affects startup time. On low-end Android devices, every additional KB matters during the initial JS parse phase. Zustand has a meaningful advantage here.

Persistence Strategies

All three libraries persist well with @react-native-async-storage/async-storage.

For Redux Toolkit, use redux-persist:

import { persistStore, persistReducer } from 'redux-persist';
import AsyncStorage from '@react-native-async-storage/async-storage';

const persistConfig = {
  key: 'root',
  storage: AsyncStorage,
  whitelist: ['auth'],
};
const persistedReducer = persistReducer(persistConfig, rootReducer);

For Zustand, use the built-in persist middleware:

import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import AsyncStorage from '@react-native-async-storage/async-storage';

const useAuthStore = create(
  persist(
    (set) => ({ user: null, login: (user) => set({ user }) }),
    {
      name: 'auth-storage',
      storage: createJSONStorage(() => AsyncStorage),
    }
  )
);

When to Use Which

  • Redux Toolkit: Large teams, complex async workflows, existing Redux codebase, need for DevTools.
  • Zustand: Medium apps, teams that find Redux verbose, apps with a handful of global state domains.
  • Jotai: Highly granular state, lots of derived/computed values, smaller apps or feature-level state isolation.

My Take

For most new React Native apps in 2025, I reach for Zustand first. It's small, the mental model is simple enough to onboard a new developer in minutes, and it scales to medium complexity without friction. I introduce RTK when the team is large enough that Redux's structural conventions become a feature rather than a constraint. Jotai earns its place in apps with genuinely atomic state — think a complex form builder or a real-time collaborative tool where individual fields have independent subscription semantics.

Written by Vipul Kaushik — CTO at Rolling Around, 8+ years building React Native apps.

Hire me →