Star on GitHub
🐻

JUST FUCKING USE ZUSTAND

A small, fast, and scalable bearbones state-management solution

SO, WHAT THE FUCK IS ZUSTAND?

Listen up, you beautiful disaster. You know how you're drowning in Redux boilerplate, wrestling with Context API's re-render hell, or watching your MobX decorators turn into a fucking archaeology expedition? Yeah, I thought so.

Zustand is here to save your sorry ass. It's a tiny (1kb) state management library that doesn't give a fuck about your opinions, doesn't need providers wrapping your entire app like a goddamn burrito, and won't make you write 50 lines of code just to increment a fucking counter.

🐻 Bear necessities for state management in React.

That's it. That's the whole fucking pitch. No bullshit, no ceremony, just pure state management goodness that actually makes sense.

WHY THE FUCK SHOULD YOU USE ZUSTAND?

BECAUSE REDUX IS KILLING YOUR SOUL, THAT'S WHY

Redux made you write:

Zustand? Create a store. Done. That's it. No ceremony, no bullshit, no existential crisis at 3 AM wondering why you need three files to manage a boolean.

// Redux: 50+ lines of bullshit for a counter
// Zustand: This beautiful bastard right here

import { create } from 'zustand'

const useStore = create((set) => ({
  bears: 0,
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
}))

// Use it anywhere, no fucking providers needed
function BearCounter() {
  const bears = useStore((state) => state.bears)
  return <h1>{bears} around here...</h1>
}

NO MORE CONTEXT HELL, YOU MAGNIFICENT FOOL

Context API seemed like a good idea until you realized:

Zustand gives you granular subscriptions. Components only re-render when their specific slice of state changes. Not the whole fucking store. Not everything under the provider. Just what you asked for.

IT'S FUCKING TINY (AND FAST AS HELL)

1 kilobyte. ONE. That's smaller than that image you downloaded of "definitely not a crypto scam." Meanwhile, Redux is sitting at 6kb+ (with Redux Toolkit even more), and MobX is having an identity crisis at 16kb.

Zustand: 1kb of pure fucking magic
Redux: 6kb+ of ceremony and regret
MobX: 16kb of decorators you don't understand
Context API: "Free" but costs you in therapy bills

NO PROVIDERS, NO FUCKING AROUND

Remember wrapping your entire app in providers like you're gift-wrapping presents for people you don't like?

// The Redux nightmare
<Provider store={store}>
  <ThemeProvider>
    <UserProvider>
      <LocalizationProvider>
        <FeatureFlagProvider>
          <App /> {/* Finally! */}
        </FeatureFlagProvider>
      </LocalizationProvider>
    </UserProvider>
  </ThemeProvider>
</Provider>

// Zustand
<App /> {/* That's it, you're done */}

Zustand stores are just hooks. Import them, use them, get on with your fucking life.

ASYNC? WHO GIVES A SHIT, JUST DO IT

Redux made you install middleware and write sagas that look like fucking ancient scrolls. Zustand? Just write async functions like a normal human being:

const useStore = create((set) => ({
  fishies: {},
  fetch: async (pond) => {
    const response = await fetch(pond)
    set({ fishies: await response.json() })
  },
}))

No thunks. No sagas. No Redux-Observable. No sacrificing goats to the async gods. Just fucking async/await like it's 2025 (because it is).

TYPESCRIPT? OF FUCKING COURSE

Full TypeScript support without the boilerplate ceremony that makes you want to go back to JavaScript like it's 2015:

interface BearState {
  bears: number
  increase: (by: number) => void
}

const useBearStore = create<BearState>()((set) => ({
  bears: 0,
  increase: (by) => set((state) => ({ bears: state.bears + by })),
}))

DEVTOOLS? FUCK YEAH

