---
title: "Zustand: State Management Without Boilerplate"
description: "Discover Zustand, a lightweight state management library for React. Build a TypeScript-based todo app and simplify your React development process."
slug: "zustand-state-management-without-boilerplate"
url: "https://furkanbaytekin.dev/blogs/zustand-state-management-without-boilerplate"
author: "Furkan Baytekin"
category: "Software"
tags: []
published: 2024-12-26T19:03:02Z
updated: 2024-12-26T19:03:02Z
readTime: 4
cover: "https://myblogcms.furkanbaytekin.dev/uploads/cover/zustand-state-management-without-boilerplate+1734310602579"
---

Managing global state in React applications doesn’t have to be complicated. Zustand is a lightweight state management library that simplifies the process with a minimalistic API and powerful features.

In this blog post, we’ll explore how Zustand works with a practical example: a **todo list** application built in TypeScript.

---

## What Is Zustand?

Zustand (*German for "state"*) is a fast, flexible, and boilerplate-free state management library. Unlike Redux, Zustand avoids complexity, allowing you to focus on building features.

### Key Features:

- **Minimal API**: Define and access state with ease.
- **Built for React**: Fully compatible with React's reactivity model.
- **TypeScript Ready**: First-class TypeScript support for strongly typed stores.
- **Efficient Updates**: Components only re-render when their specific slice of state changes.

---

## Installation

To get started, install Zustand using your package manager:

```bash
npm install zustand
# or
yarn add zustand
```

---

## Building a Todo List with Zustand

Let’s create a todo list application to see Zustand in action. We’ll handle adding, toggling, and removing todos, all managed in a single store.

---

### Step 1: Create the Store

We’ll use Zustand’s `create` function to define our store, complete with state and actions.

```typescript
import { create } from "zustand"

interface Todo {
  id: number
  text: string
  completed: boolean
}

interface TodoStore {
  todos: Todo[]
  addTodo: (text: string) => void
  toggleTodo: (id: number) => void
  removeTodo: (id: number) => void
}

export const useTodoStore = create<TodoStore>((set) => ({
  todos: [],

  addTodo: (text) => set((state) => ({
    todos: [ // Spread all, then add new todo
      ...state.todos, {
        id: Date.now(),
        text,
        completed: false
      }
    ]
  })),

  toggleTodo: (id) => set((state) => ({
    todos: state.todos.map((todo) =>
      todo.id === id ? {
        // Spread all properties, then toggle completed
        ...todo,
        completed: !todo.completed
      } : todo
    )
  })),

  removeTodo: (id) => set((state) => ({
    todos: state.todos.filter((todo) =>
      // Filter out the todo with the matching ID
      todo.id !== id
    )
  }))
}))
```

Here’s what’s happening:
1. `todos` is our list of todo items.
2. `addTodo` adds a new todo with a unique ID.
3. `toggleTodo` toggles the `completed` status of a todo by ID.
4. `removeTodo` removes a todo by ID.

---

### Step 2: Use the Store in Components

Now, let’s build components that interact with our store.

#### Displaying Todos

```typescript
import React from "react"
import { useTodoStore } from "./todoStore"
import { FC } from "react"

const TodoList: FC = () => {
  // Access the todos and actions from the store
  const todos = useTodoStore((state) => state.todos)
  const toggleTodo = useTodoStore((state) => state.toggleTodo)
  const removeTodo = useTodoStore((state) => state.removeTodo)

  return <ul>
    {todos.map((todo) =>
      <li key={todo.id}>
        <span
          onClick={() => toggleTodo(todo.id)}
          style={{
            textDecoration: todo.completed
              ? "line-through"
              : "none",
            cursor: "pointer"
          }}
        >
          {todo.text}
        </span>

        <button onClick={() => removeTodo(todo.id)}>
          Remove
        </button>
      </li>
    )}
  </ul>
}

export default TodoList
```

#### Adding Todos

```typescript
import React, { useState } from "react"
import { useTodoStore } from "./todoStore"
import { FC } from "react"

const AddTodo: FC = () => {
  const [text, setText] = useState("")
  const addTodo = useTodoStore((state) => state.addTodo)

  const handleAdd = () => {
    if (text.trim()) {
      addTodo(text)
      setText("")
    }
  }

  return <div>
    <input
      type="text"
      value={text}
      onChange={(event) => setText(event.target.value)}
      placeholder="Add a new todo"
    />

    <button onClick={handleAdd}>
      Add Todo
    </button>
  </div>
}

export default AddTodo
```

---

### Step 3: Bringing It All Together

Finally, combine the `TodoList` and `AddTodo` components into a single app:

```typescript
import React from "react"
import TodoList from "./TodoList"
import AddTodo from "./AddTodo"
import { FC } from "react"

const App: FC = () => {
  return <div>
    <h1>Todo List</h1>
    <AddTodo />
    <TodoList />
  </div>
}

export default App
```

---

## Why Use Zustand?

Zustand stands out for its simplicity and power. Here’s why it’s worth considering:
- **No Boilerplate**: Manage state and actions in one place.
- **Performance**: Avoid unnecessary renders with selectors.
- **TypeScript Support**: Strongly type your stores for better developer experience.
- **Scalable**: Works for both small projects and complex apps.

---

## Conclusion

Zustand is a fantastic choice for React developers who want simple yet powerful state management. Whether you’re working on a small project or scaling a larger application, Zustand offers the flexibility you need without the complexity.

To learn more, check out the [Zustand GitHub Repository](https://github.com/pmndrs/zustand).

---

Ready to simplify your state management? Give Zustand a try!
