Lightweight State

Zustand

Zustand es un almacén de estado pequeño, rápido y sin opiniones para React. Sin providers, sin boilerplate y con suscripciones basadas en selectores que mantienen los re-renders al mínimo.

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 }),
}));
Tamaño
Aproximadamente 1kB
Provider
No es requerido
Crear con
create
Leer con
Selectors
Async
Funciones async planas
Middleware
persist, devtools, immer

Por que importa

Por qué Zustand se siente refrescante

Pequeño y rápido

Alrededor de un kilobyte con suscripciones basadas en selectores, por lo que los componentes solo se re-renderizan cuando cambia el valor exacto que utilizan.

Casi sin boilerplate

Un store es una sola llamada a una función. No hay providers, reducers, tipos de acción ni envoltorios de contexto que configurar.

Sin opiniones

Mantén el estado y las acciones juntos, divídelos en slices o usa middleware. Zustand no se interpone en tu camino.

La imagen completa

Las tres ideas detrás de Zustand

Un hook, un objeto plano de estado y acciones, y suscripciones basadas en selectores. Ese es todo el modelo.

El store

Estado

Un hook creado con create que contiene el estado y las acciones que lo actualizan.

Selectors

Suscripciones

Los componentes leen la parte exacta que necesitan y se re-renderizan solo cuando esta cambia.

Acciones

Actualizaciones

Funciones planas que llaman a set para fusionar el nuevo estado, sin necesidad de reducers o dispatch.

Zustand de un vistazo

El núcleo de Zustand

create

Construye un hook de store con el estado y las acciones en un solo lugar.

Selectors

Suscríbete a una parte del estado para que los cambios no relacionados no provoquen re-renders.

set y get

Actualiza el estado con set y léelo imperativamente con get.

Acciones asíncronas

Escribe funciones async directamente en el store, sin necesidad de middleware.

persist middleware

Guarda y rehidrata el estado en localStorage u otro almacenamiento.

Middleware

Añade devtools, ayudantes de inmutabilidad y persistencia componiendo funciones.

Una breve historia

Librería pequeña, gran adopción

  1. 2019

    Lanzamiento de Zustand

    Un store pequeño creado por los desarrolladores de Jotai y el ecosistema de React Spring.

    19
  2. 2020

    Crecimiento en la adopción

    Desarrolladores cansados del boilerplate lo adoptan por su simplicidad y modelo de selectores.

    20
  3. 2022

    Zustand 4

    Mejor soporte para TypeScript y un núcleo más pequeño y flexible.

    22
  4. 2024

    Zustand 5

    Una API más limpia y compatibilidad mejorada con React 18 y 19.

    24
  5. Hoy

    Un estándar moderno

    Una opción común para el estado global del cliente en proyectos de React de cualquier tamaño.

    Hoy

La guia completa

Zustand: Todo lo que necesitas saber

¿Qué es Zustand?

Zustand es un almacén de estado pequeño y rápido para React. Toda su API es sumamente sencilla: llamas a create con una función que devuelve el estado y las acciones, y obtienes un hook. No hay provider, ni reducer, ni tipos de acción, ni dispatch. Simplemente importas el hook y lo usas.

Su nombre significa “estado” en alemán, y la librería hace honor a esa simplicidad. Pesa alrededor de un kilobyte, utiliza suscripciones basadas en selectores para mantener los re-renders al mínimo y funciona con JavaScript y TypeScript puro. Para muchas aplicaciones de React, es la opción con menos ceremonia posible para compartir el estado de forma global.

Creando un store

Un store es un hook. El estado y las funciones que lo actualizan conviven en el mismo lugar.

// 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 fusiona el objeto devuelto en el estado, y get lee el estado actual de forma imperativa. No hay que escribir ningún reducer ni despachar ninguna acción; simplemente son funciones que actualizan el estado.

Lectura del estado con selectores

Los componentes se suscriben exactamente a la parte del estado que necesitan.

// 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>;
}

