Zustand is a small, fast, and scalable state management solution for React. It is an excellent alternative to more complex state management libraries like Redux. Zustands's API is minimalistic, and it's highly performant, making it a good choice for managing global state in React applications.

Key Features of Zustand:

  • Minimal boilerplate: No need to define actions or reducers.
  • Simple API: Just create a store and use it.
  • Reactivity: Components automatically re-render when the state they depend on changes.
  • Performance: Zustand uses hooks internally to minimize unnecessary re-renders.

Setting Up Zustand in a React Application

Let's go through how to set up and use Zustand in a React app for global state management.

1. Install Zustand

You can install Zustand via npm or yarn:

npm install zustand

or

yarn add zustand

2. Create a Store with Zustand

In Zustand, you define a store using the create function, which takes a function that returns the state object.

Here’s an example of a simple store that manages a counter and a theme (light or dark).

import create from 'zustand'

// Create a store
const useStore = create((set) => ({
  count: 0, // state variable
  theme: 'light', // state variable for theme
  increment: () => set((state) => ({ count: state.count + 1 })), // action to increment count
  decrement: () => set((state) => ({ count: state.count - 1 })), // action to decrement count
  toggleTheme: () => set((state) => ({ theme: state.theme === 'light' ? 'dark' : 'light' })), // toggle between light/dark theme
}))

In this example:

  • count is the state variable.
  • increment and decrement are actions that update the count state.
  • theme is another state variable for the current theme (either 'light' or 'dark').
  • toggleTheme is an action to toggle the theme.

3. Using the Store in Components

You can use the store in your components with the useStore hook. Components that use state from the store will automatically re-render when the state changes.

Example: Counter Component

import React from 'react'
import { useStore } from './store' // import your Zustand store

function Counter() {
  const { count, increment, decrement } = useStore((state) => ({
    count: state.count,
    increment: state.increment,
    decrement: state.decrement,
  }))

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  )
}

export default Counter

Example: Theme Toggle Component

import React from 'react'
import { useStore } from './store' // import your Zustand store

function ThemeToggle() {
  const { theme, toggleTheme } = useStore((state) => ({
    theme: state.theme,
    toggleTheme: state.toggleTheme,
  }))

  return (
    <div>
      <p>Current Theme: {theme}</p>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  )
}

export default ThemeToggle

4. Connecting Zustand to Your Application

You can now use the Counter and ThemeToggle components in your app, and they will have access to the shared global state.

import React from 'react'
import Counter from './Counter'
import ThemeToggle from './ThemeToggle'

function App() {
  return (
    <div>
      <h1>My Zustand App</h1>
      <Counter />
      <ThemeToggle />
    </div>
  )
}

export default App

5. Persisting State Across Reloads (Optional)

Zustand supports state persistence using middleware. You can use the persist middleware to store the state in localStorage or sessionStorage.

Example: Persisting the theme state

import create from 'zustand'
import { persist } from 'zustand/middleware'

const useStore = create(
  persist(
    (set) => ({
      count: 0,
      theme: 'light',
      increment: () => set((state) => ({ count: state.count + 1 })),
      decrement: () => set((state) => ({ count: state.count - 1 })),
      toggleTheme: () => set((state) => ({ theme: state.theme === 'light' ? 'dark' : 'light' })),
    }),
    {
      name: 'my-app-storage', // the name of the storage key
      getStorage: () => localStorage, // storage type (localStorage or sessionStorage)
    }
  )
)

With this middleware, the theme and other state variables will persist across page reloads.

6. Using Zustand in Large Applications

As your application grows, you might want to organize your stores by dividing them into multiple smaller stores based on feature areas (e.g., user, settings, etc.). Zustand allows you to create multiple stores and combine them into one. Here's an example:

Example: Multiple Stores

// store/userStore.js
import create from 'zustand'

export const useUserStore = create((set) => ({
  user: null,
  setUser: (user) => set({ user }),
}))

// store/settingsStore.js
import create from 'zustand'

export const useSettingsStore = create((set) => ({
  theme: 'light',
  toggleTheme: () => set((state) => ({ theme: state.theme === 'light' ? 'dark' : 'light' })),
}))

You can then use both stores in your components:

import { useUserStore } from './store/userStore'
import { useSettingsStore } from './store/settingsStore'

function App() {
  const { user, setUser } = useUserStore((state) => ({
    user: state.user,
    setUser: state.setUser,
  }))

  const { theme, toggleTheme } = useSettingsStore((state) => ({
    theme: state.theme,
    toggleTheme: state.toggleTheme,
  }))

  return (
    <div>
      <h1>User: {user ? user.name : 'Guest'}</h1>
      <button onClick={() => setUser({ name: 'John Doe' })}>Set User</button>

      <p>Current Theme: {theme}</p>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  )
}

Conclusion

Zustand is a simple and powerful state management library for React. It provides an easy-to-use API with minimal setup, making it great for small to large applications. By following the steps above, you can manage global state effectively using Zustand and even extend it for more complex use cases.