Redux DevTools support out of the box. Because sometimes you need to time-travel debug your mistakes (we've all been there):

import { devtools } from 'zustand/middleware'

const useStore = create(devtools((set) => ({
  // Your store
})))

PERSIST? YOU BET YOUR ASS

Want to save state to localStorage? sessionStorage? IndexedDB? Fucking smoke signals? There's middleware for that:

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

const useStore = create(
  persist(
    (set) => ({
      bears: 0,
      addBear: () => set((state) => ({ bears: state.bears + 1 })),
    }),
    {
      name: 'bear-storage', // unique name
    },
  ),
)

LOCALSTORAGE WITHOUT ZUSTAND? ENJOY YOUR FUCKING NIGHTMARE

Let's talk about what happens when you try to manage localStorage yourself like some kind of masochist:

// The localStorage hell you're currently living in:
const [user, setUser] = useState(() => {
  const saved = localStorage.getItem('user')
  return saved ? JSON.parse(saved) : null
})

useEffect(() => {
  localStorage.setItem('user', JSON.stringify(user))
}, [user])

// Oh wait, you have 15 more pieces of state? 
// Copy-paste this shit 15 times, you fucking genius.
// Don't forget to handle:
// - Parsing errors (because users WILL fuck with localStorage)
// - Serialization of complex objects
// - Hydration mismatches
// - Race conditions
// - Storage quota exceeded errors
// - Multiple tabs syncing
// And the mental breakdown that comes with all of this

Now watch Zustand handle ALL of this like a fucking boss:

// Zustand: One line and you're done
import { create } from 'zustand'
import { persist } from 'zustand/middleware'

const useStore = create(
  persist(
    (set) => ({
      user: null,
      todos: [],
      preferences: {},
      // Add 100 more fields, I don't give a fuck
      updateUser: (user) => set({ user }),
    }),
    {
      name: 'app-storage', // Everything auto-syncs
      // It even handles tabs, serialization, errors...
      // You know, like a PROFESSIONAL library should
    },
  ),
)

LocalStorage sync across tabs? ✅ Built-in.

Serialization errors? ✅ Handled gracefully.

Partial hydration? ✅ Supported.

Custom storage engines? ✅ IndexedDB, sessionStorage, whatever.

Your sanity? ✅ Actually preserved for once.

LEARNING CURVE? WHAT FUCKING CURVE?

Redux: Read 10 articles, watch 5 hours of tutorials, question your sanity, still fuck it up.

Zustand: Read the docs for 10 minutes, start coding, succeed immediately, wonder why you wasted years of your life on Redux.

WHEN SHOULD YOU JUST FUCKING USE IT?

BUT WHAT IF MY APP IS SIMPLE?

Then Zustand is STILL perfect because it's so simple it won't hurt you. It's 1kb. Your favicon is probably bigger. Just use it and stop overthinking, you beautiful overthinker.

WHAT IF MY APP IS COMPLEX?

Then you DEFINITELY need Zustand because Redux will make you want to die and Context will make everything slower than your grandma on dial-up. Zustand scales beautifully without the pain.

HOW THE FUCK DO YOU USE IT?

STEP 1: INSTALL IT (DUH)

npm install zustand
# or if you're a yarn person (no judgment)
yarn add zustand
# or if you're living in the future with pnpm
pnpm add zustand
# or if you're a speed demon with bun
bun add zustand

Bun users: You beautiful speed freaks. Zustand works perfectly with Bun's blazing-fast runtime. Install it, use it, marvel at how fast everything is, and feel superior to npm peasants. You've earned it.

STEP 2: CREATE A STORE

import { create } from 'zustand'

const useTodoStore = create((set) => ({
  todos: [],
  addTodo: (text) => set((state) => ({ 
    todos: [...state.todos, { id: Date.now(), text, done: false }] 
  })),
  toggleTodo: (id) => set((state) => ({
    todos: state.todos.map(todo =>
      todo.id === id ? { ...todo, done: !todo.done } : todo
    )
  })),
  deleteTodo: (id) => set((state) => ({
    todos: state.todos.filter(todo => todo.id !== id)
  })),
}))

STEP 3: USE IT IN YOUR COMPONENTS

function TodoList() {
  // Select only what you need
  const todos = useTodoStore((state) => state.todos)
  const toggleTodo = useTodoStore((state) => state.toggleTodo)
  const deleteTodo = useTodoStore((state) => state.deleteTodo)

  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>
          <input 
            type="checkbox" 
            checked={todo.done}
            onChange={() => toggleTodo(todo.id)}
          />
          {todo.text}
          <button onClick={() => deleteTodo(todo.id)}>Delete</button>
        </li>
      ))}
    </ul>
  )
}

function AddTodo() {
  const addTodo = useTodoStore((state) => state.addTodo)
  const [text, setText] = useState('')

  return (
    <form onSubmit={(e) => {
      e.preventDefault()
      addTodo(text)
      setText('')
    }}>
      <input 
        value={text} 
        onChange={(e) => setText(e.target.value)} 
      />
      <button>Add</button>
    </form>
  )
}

STEP 4: ENJOY YOUR LIFE

That's it. You're done. No more steps. Go build something cool instead of configuring state management for 3 hours.

PRO TIPS FOR THE OVERACHIEVERS

  • Use useShallow from zustand/react/shallow when selecting multiple values to prevent unnecessary re-renders
  • Middleware stack: devtools + persist + immer = developer happiness
  • Split large stores into slices and combine them (see the docs, they're actually good)
  • Use subscribeWithSelector for advanced subscription patterns
  • Read state outside React with useStore.getState() and useStore.setState()

THE REAL FUCKING PROBLEM ISN'T THE TOOLS. IT'S YOU.

Let's be real for a second (just this once).

Redux isn't bad because it's Redux. It's bad because some architect in 2016 decided every fucking app needs enterprise-level state management for a goddamn landing page.

Context isn't bad because it's Context. It's bad because you're using it wrong and re-rendering your entire app tree every time someone clicks a button.

The tool isn't the problem. Using the wrong tool, or using the right tool like a fucking idiot, is the problem.

Zustand is the right tool for 90% of React apps. It's simple, powerful, and doesn't require a PhD in functional programming to understand.

BUT CHOOSE WISELY, YOUNG PADAWAN

  • Building a simple static site? You don't need ANY state management, you delusional architect.
  • Need to pass 1-2 values down? Props and context are fine. Stop overengineering.
  • Building an actual app with real state? JUST FUCKING USE ZUSTAND.
  • Building the next Facebook? Sure, maybe consider Redux or something. But you're not, so don't.

Stop cargo-culting tools. Stop using Redux because "that's what we always used." Stop avoiding state management libraries because you read some article about prop drilling being "good enough."

Use Zustand. It's 2025. State management doesn't need to hurt.

STILL NOT CONVINCED? HERE'S THE FUCKING DOCUMENTATION

I can't hold your hand forever, you beautiful disaster. Here's where to learn more:

The docs are actually good. Like, shockingly good. They won't make you want to punch your monitor. Revolutionary, I know.