DEV Community

Cover image for Zustand in React Native: A Modern State Management Solution
Suman Bhattarai
Suman Bhattarai

Posted on

Zustand in React Native: A Modern State Management Solution

Zustand in React Native: A Modern State Management Solution

State management has long been one of the most debated topics in the React ecosystem. While Redux dominated for years, the landscape has evolved significantly. Enter Zustand—a lightweight, flexible state management library that's become increasingly popular in React Native development. In this guide, we'll explore why Zustand might be the perfect fit for your next React Native project.

What is Zustand?

Zustand (German for "state") is a minimalist state management library created by the team behind React Spring. Unlike Redux or MobX, Zustand takes a radically simple approach: it uses hooks and doesn't require providers, actions, or reducers. The entire API can be learned in under 15 minutes, yet it's powerful enough to handle complex application state.

At its core, Zustand creates a store using plain JavaScript functions. The store can be accessed from any component without the need for context providers, making it ideal for React Native's component-based architecture.

Zustand Architecure

Why Zustand for React Native?

React Native developers face unique challenges compared to web developers. App bundle size matters more, performance is critical on resource-constrained devices, and the developer experience needs to be smooth for rapid iteration. Zustand addresses all these concerns:

Tiny Bundle Size: At just 1KB gzipped, Zustand adds negligible overhead to your React Native app. This is crucial when every kilobyte counts toward your app's download size and startup time.

Zero Boilerplate: No providers to wrap your app in, no action creators to write, no complex middleware setup. You create a store, and you're ready to go.

Performance: Zustand only re-renders components that subscribe to changed state slices. This fine-grained reactivity is essential in React Native where rendering performance directly impacts user experience.

TypeScript Support: First-class TypeScript support out of the box, which is increasingly important for maintaining large React Native codebases.

Getting Started with Zustand in React Native

Installation is straightforward. In your React Native project, run:

npm install zustand
# or
yarn add zustand
Enter fullscreen mode Exit fullscreen mode

That's it. No peer dependencies, no additional configuration.

Creating Your First Store

Let's build a practical example—a simple cart store for an e-commerce React Native app:

import { create } from 'zustand';

const useCartStore = create((set, get) => ({
  items: [],
  totalPrice: 0,

  addItem: (product) => set((state) => {
    const existingItem = state.items.find(item => item.id === product.id);

    if (existingItem) {
      return {
        items: state.items.map(item =>
          item.id === product.id
            ? { ...item, quantity: item.quantity + 1 }
            : item
        ),
        totalPrice: state.totalPrice + product.price
      };
    }

    return {
      items: [...state.items, { ...product, quantity: 1 }],
      totalPrice: state.totalPrice + product.price
    };
  }),

  removeItem: (productId) => set((state) => {
    const item = state.items.find(item => item.id === productId);
    return {
      items: state.items.filter(item => item.id !== productId),
      totalPrice: state.totalPrice - (item.price * item.quantity)
    };
  }),

  clearCart: () => set({ items: [], totalPrice: 0 }),

  getItemCount: () => {
    const state = get();
    return state.items.reduce((count, item) => count + item.quantity, 0);
  }
}));

export default useCartStore;
Enter fullscreen mode Exit fullscreen mode

Using the Store in Components

Consuming state in your React Native components is beautifully simple:

import React from 'react';
import { View, Text, FlatList, TouchableOpacity } from 'react-native';
import useCartStore from './stores/cartStore';

const CartScreen = () => {
  const items = useCartStore((state) => state.items);
  const totalPrice = useCartStore((state) => state.totalPrice);
  const removeItem = useCartStore((state) => state.removeItem);
  const clearCart = useCartStore((state) => state.clearCart);

  return (
    <View>
      <Text style={{ fontSize: 24 }}>
        Total: ${totalPrice.toFixed(2)}
      </Text>

      <FlatList
        data={items}
        keyExtractor={(item) => item.id}
        renderItem={({ item }) => (
          <View style={{ flexDirection: 'row', padding: 10 }}>
            <Text>{item.name} x {item.quantity}</Text>
            <TouchableOpacity onPress={() => removeItem(item.id)}>
              <Text style={{ color: 'red' }}>Remove</Text>
            </TouchableOpacity>
          </View>
        )}
      />

      <TouchableOpacity onPress={clearCart}>
        <Text>Clear Cart</Text>
      </TouchableOpacity>
    </View>
  );
};
Enter fullscreen mode Exit fullscreen mode

Notice how we're selecting specific pieces of state. This is Zustand's selector pattern—components only re-render when the selected state changes, not when any part of the store updates.

