Lightweight State

Zustand

Zustand ist ein winziger, schneller und unvoreingenommener State Store für React. Keine Provider, kein Boilerplate und selector-basierte Subscriptions, die Re-Renders minimieren.

intermediate13 min readUpdated 15. Sept. 2026
store.js
js
// store.js
import { create } from "zustand";

export const useStore = create((set) => ({
  count: 0,
  increment: () =>
    set((state) => ({ count: state.count + 1 })),
  reset: () => set({ count: 0 }),
}));
Größe
ca. 1kB
Provider
Nicht erforderlich
Erstellung mit
create
Auslesen mit
Selectors
Async
Einfache async Funktionen
Middleware
persist, devtools, immer

Warum es wichtig ist

Warum Zustand erfrischend wirkt

Winzig und schnell

Nur etwa ein Kilobyte mit selector-basierten Subscriptions, sodass Komponenten nur dann neu rendern, wenn sich genau der Wert ändert, den sie verwenden.

Fast kein Boilerplate

Ein Store ist ein einziger Funktionsaufruf. Es gibt keine Provider, Reducer, Action-Typen oder Context-Wrapper, die konfiguriert werden müssen.

Unvoreingenommen

Behalte State und Actions zusammen, teile sie in Slices auf oder nutze middleware. Zustand drängt sich nicht auf.

Das Gesamtbild

Die drei Kernideen hinter Zustand

Ein Hook, ein einfaches Objekt aus State und Actions sowie selector-basierte Subscriptions. Das ist das gesamte Modell.

Der Store

State

Ein mit create erstellter Hook, der den State und die Actions zu dessen Aktualisierung enthält.

Selectors

Subscriptions

Komponenten lesen genau den Slice aus, den sie benötigen, und rendern nur bei dessen Änderung neu.

Actions

Updates

Einfache Funktionen, die set aufrufen, um den State zu mergen – ohne Reducer oder dispatch.

Zustand auf einen Blick

Der Kern von Zustand

create

Erstelle einen Store-Hook mit State und Actions an einem Ort.

Selectors

Abonniere einen Slice des States, damit nicht zusammenhängende Änderungen keinen Re-Render auslösen.

set und get

Aktualisiere den State mit set und lies ihn imperativ mit get aus.

Async actions

Schreibe async Funktionen direkt in den Store, ohne dass eine middleware erforderlich ist.

persist middleware

Speichere und rehydriere den State im localStorage oder einem anderen Speicher.

Middleware

Füge devtools, Immutability-Helper und Persistenz durch Funktionskomposition hinzu.

Eine kurze Geschichte

Kleine Library, große Verbreitung

  1. 2019

    Zustand Release

    Ein kleiner Store von den Entwicklern von Jotai und dem React Spring Ökosystem.

    19
  2. 2020

    Wachsende Verbreitung

    Entwickler, die genug von Boilerplate haben, nutzen es wegen seiner Einfachheit und dem Selector-Modell.

    20
  3. 2022

    Zustand 4

    Bessere TypeScript-Unterstützung und ein kleinerer, flexiblerer Kern.

    22
  4. 2024

    Zustand 5

    Eine sauberere API und verbesserte Kompatibilität mit React 18 und 19.

    24
  5. Heute

    Ein moderner Standard

    Eine gängige Wahl für globalen Client-State in React-Projekten jeder Größe.

    Heute

Der vollständige Leitfaden

Zustand: Alles was Sie wissen müssen

Was ist Zustand?

Zustand ist ein kleiner, schneller State Store für React. Die gesamte API ist extrem überschaubar: Du rufst create mit einer Funktion auf, die den State und die Actions zurückgibt, und erhältst im Gegenzug einen Hook. Es gibt keinen Provider, keinen Reducer, keine Action-Types und kein Dispatch. Du importierst den Hook und verwendest ihn einfach.

