Lightweight State

Zustand

Zustand é um store de estado pequeno, rápido e sem opiniões para React. Sem providers, sem boilerplate e com assinaturas baseadas em seletores que mantêm as re-renderizações ao mínimo.

intermediate13 min readUpdated 15 de set. de 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 }),
}));
Tamanho
Cerca de 1kB
Provider
Não é necessário
Criação com
create
Leitura com
Selectors
Async
Funções async simples
Middleware
persist, devtools, immer

Por que importa

Por que o Zustand parece revigorante

Pequeno e rápido

Cerca de um kilobyte com assinaturas baseadas em seletores, para que os componentes façam re-render apenas quando o valor exato que utilizam for alterado.

Quase nenhum boilerplate

Um store é uma única chamada de função. Não há providers, reducers, tipos de action ou wrappers de contexto para configurar.

Sem opiniões

Mantenha estado e actions juntos, divida-os em slices ou use middleware. O Zustand não atrapalha o seu fluxo.

O panorama completo

As três ideias por trás do Zustand

Um hook, um objeto simples de estado e actions, e assinaturas baseadas em seletores. Esse é todo o modelo.

O store

Estado

Um hook criado com create que armazena o estado e as actions que o atualizam.

Seletores

Assinaturas

Componentes leem a fatia exata de que precisam e fazem re-render apenas quando ela muda.

Actions

Atualizações

Funções simples que chamam set para mesclar o novo estado, sem a necessidade de reducers ou dispatch.

Zustand em resumo

O núcleo do Zustand

create

Construa um hook de store com estado e actions em um só lugar.

Selectors

Assine uma fatia do estado para que mudanças não relacionadas não causem re-render.

set e get

Atualize o estado com set e leia-o imperativamente com get.

Async actions

Escreva funções async diretamente no store, sem necessidade de middleware.

persist middleware

Salve e reidrate o estado no localStorage ou em outro armazenamento.

Middleware

Adicione devtools, helpers de imutabilidade e persistência compondo funções.

Uma breve historia

Biblioteca pequena, grande adoção

  1. 2019

    Lançamento do Zustand

    Um store pequeno criado pelos desenvolvedores do Jotai e do ecossistema React Spring.

    19
  2. 2020

    Crescimento na adoção

    Desenvolvedores cansados de boilerplate o adotam por sua simplicidade e modelo de seletores.

    20
  3. 2022

    Zustand 4

    Melhor suporte a TypeScript e um núcleo menor e mais flexível.

    22
  4. 2024

    Zustand 5

    Uma API mais limpa e compatibilidade aprimorada com React 18 e 19.

    24
  5. Hoje

    Um padrão moderno

    Uma escolha comum para estado global de cliente em projetos React de qualquer tamanho.

    Hoje

O guia completo

Zustand: Tudo que voce precisa saber

O que é Zustand?

Zustand é um store de estado pequeno e rápido para React. Toda a sua API cabe na cabeça: você chama create com uma função que retorna o estado e as ações, e recebe de volta um hook. Não existe provider, nem reducer, nem tipos de ação e nem dispatch. Você importa o hook e o utiliza.

O nome significa “estado” em alemão, e a biblioteca faz jus a essa simplicidade. Ela possui cerca de um kilobyte, utiliza inscrições baseadas em seletores para manter as re-renderizações no mínimo e funciona com JavaScript e TypeScript puros. Para muitos apps React, é a menor quantidade de cerimônia que você pode adicionar e ainda assim compartilhar estado globalmente.

Criando uma store

Uma store é um hook. O estado e as funções que o atualizam coexistem no mesmo 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 mescla o objeto retornado ao estado, e get lê o estado atual de forma imperativa. Não há reducers para escrever nem actions para disparar — apenas funções que atualizam o estado.

Lendo o estado com seletores

Os componentes se inscrevem exatamente na fatia (slice) de que precisam.

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

Como o componente se inscreve apenas em state.count, atualizações em partes não relacionadas da store não causam a sua renderização novamente. Essa é a principal diferença de performance em relação à Context API do React, que renderiza novamente todos os consumidores sempre que o valor é alterado.

