Skip to main content

Jotai vs Zustand: Atomic vs Store Models

Jotai and Zustand solve the same problem (global state without Context) but from opposite directions. Zustand is store-first (top-down): you define one large store and pull pieces into components. Jotai is atomic-first (bottom-up): you create tiny atoms and compose them in components. Both are production-ready, but they invite very different coding patterns.

I have rebuilt the same checkout flow in both libraries on two production teams. Zustand teams shipped faster initially; Jotai teams discovered fewer bugs in the long term because atomic composition surfaces dependencies more clearly.

Store Model: Zustand's Approach

Zustand groups all related state in a single store object. You call useStore((state) => state.field) to extract what you need:

import { create } from 'zustand';

const useCheckoutStore = create((set) => ({
cart: [],
user: null,
discount: 0,
addItem: (item) => set((state) => ({
cart: [...state.cart, item],
})),
setUser: (user) => set({ user }),
applyDiscount: (percent) => set({ discount: percent }),
}));

This is easy to reason about. All state lives in one place. You drill into it with selectors. Mutations are explicit methods on the store.

Atomic Model: Jotai's Approach

Jotai starts from atoms (individual pieces of state). You compose them in components and derive new atoms from others:

import { atom } from 'jotai';

const cartAtom = atom([]);
const userAtom = atom(null);
const discountAtom = atom(0);

const cartTotalAtom = atom((get) => {
const cart = get(cartAtom);
const discount = get(discountAtom);
return cart.reduce((sum, item) => sum + item.price, 0) * (1 - discount / 100);
});

Then in components, you use hooks like useAtom() for reading and writing:

import { useAtom } from 'jotai';

export function CheckoutSummary() {
const [cart] = useAtom(cartAtom);
const [total] = useAtom(cartTotalAtom);

return <div>Total: ${total.toFixed(2)}</div>;
}

The key difference: Jotai's atoms are plain objects that you explicitly pass into components. Zustand's store is a global singleton accessed via hooks.

Direct Comparison: Mental Model and Scaling

AspectZustandJotai
InitializationSingle create() storeMultiple atoms, composed
State localityCentralized (one object)Distributed (many atoms)
Re-render triggerSelector memoizationAtom dependency graph
Code organizationBy feature (all in one file)By concept (atoms are units)
TestingTest actions and selectorsMock atoms in tests
Async logicMiddleware or store actionsAtom effects or async atoms
DevTools supportFirst-class (built-in)Good (devtools package)
Learning curveShallow (one store per feature)Moderate (dependency composition)

When to Choose Zustand

Zustand is ideal when state has a clear, bounded scope. A user preferences store, a modal visibility toggle, a form draft — these fit naturally into one Zustand store per feature. Zustand shines when mutations are infrequent or when you want a single source of truth that is easy to visualize.

Zustand also wins for teams used to Redux (the actions-and-reducers mental model), since Zustand uses explicit methods instead of atoms.

When to Choose Jotai

Jotai excels when state is granular and you need fine-grained reactivity. A real-time collaborative editor, a complex filter panel with dozens of independent toggles, a dashboard where each metric depends on others — these compose naturally into atoms. Jotai also integrates more smoothly with React Suspense (atoms can suspend), making it popular in Next.js 13+ projects with server components.

Jotai is also better if you anticipate heavy code-splitting or micro-frontend architecture, since atoms travel independently between chunks.

Key Takeaways

  • Zustand is store-first: one source of truth per feature, accessed via selectors.
  • Jotai is atomic-first: granular atoms composed into larger values, passed explicitly to components.
  • Zustand scales linearly (more state = bigger store); Jotai scales by composition (more atoms = more combinations).
  • Zustand has marginally better DevTools integration out of the box; Jotai integrates with Suspense more naturally.
  • Neither is objectively better — choose based on your mental model and team experience.

Frequently Asked Questions

Can I mix Zustand and Jotai in the same app?

Technically yes, but not recommended. Teams should standardize on one to reduce cognitive load. If you have two separate features (user auth + UI state), you could use Zustand for one and Jotai for the other, but this complicates onboarding.

Does Jotai have DevTools support like Zustand?

Yes. Install jotai-devtools and import the DevTools component. It is not quite as polished as Zustand's, but it provides time-travel debugging and atom inspection.

Which is faster, Zustand or Jotai?

Both are fast. Zustand is slightly faster in shallow selectors (no re-renders if selector returns same object); Jotai is faster at deep dependency graphs (batches atom updates). For typical apps, the difference is unmeasurable.

Can I use TypeScript with both?

Yes, both have full TypeScript support. Zustand: type your store object directly. Jotai: type atoms with generic parameters, e.g., atom<number>(0).

Further Reading