Der Name ist das deutsche Wort für „state“, und die Library wird dieser Einfachheit gerecht. Sie ist nur etwa ein Kilobyte groß, nutzt selector-basierte Subscriptions, um Re-Renders zu minimieren, und funktioniert sowohl mit plain JavaScript als auch mit TypeScript. Für viele React-Apps ist es der Weg mit dem geringsten Overhead, um State global zu teilen.

Einen Store erstellen

Ein Store ist ein Hook. Der State und die Funktionen, die ihn aktualisieren, existieren gemeinsam.

// store.js
import { create } from "zustand";

export const useStore = create((set, get) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),

  double: () => get().count * 2,
}));

set führt das zurückgegebene Objekt in den State zusammen, und get liest den aktuellen State imperativ aus. Es muss kein Reducer geschrieben und keine Action dispatched werden – es gibt lediglich Funktionen, die den State aktualisieren.

State mit Selectors auslesen

Komponenten abonnieren genau den Slice, den sie benötigen.

// Counter.jsx
import { useStore } from "./store";

export function Counter() {
  const count = useStore((state) => state.count);
  const increment = useStore((state) => state.increment);

  return <button onClick={increment}>{count}</button>;
}

Da die Komponente nur state.count abonniert, führen Aktualisierungen an nicht verwandten Teilen des Stores nicht zu einem Re-Render. Das ist der entscheidende Performance-Unterschied zur Context API von React, bei der jeder Consumer neu gerendert wird, sobald sich der Wert ändert.

Um mehrere Werte auszuwählen, verwende einen shallow comparison, damit die Komponente nur dann neu gerendert wird, wenn sich tatsächlich einer dieser Werte ändert:

// shallow.js
import { useShallow } from "zustand/react/shallow";

const { count, increment } = useStore(
  useShallow((state) => ({ count: state.count, increment: state.increment })),
);

State aktualisieren

set führt standardmäßig einen Merge durch, sodass Sie nur die Änderungen angeben müssen. Übergeben Sie eine Funktion, wenn der nächste Wert vom vorherigen abhängt.

// updates.js
set({ count: 0 }); // merge
set((state) => ({ count: state.count + 1 })); // derive from previous
set((state) => ({ items: [...state.items, item] })); // arrays

Bei verschachtelten Objekten ersetzt set den Key auf der obersten Ebene. Verwenden Sie daher den Spread-Operator für das bestehende Objekt, wenn Sie andere Eigenschaften beibehalten möchten. Falls Sie eine mutierende Syntax für tiefe Updates bevorzugen, steht die Immer middleware zur Verfügung.

Async Actions

Asynchrone Logik ist in diesem Fall einfach eine async-Funktion im Store. Es muss keine middleware konfiguriert werden.

// usersStore.js
import { create } from "zustand";

export const useUsers = create((set) => ({
  users: [],
  loading: false,
  error: null,

  fetchUsers: async () => {
    set({ loading: true, error: null });
    try {
      const res = await fetch("/api/users");
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      set({ users: await res.json(), loading: false });
    } catch (error) {
      set({ error: error.message, loading: false });
    }
  },
}));

Die Loading- und Error-Flags befinden sich direkt neben den Daten, und die Komponenten wählen nur die Teile aus, die sie tatsächlich anzeigen.

Slices und middleware

Teilen Sie einen großen Store in Slices auf, die anschließend zu einem einzigen Store kombiniert werden. Jeder Slice ist eine Funktion, die set und get erhält.

// slices/cart.js
export const createCartSlice = (set) => ({
  items: [],
  addItem: (item) => set((state) => ({ items: [...state.items, item] })),
  clear: () => set({ items: [] }),
});
// store.js
import { create } from "zustand";
import { createCartSlice } from "./slices/cart";
import { createUserSlice } from "./slices/user";

export const useStore = create((...args) => ({
  ...createCartSlice(...args),
  ...createUserSlice(...args),
}));