Para selecionar múltiplos valores, utilize uma comparação rasa (shallow comparison) para que o componente seja renderizado novamente apenas quando um deles realmente mudar:

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

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

Atualizando o estado

set realiza o merge por padrão, portanto você especifica apenas o que mudou. Passe uma função quando o próximo valor depender do anterior.

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

Para objetos aninhados, set substitui a chave de nível superior, então utilize o spread do objeto existente quando precisar manter outras propriedades. O middleware Immer está disponível se você preferir a sintaxe de mutação para atualizações profundas.

Ações assíncronas

A lógica assíncrona é apenas uma função async na store. Não há middleware para 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 });
    }
  },
}));

As flags de carregamento (loading) e erro ficam logo ao lado dos dados, e os componentes selecionam apenas as partes que precisam exibir.

Slices e middleware

Para stores grandes, divida-as em slices que são combinados em uma única store. Cada slice é uma função que recebe set e 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),
}));

O middleware é composto como funções ao redor do criador da store. Os mais comuns são:

  • persist salva e reidrata o estado no localStorage ou em outro armazenamento.
  • devtools conecta a store à extensão Redux DevTools.
  • immer permite escrever atualizações mutáveis que permanecem imutáveis.
// persisted.js
import { persist } from "zustand/middleware";

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

Quando usar Zustand

O Zustand é ideal para estados globais do client: temas e preferências, carrinhos de compras, estados de UI compartilhados entre componentes distantes, rascunhos, filtros e estados de modais. Ele é especialmente útil quando o Context causaria re-renders excessivos ou quando o Redux parece ter mais estrutura do que o necessário.

Não o utilize para estados do server. Dados provenientes de uma API exigem cache, deduplicação, refetching em segundo plano e invalidação, e o TanStack Query lida com essas questões de forma muito mais eficiente. A configuração comum é utilizar Zustand para o estado do client e TanStack Query para o estado do server.

Melhores práticas

  • Sempre leia o estado utilizando um selector.
  • Use useShallow ao selecionar múltiplos valores.
  • Mantenha as actions na store para que os componentes não precisem conhecer a estrutura do estado.
  • Divida stores grandes em slices.
  • Use o persist middleware para configurações que devem persistir após o recarregamento da página.
  • Mantenha dados do servidor fora da store.
  • Armazene dados simples e serializáveis sempre que possível.

Erros comuns

  • Chamar useStore() sem um selector e causar re-renderizações a cada mudança.
  • Retornar um novo objeto de um selector sem utilizar uma comparação rasa (shallow comparison).
  • Tratar o Zustand como um cache de estado do servidor.
  • Mutar estados aninhados sem o Immer e perder atualizações.
  • Criar uma store dentro de um componente, o que a reseta a cada renderização.
  • Adicionar a “cerimônia” típica do Redux que o Zustand foi projetado para eliminar.

Próximos passos

O Zustand costuma ser o equilíbrio ideal entre Context e Redux. Compare-o com o Context para compartilhamentos simples e com o Redux Toolkit para estados rigorosos e com mais ferramentas, e mova os dados do servidor para o TanStack Query. Para Vue, esse mesmo papel é desempenhado pelo Pinia.

Assinando o estado

Selecione apenas o que o componente precisa. Selecionar o store inteiro causa re-render a cada mudança.

Preferível
const count = useStore((s) => s.count);
const increment = useStore((s) => s.increment);
Evite
const { count, other, more } = useStore();
// re-renders on any change

Definindo atualizações

Mantenha as actions no store para que os componentes não precisem saber como o estado é estruturado.

Preferível
const useStore = create((set) => ({
  count: 0,
  increment: () =>
    set((s) => ({ count: s.count + 1 })),
}));
Evite
const useStore = create(() => ({
  count: 0,
}));

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

Perguntas frequentes

Perguntas frequentes

Keep learning

Related topics from the roadmap.

$ comecar a aprender

Pronto para aprender Zustand?

Nosso tutorial interativo te guia por Zustand passo a passo — com quizzes e codigo real que voce pode executar no navegador.