← Back to articles

Zustand vs Jotai vs Nanostores: Lightweight State Management (2026)

Redux is overkill for most apps. In 2026, lightweight state libraries handle 95% of state management needs with a fraction of the complexity. Zustand, Jotai, and Nanostores lead the pack.

Quick Comparison

FeatureZustandJotaiNanostores
Mental modelStore (like Redux-lite)Atoms (like Recoil)Atoms (framework-agnostic)
Bundle size~1.1KB~2.4KB~0.3KB
FrameworkReactReactAny (React, Vue, Svelte, vanilla)
TypeScriptExcellentExcellentGood
DevToolsRedux DevToolsCustom DevToolsNone built-in
SSRYesYes (with Provider)Yes
MiddlewareYes (persist, immer, etc.)ExtensionsPlugins
Learning curveVery lowLowVery low
CommunityLargestGrowingSmaller

Zustand: The Store-Based Approach

Zustand uses a simple store pattern. Define a store with state and actions, use it in components.

import { create } from 'zustand'

interface TaskStore {
  tasks: Task[]
  addTask: (task: Task) => void
  removeTask: (id: string) => void
}

const useTaskStore = create<TaskStore>((set) => ({
  tasks: [],
  addTask: (task) => set((state) => ({ tasks: [...state.tasks, task] })),
  removeTask: (id) => set((state) => ({
    tasks: state.tasks.filter(t => t.id !== id)
  })),
}))

// In component
function TaskList() {
  const tasks = useTaskStore((state) => state.tasks)
  const addTask = useTaskStore((state) => state.addTask)
  // ...
}

Strengths

  • Simplest API. Create a store, use it. No providers, no context, no boilerplate.
  • Selector-based re-renders. Components only re-render when the selected state changes.
  • Middleware ecosystem. persist (localStorage), immer (immutable updates), devtools (Redux DevTools).
  • Works outside React. Access store state in vanilla JS, utility functions, or API routes.
  • Largest community. Most npm downloads, most tutorials, most Stack Overflow answers.

Weaknesses

  • Store can grow unwieldy. Large stores become hard to manage (split into slices).
  • No built-in atom composition. Derived state requires manual selectors or subscribeWithSelector.
  • React-only hooks. Core API is framework-agnostic but the hooks are React-specific.

Best For

Most React applications. The default choice when you need state management beyond useState/useContext.

Jotai: The Atomic Approach

Jotai uses atoms — small, independent pieces of state that compose together. Inspired by Recoil but simpler.

import { atom, useAtom } from 'jotai'

const tasksAtom = atom<Task[]>([])
const taskCountAtom = atom((get) => get(tasksAtom).length)
const completedTasksAtom = atom((get) =>
  get(tasksAtom).filter(t => t.completed)
)

// In component
function TaskList() {
  const [tasks, setTasks] = useAtom(tasksAtom)
  const [count] = useAtom(taskCountAtom) // auto-derived
  // ...
}

Strengths

  • Bottom-up composition. Build complex state from small, reusable atoms. Derived atoms auto-update.
  • Minimal re-renders. Components subscribe to specific atoms — changing one atom doesn't re-render components using other atoms.
  • Async atoms. Atoms can be async (data fetching) without additional libraries.
  • Integrations. Official integrations with React Query, tRPC, Immer, and more.
  • Code splitting friendly. Atoms are defined where they're used, not in a central store.

Weaknesses

  • Provider needed for SSR. Server-side rendering requires wrapping your app in a Provider.
  • Debugging harder. Atom-based state is more distributed — harder to see the full picture without DevTools.
  • Conceptual overhead. The atom → derived atom → write atom pattern has a learning curve.
  • Less intuitive for store-like patterns. If you think in "stores," Jotai's atom model may feel unnatural initially.

Best For

Applications with lots of independent state that composes together. Great for complex UIs with many interrelated but independently updating pieces.

Nanostores: The Framework-Agnostic Atoms

Nanostores provides tiny state atoms that work with any framework — React, Vue, Svelte, or vanilla JS.

import { atom, computed } from 'nanostores'

const $tasks = atom<Task[]>([])
const $taskCount = computed($tasks, tasks => tasks.length)
const $completedTasks = computed($tasks, tasks =>
  tasks.filter(t => t.completed)
)

// React
import { useStore } from '@nanostores/react'

function TaskList() {
  const tasks = useStore($tasks)
  const count = useStore($taskCount)
  // ...
}

// Vue (same atoms!)
import { useStore } from '@nanostores/vue'
// ... identical usage

Strengths

  • Tiny. 0.3KB — the smallest state library. Your entire state management adds less than a single icon to your bundle.
  • Framework-agnostic. Same atoms work in React, Vue, Svelte, Solid, and vanilla JS. Share state across micro-frontends or framework migrations.
  • Simple. atom, map, computed — three concepts cover everything.
  • Perfect for Astro. Astro uses multiple frameworks on one page. Nanostores is the recommended state solution.
  • Lifecycle built-in. onMount handles initialization (fetch data on first subscriber).

Weaknesses

  • Smaller ecosystem. Fewer middleware, fewer tutorials, smaller community.
  • No DevTools. No built-in debugging tools (though state is simple enough to console.log).
  • Less middleware. No built-in persist, no immer integration, fewer extensions.
  • Less React-optimized. Zustand and Jotai have more React-specific optimizations.

Best For

Astro projects (multi-framework islands), library authors (no framework dependency), and teams planning framework migrations.

Performance Comparison

MetricZustandJotaiNanostores
Bundle size (min+gzip)1.1KB2.4KB0.3KB
Re-render efficiencySelector-basedAtom-basedSubscription-based
1000-atom updateFastFastestFast
Memory overheadLowLowLowest

All three are fast enough for any practical application. The performance differences are negligible unless you have thousands of atoms updating simultaneously.

When to Use Each

Zustand When:

  • You want the simplest possible state management
  • You're coming from Redux and want something familiar but lighter
  • You need middleware (persist, devtools)
  • Your state is naturally store-shaped (user, settings, cart)

Jotai When:

  • You have lots of independent, composable state
  • You want derived state that auto-updates
  • You need async atoms (built-in data fetching)
  • You like the Recoil mental model but want something simpler

Nanostores When:

  • You're using Astro or multiple frameworks
  • Bundle size is critical
  • You want framework-agnostic state
  • You're building a library or design system

FAQ

Can I use Zustand and Jotai together?

Yes, and some teams do — Zustand for "global" stores (user, settings) and Jotai for "local" composable state (UI state, form state). But pick one if you can.

What about Redux Toolkit?

Redux Toolkit is fine for large teams with established Redux patterns. But for new projects, Zustand provides a similar mental model with 90% less boilerplate.

What about Valtio?

Valtio uses mutable proxies (like MobX) — const state = proxy({ count: 0 }) then mutate directly. Great DX but the proxy model can be surprising. Consider it if you prefer mutable state patterns.

Do I even need a state library?

For most apps, useState + useContext + React Query covers 90% of state needs. Add a state library when context re-rendering becomes a problem or when you need state outside of React.

The Verdict

  • Zustand for most React apps. Simplest API, largest community, battle-tested. The default choice.
  • Jotai for complex, composable state. Best when you have many independent pieces of state that derive from each other.
  • Nanostores for multi-framework projects and when bundle size matters. The right choice for Astro.

If unsure, start with Zustand. You'll know if you need Jotai's atom composition when you start writing complex selectors.

Get AI tool guides in your inbox

Weekly deep-dives on the best AI coding tools, automation platforms, and productivity software.