Middleware wird wie Funktionen um den Store-Creator herum komponiert. Die gebräuchlichsten sind:

  • persist speichert den State in localStorage oder einem anderen Speicher und stellt ihn wieder her (Rehydration).
  • devtools verbindet den Store mit der Redux DevTools Extension.
  • immer ermöglicht es Ihnen, mutierende Updates zu schreiben, die dennoch immutable bleiben.
// persisted.js
import { persist } from "zustand/middleware";

export const useSettings = create(
  persist(
    (set) => ({
      theme: "dark",
      setTheme: (theme) => set({ theme }),
    }),
    { name: "settings" },
  ),
);

Wann man Zustand einsetzen sollte

Zustand eignet sich hervorragend für globalen Client-State: Themes und Einstellungen, ein Warenkorb, UI-Zustände, die über weit entfernte Komponenten hinweg geteilt werden, Entwürfe, Filter und Modal-Zustände. Es ist besonders nützlich, wenn Context zu viele Re-Renders verursachen würde oder wenn Redux sich nach mehr Struktur anfühlt, als man eigentlich benötigt.

Verwenden Sie es nicht für Server-State. Daten, die von einer API kommen, benötigen Caching, Deduplizierung, Background-Refetching und Invalidation – diese Anforderungen werden von TanStack Query wesentlich besser gelöst. Das gängige Setup besteht aus Zustand für den Client-State und TanStack Query für den Server-State.

Best Practices

  • Lesen Sie den State immer mit einem Selector aus.
  • Verwenden Sie useShallow, wenn Sie mehrere Werte auswählen.
  • Definieren Sie Actions im Store, damit Komponenten die Struktur des State nicht kennen müssen.
  • Teilen Sie große Stores in Slices auf.
  • Verwenden Sie die persist middleware für Einstellungen, die Neuladevorgänge überstehen sollen.
  • Halten Sie Server-Daten aus dem Store heraus.
  • Speichern Sie nach Möglichkeit einfache, serialisierbare Daten.

Häufige Fehler

  • Aufruf von useStore() ohne Selector, was zu einem Re-Render bei jeder Änderung führt.
  • Rückgabe eines neuen Objekts aus einem Selector ohne shallow comparison.
  • Verwendung von Zustand als Cache für Server-State.
  • Mutation von verschachteltem State ohne Immer, wodurch Updates verloren gehen.
  • Erstellung eines Stores innerhalb einer Komponente, wodurch dieser bei jedem Render zurückgesetzt wird.
  • Einführung von Redux-typischem Boilerplate, das Zustand eigentlich eliminieren soll.

Wie geht es weiter?

Zustand ist oft der ideale Mittelweg zwischen Context und Redux. Vergleiche es mit Context für einfaches Teilen von Daten und Redux Toolkit für einen strikten, tool-gestützten State, und lagere Server-Daten nach TanStack Query aus. Für Vue übernimmt Pinia die gleiche Rolle.

State abonnieren

Wähle nur das aus, was die Komponente benötigt. Das Auswählen des gesamten Stores führt bei jeder Änderung zu einem Re-Render.

Bevorzugt
const count = useStore((s) => s.count);
const increment = useStore((s) => s.increment);
Vermeiden
const { count, other, more } = useStore();
// re-renders on any change

Updates definieren

Behalte Actions im Store, damit Komponenten nicht wissen müssen, wie der State strukturiert ist.

Bevorzugt
const useStore = create((set) => ({
  count: 0,
  increment: () =>
    set((s) => ({ count: s.count + 1 })),
}));
Vermeiden
const useStore = create(() => ({
  count: 0,
}));

// component does
// useStore.setState({ count: 1 })

Häufig gestellte Fragen

Häufig gestellte Fragen

Keep learning

Related topics from the roadmap.

$ Lernen Sie jetzt

Bereit, Zustand zu lernen?

Unser interaktives Tutorial führt Sie Schritt für Schritt durch Zustand — mit Quizzen und echtem Code, den Sie im Browser ausführen können.