Debido a que el componente se suscribe únicamente a state.count, las actualizaciones en partes no relacionadas del store no provocan que se vuelva a renderizar. Esa es la diferencia clave de rendimiento respecto a la Context API de React, que renderiza nuevamente a cada consumidor cuando el valor cambia.

Para seleccionar múltiples valores, utiliza una comparación superficial (shallow comparison) para que el componente se renderice solo cuando uno de ellos cambie realmente:

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

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

Actualización del estado

set realiza una fusión (merge) por defecto, por lo que solo necesitas especificar qué ha cambiado. Pasa una función cuando el siguiente valor dependa del anterior.

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

En el caso de objetos anidados, set reemplaza la clave de nivel superior, así que utiliza el operador spread con el objeto existente cuando necesites conservar otras propiedades. El middleware Immer está disponible si prefieres una sintaxis de mutación para actualizaciones profundas.

Acciones asíncronas

La lógica asíncrona es simplemente una función async en el store. No hay ningún middleware que configurar.

// 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 });
    }
  },
}));

Los flags de carga (loading) y error residen junto a los datos, y los componentes seleccionan únicamente las piezas que necesitan mostrar.

Slices y middleware

Para un store extenso, divídelo en slices que se combinen en un único store. Cada slice es una función que recibe set y get.

// 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),
}));

El middleware se compone como funciones alrededor del creador del store. Los más comunes son:

  • persist guarda y rehidrata el estado en localStorage u otro almacenamiento.
  • devtools conecta el store con la extensión Redux DevTools.
  • immer te permite escribir actualizaciones mutables que se mantienen inmutables.
// persisted.js
import { persist } from "zustand/middleware";

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

Cuándo usar Zustand

Zustand es ideal para el estado global del cliente: temas y preferencias, un carrito de compras, estado de la UI compartido entre componentes distantes, borradores, filtros y el estado de los modales. Es especialmente útil cuando Context provocaría demasiados re-renders o cuando Redux se siente como una estructura más compleja de lo que necesitas.

No lo utilices para el estado del servidor. Los datos que provienen de una API requieren almacenamiento en caché, deduplicación, refetching en segundo plano e invalidación, y TanStack Query gestiona esas necesidades mucho mejor. La configuración más común es utilizar Zustand para el estado del cliente y TanStack Query para el estado del servidor.

Mejores prácticas

  • Lee siempre el estado mediante un selector.
  • Utiliza useShallow cuando selecciones múltiples valores.
  • Mantén las acciones dentro del store para que los componentes no necesiten conocer la estructura del estado.
  • Divide los stores grandes en slices.
  • Utiliza el persist middleware para los ajustes que deban persistir tras recargar la página.
  • Mantén los datos del servidor fuera del store.
  • Almacena datos planos y serializables siempre que sea posible.

Errores comunes

  • Llamar a useStore() sin un selector y provocar un re-renderizado en cada cambio.
  • Retornar un nuevo objeto desde un selector sin realizar una comparación superficial (shallow comparison).
  • Tratar Zustand como un caché de estado del servidor.
  • Mutar el estado anidado sin Immer y perder las actualizaciones.
  • Crear un store dentro de un componente, lo que provoca que se reinicie en cada renderizado.
  • Añadir la complejidad y rituales típicos de Redux que Zustand fue diseñado para eliminar.

Próximos pasos

Zustand suele ser el punto medio ideal entre Context y Redux. Compáralo con Context para un intercambio de datos sencillo y con Redux Toolkit para un estado más estricto y robusto, y traslada los datos del servidor a TanStack Query. En Vue, el mismo rol lo cumple Pinia.

Suscripción al estado

Selecciona solo lo que el componente necesita. Seleccionar todo el store provoca re-renders en cada cambio.

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

Definición de actualizaciones

Mantén las acciones en el store para que los componentes no necesiten saber cómo está estructurado el estado.

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

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

Preguntas frecuentes

Preguntas frecuentes

Keep learning

Related topics from the roadmap.

$ comienza a aprender

Listo para aprender Zustand?

Nuestro tutorial interactivo te guia a traves de Zustand paso a paso — con quizzes y codigo real que puedes ejecutar en el navegador.