React Native2025-02-20|6 min read

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

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.