HomeAboutBlog

Anurag Sharma

Built with Next.js. View source

↑ Home

© 2026 Anurag Sharma

Back to writing

Mastering State Management with Zustand: A Complete Guide

December 10, 2025

Zustand State Management
Zustand State Management

reactstate-managementzustandfrontend

Zustand is a small, fast, and scalable state management solution for React applications. It provides a simple API that makes state management a breeze without the need for complex setup or boilerplate code.

Why Choose Zustand?

  • →🚀 Minimal API with zero configuration
  • →🔄 Easy to learn and use
  • →⚡ Lightweight (less than 1KB)
  • →🔄 Supports React Concurrent Mode
  • →🛠️ Built-in devtools support
  • →🧩 Middleware support for extending functionality

Installation

First, install Zustand in your project:

npm install zustand
# or
yarn add zustand

Creating a Store

Let's create a simple counter store:

// stores/useCounterStore.ts
import { create } from "zustand";

interface CounterState {
  count: number;
  increment: () => void;
  decrement: () => void;
  reset: () => void;
}

export const useCounterStore = create<CounterState>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
}));

Using the Store in Components

Now, let's use our counter store in a React component:

// components/Counter.tsx
"use client";

import { useCounterStore } from "@/stores/useCounterStore";

export function Counter() {
  const { count, increment, decrement, reset } = useCounterStore();

  return (
    <div className="flex flex-col items-center gap-4 p-4 border rounded-lg">
      <h2 className="text-2xl font-bold">Count: {count}</h2>
      <div className="flex gap-2">
        <Button onClick={decrement}>Decrement</Button>
        <Button onClick={increment}>Increment</Button>
        <Button variant="outline" onClick={reset}>
          Reset
        </Button>
      </div>
    </div>
  );
}

Handling Async Actions

Zustand makes it easy to handle asynchronous operations:

// stores/usePostsStore.ts
import { create } from "zustand";

interface Post {
  id: number;
  title: string;
  body: string;
}

interface PostsState {
  posts: Post[];
  loading: boolean;
  error: string | null;
  fetchPosts: () => Promise<void>;
}

export const usePostsStore = create<PostsState>((set) => ({
  posts: [],
  loading: false,
  error: null,
  fetchPosts: async () => {
    set({ loading: true, error: null });
    try {
      const response = await fetch(
        "https://jsonplaceholder.typicode.com/posts",
      );
      const data = await response.json();
      set({ posts: data, loading: false });
    } catch (error) {
      set({ error: "Failed to fetch posts", loading: false });
    }
  },
}));

Using the Posts Store

// components/PostsList.tsx
"use client";

import { useEffect } from "react";
import { usePostsStore } from "@/stores/usePostsStore";

export function PostsList() {
  const { posts, loading, error, fetchPosts } = usePostsStore();

  useEffect(() => {
    fetchPosts();
  }, [fetchPosts]);

  if (loading) return <div>Loading posts...</div>;
  if (error) return <div className="text-red-500">{error}</div>;

  return (
    <div className="space-y-4">
      <h2 className="text-2xl font-bold mb-4">Latest Posts</h2>
      <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
        {posts.slice(0, 6).map((post) => (
          <div
            key={post.id}
            className="p-4 border rounded-lg hover:shadow-md transition-shadow"
          >
            <h3 className="font-semibold text-lg mb-2">{post.title}</h3>
            <p className="text-gray-600">{post.body.substring(0, 100)}...</p>
          </div>
        ))}
      </div>
    </div>
  );
}

Best Practices

  1. →Keep Stores Focused: Create separate stores for different domains of your application.
  2. →Use Selectors: Only subscribe to the state you need to prevent unnecessary re-renders.
  3. →Leverage Middleware: Use middleware like persist for state persistence or devtools for debugging.
  4. →Type Safety: Always define TypeScript interfaces for your store state and actions.
  5. →Testing: Test your stores in isolation using Jest or your preferred testing framework.

Conclusion

Zustand provides a simple yet powerful way to manage state in your React applications. Its minimal API and small footprint make it an excellent choice for projects of any size. By following the patterns shown in this guide, you can build maintainable and scalable applications with ease.

Further Reading

  • →Zustand Documentation
  • →Zustand with TypeScript
  • →State Management Showdown

Happy coding! 🚀