Advanced Patterns for React Native

Persisting State with AsyncStorage

React Native apps often need to persist state between sessions. Zustand makes this easy with middleware:

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

const useUserStore = create(
  persist(
    (set) => ({
      user: null,
      theme: 'light',

      setUser: (user) => set({ user }),
      setTheme: (theme) => set({ theme }),
      logout: () => set({ user: null })
    }),
    {
      name: 'user-storage',
      storage: createJSONStorage(() => AsyncStorage),
    }
  )
);
Enter fullscreen mode Exit fullscreen mode

This automatically saves your store to AsyncStorage and rehydrates it on app launch.

Middleware for Side Effects

Zustand supports middleware for logging, devtools, and more:

import { create } from 'zustand';
import { devtools } from 'zustand/middleware';

const useStore = create(
  devtools(
    (set) => ({
      // your store
    }),
    { name: 'MyAppStore' }
  )
);
Enter fullscreen mode Exit fullscreen mode

This enables debugging with Redux DevTools, which works seamlessly with React Native Debugger.

Slicing Large Stores

For larger applications, you can split your store into slices:

const createUserSlice = (set) => ({
  user: null,
  setUser: (user) => set({ user }),
});

const createSettingsSlice = (set) => ({
  theme: 'light',
  notifications: true,
  setTheme: (theme) => set({ theme }),
  toggleNotifications: () => set((state) => ({
    notifications: !state.notifications
  })),
});

const useStore = create((...a) => ({
  ...createUserSlice(...a),
  ...createSettingsSlice(...a),
}));
Enter fullscreen mode Exit fullscreen mode

Performance Optimization Tips

While Zustand is fast by default, here are some React Native-specific optimizations:

Use Selectors Wisely: Only subscribe to the state you need. Instead of const state = useCartStore(), use const items = useCartStore((state) => state.items).

Shallow Equality for Objects: When selecting multiple values, use shallow equality:

import { shallow } from 'zustand/shallow';

const { items, totalPrice } = useCartStore(
  (state) => ({ items: state.items, totalPrice: state.totalPrice }),
  shallow
);
Enter fullscreen mode Exit fullscreen mode

Transient Updates: For frequently changing values that don't need to trigger renders (like scroll position), use transient updates:

const useStore = create((set) => ({
  scrollPosition: 0,
  setScrollPosition: (position) => {
    useStore.setState({ scrollPosition: position }, true); // true = skip re-render
  }
}));
Enter fullscreen mode Exit fullscreen mode

Zustand vs. Other Solutions

How does Zustand compare to other state management solutions in React Native?

vs. Redux: Redux requires more boilerplate, uses context (which can impact performance), and has a steeper learning curve. Zustand is simpler and faster, though Redux has a larger ecosystem and more middleware options.

vs. Context API: React Context causes all consumers to re-render when any part of the context changes. Zustand's selector pattern prevents this, making it more performant for frequently updating state.

vs. MobX: MobX is powerful but uses decorators and observables, which can be harder to reason about. Zustand's functional approach is more aligned with modern React patterns.

vs. Recoil/Jotai: These are atom-based solutions that work differently. Zustand's centralized store approach can be simpler for many use cases, though atoms excel at managing independent pieces of state.

Real-World Use Cases in React Native

Zustand shines in several React Native scenarios:

  • Navigation State: Managing global navigation state without prop drilling
  • Theme Management: Switching between light/dark modes across the app
  • Authentication: Storing user tokens and profile data
  • Offline-First Apps: Combined with AsyncStorage for robust offline support
  • Real-Time Updates: Managing WebSocket connections and live data
  • Form State: For complex multi-step forms across screens

Conclusion

Zustand represents a paradigm shift in React Native state management. Its simplicity doesn't mean it's simplistic—it's a thoughtfully designed library that respects the principle of doing one thing well. By eliminating boilerplate and providing excellent performance out of the box, Zustand lets you focus on building features rather than managing infrastructure.

For React Native developers tired of Redux's ceremony or Context's performance issues, Zustand offers a refreshing alternative. Its tiny bundle size, intuitive API, and powerful middleware system make it an excellent choice for apps of any size.

Start small with a single store, and you'll quickly find yourself reaching for Zustand in every new React Native project. The combination of simplicity and power is hard to resist once you've experienced it.

Ready to give it a try? Your next React Native app is waiting for its perfect state management solution.


This blog post is designed for a 5-minute read. For more information, check out the official Zustand documentation and explore the thriving community around this excellent library.

Top comments (0)