Zustand 101: Beginner's Guide

Zustand 101: Beginner's Guide

Publish Date: Jun 17
1 2

Have you ever wished for a state management library that's as simple as useState but works globally across your entire React application ? Are you looking for a state management library that doesn't require tons of boilerplate code ? Then this article is for you, it will show you how to handle React state using a simple external library called Zustand.

Indeed! State and state management have always been the backbone of React apps, determining when and how components re-render. While React's built-in useState hook works perfectly for local component state, things get worse when you need to share state across multiple components.

As your React app grows and becomes more complex, you'll find yourself facing challenges like prop drilling, context provider hell, and the need for a cleaner way to manage state.


React State Management


You can manage the state of a React application in several ways.

1.Built-in React State Solutions: Native hooks like useState, useReducer, useContext, and useMemo provide foundational state management capabilities.

2.Indirect State Managers: There are external libraries such as React Router and React Query. They are not primarily used to manage the state. Yet, when combined with a native hook, they can handle the state well.

3.Dedicated State Management Libraries: Purpose-built solutions like Redux, Zustand, Jotai, and Valtio are designed exclusively for comprehensive state management across your application.


What is Zustand ?

Zustand is a lightweight state management library. It is compact, fast, and scalable. It's simple with little boilerplate code. It doesn’t rely on a provider. As a result, you don’t have to code as much React logic, which may lessen the things we tend to forget. It works based on simplified flux principles and primarily makes use of hooks.

Why Zustand ?
  • It is faster than context. It lets you subscribe to specific slices of state, reducing unnecessary re-renders.

  • It does state merging by default. Consider updating a single property of an object state, {name: 'John', age: 25}. You can directly set {age: 26}. Zustand will merge the data for you. You don’t have to distribute the old state and update properties like {…state, age: 26}.

  • It is extendable by default. Thus, you can use a variety of middleware types such as persist, immer, devtools ... etc.

  • It is less opinionated and rigid. You don’t have to stick to one way of doing things. Even though there is a recommended approach, you are not required to adopt it.


End of theory. Let's get your hands dirty now!

Zustand with ReactJs


Let's create a React project to show how to manage the states using Zustand. Let's consider a simple Counter application to understand Zustand better. The steps are listed below.

1. Create a React application

Create your React application using the below command.

npm create vite@latest 'project-name' 
Enter fullscreen mode Exit fullscreen mode


2. Install Zustand Dependency

Go to the project’s directory and install the zustand dependency to manage the React state.

npm install
npm i zustand
Enter fullscreen mode Exit fullscreen mode


3. Let's Start

  • Let’s first build a basic counter using React's local state to understand the problem Zustand solves.

Let's create a Counter application with simple increase and decrease button as seen in the below image.

Counter app

refer below code.

//App.jsx

import { useState } from 'react'
import './App.css'

function App() {

  return (
    <div>
      <Counter/>
    </div>
  )
}

function Counter(){
  const [count, setCount] = useState(0)

  return(
    <div>
      <CurrentCount count={count}/>
      <br />
      <div className='flex'>
        <Increase setCount={setCount}/>
        <Decrease setCount={setCount}/>
      </div>
    </div>
  )
}


function CurrentCount({count}){
  return(
    <div className='text-8xl'>
      {count}
    </div>
  )
}

function Decrease({setCount}){
  return(
    <div className='m-3'>
      <button onClick={()=>{setCount(c=>c-1)}}>Decrease Count</button>
    </div>
  )
}

function Increase({setCount}){
  return(
    <div className='m-3'>
      <button onClick={()=>{setCount(c=>c+1)}}>Increase Count</button>
    </div>
  )
}
export default App
Enter fullscreen mode Exit fullscreen mode
  • Upon clicking either of the increase or decrease button, only the count part should re-render, but this is not the case here, on clicking either of the buttons, the count, both of the button and the whole app component re-renders unnecessarily, as seen in below images.

on clicking increase both the buttons re-render

on clicking decrease both the buttons re-render

  • So, now let's use Zustand to manage the state and resolve this issue.


4. Using Zustand to Fix Re-Renders

Let's create a store. Refer the below code to create Store.jsx file.

import { create } from "zustand";

export const useCounterStore = create((set)=>({
    count:0,
    increment:()=>{
        set((state)=>({count:state.count+1}))
    },
    decrement:()=>{
        set((state)=>({count:state.count-1}))
    }
}))
Enter fullscreen mode Exit fullscreen mode
  • Zustand store is a hook, which is why useCounterStore is the component name. createis the method used to create the store. The storeis the sole source of truth that each component shares. The function setis used to modify the state of a variable or object.

  • The state object of the store in the example contains the following field and methods: count, which stores the current count of the counter and increment and decrement methods which increase and decrease the count respectively.

  • The increment and decrement methods use set to modify the state of the count.


4. Bind the component with your store

Let's modify the code inside App.jsx using the useCounterStore.

import './App.css'
import { useCounterStore } from './store'

function App() {
  return (
    <div>
      <CurrentCount/>
      <Buttons/>
    </div>
  )
}


function CurrentCount(){
  const currcount=useCounterStore((state)=>state.count);
  return(
    <div className='text-8xl'>
      {currcount}
    </div>
  )
}

function Buttons(){
const increase =useCounterStore((state)=>state.increment);
  return (
    <div className='flex'>
      <div className='m-3'><button onClick={increase}>Increase</button></div>
      <div className='m-3'><button onClick={useCounterStore((state)=>state.decrement)}>Decrease</button></div>
    </div>
  )
}
export default App
Enter fullscreen mode Exit fullscreen mode
  • The App.jsx contains three components i.e. App, CurrentCount and Buttons.
  • The App is the parent component which contains both the CurrentCount and Buttons components.
  • The CurrentCount component subscribes only to countfrom the useCounterStore.
  • The Buttons component subscribes only to the increment and decrement methods and does not re-render when the value of count changes.

Now, on clicking either of the increase or decrease button, only the count part re-renders and there are no unnecessary re-renders, as seen in below images.

on clicking increase only count re-render

on clicking decrease only count re-render

Neither of the button's re-render unnecessarily when the value of count changes.


Since the Zustand store is a hook, you can use it anywhere. In contrast to Redux or Redux Toolkit, no context provider is required. Simply select your state, and the component will re-render when the state changes. We must give a selector to the useCounterStore to get only the desired slice.

We are not done yet! Zustand has a big advantage: Middlewares.

which we'll be covering in the upcoming blog.


Summary

As we mentioned in this article, state management is critical in React applications. As your application grows, you will have to pick a strong way to manage its state. The Zustand library is an ideal remedy for managing your React state. It is much simpler and has less boilerplate than Redux. You can also explore different state management libraries to find the right one for your application.

References

  1. https://zustand.docs.pmnd.rs/getting-started/introduction
  2. https://youtu.be/_ngCLZ5Iz-0?si=ntdxgnViME_fkZwf

Comments 2 total

  • Admin
    AdminJun 17, 2025

    Hi Dev.to contributors! If you've published on Dev.to, read this: We're offering DEV Contributor rewards to reward our amazing writing community. Claim your rewards here (instant distribution). – Admin

  • Karandeep Singh
    Karandeep SinghJun 29, 2025

    Really good explanation and examples! Over the last couple of months, I’ve been digging deep into Zustand architecture too, and while it’s easy for modern patterns. We’ve been experimenting with a lightweight pub-sub architecture for state management that feels cleaner for apps. Interesting to see different perspectives on this! can read about it on my profile (not doing the promotion though)

Add comment