February 25, 2026
React State Management in 2026: A Data-Driven Comparison
Zustand, Jotai, Nanostores, Redux Toolkit, Valtio, XState, and more. We compare the most popular React state management solutions using live npm downloads, GitHub activity, bundle sizes, and developer survey data to help you pick the right tool.
Sascha Becker
Author22 min read
The state management landscape for React has shifted dramatically over the past two years. Redux is no longer the default. Recoil is archived. Signals are still stuck in committee. And a new generation of tiny, focused libraries has taken over.
We currently use Zustand and are evaluating alternatives, including Nanostores, native React solutions, and other emerging packages. Instead of relying on vibes, we pulled live data from npm, GitHub, and developer surveys to make an informed decision.
The Numbers: npm Weekly Downloads
All numbers pulled from the npm registry API on February 25, 2026.
npm Weekly Downloads (Feb 25, 2026)
Market share by weekly downloads. Zustand and Redux together account for over 60% of all state management downloads.
- Zustand
- Redux
- RTK
- TanStack
- XState
- Jotai
- MobX
- Nanostores
- Valtio
- Others
Context matters
Raw download numbers include CI pipelines, bots, and transitive dependencies. Redux's high count partly reflects legacy projects and RTK pulling it in. Zustand surpassing Redux in raw downloads is the real story of 2025-2026.
Key takeaway: Zustand has overtaken Redux as the most-downloaded dedicated state management library. The TanStack ecosystem is a surprising third force at 7.2M combined downloads. Among the modern lightweight alternatives, Jotai leads at 2.9M, with Nanostores growing fast but still an order of magnitude smaller.
12-Month Trend: The Full Picture
Weekly snapshots only tell part of the story. The chart below shows monthly aggregated downloads from the npm API over the last 12 months. The growth trajectories reveal where the momentum is.
npm Monthly Downloads (Mar 2025 – Feb 2026)
Data from the npm registry API. Feb 2026 is partial (1st–25th).
- Zustand
- Redux Toolkit
- XState
- MobX
- Jotai
- Valtio
- Nanostores
Zustand's lead is accelerating, not plateauing. It nearly tripled from 26.7M to 72.9M monthly downloads. Redux Toolkit grew too, but at a slower pace, doubling from 18.3M to 37.1M.
The mid-tier libraries (XState, MobX, Jotai) are clustered between 8-12M monthly downloads, all showing steady but unspectacular growth of 30-70%.
But zoom into the smaller libraries and the real surprise emerges:
The Challengers: Jotai vs Nanostores vs Valtio
Nanostores grew 6.6x in 12 months, the fastest growth of any library in this comparison.
- Jotai
- Nanostores
- Valtio
Nanostores is the breakout story. It went from 737K to 4.9M monthly downloads, a 6.6x increase in 12 months. That's faster proportional growth than any other library in this comparison. Much of this is driven by the Astro ecosystem adopting it as the default state solution, but the trajectory is undeniable.
Jotai crossed the line from "promising" to "established" during this period, stabilizing around 9-10M monthly. Valtio remains steady but hasn't found the same growth gear.
GitHub Health Check
A library's GitHub activity tells you whether it will still exist next year. Data pulled from the GitHub API on February 25, 2026.
| Library | Stars | Open Issues | Archived? |
|---|---|---|---|
| Zustand | 57.1K | 4 | No |
| XState | 29.3K | 165 | No |
| MobX | 28.2K | 86 | No |
| Jotai | 21.0K | 3 | No |
| Recoil | 19.5K | 322 | Yes |
| Redux Toolkit | 11.2K | 260 | No |
| Valtio | 10.1K | 2 | No |
| Nanostores | 7.1K | 26 | No |
| Effector | 4.8K | 151 | No |
| Preact Signals | 4.4K | 45 | No |
| Legend State | 4.1K | 210 | No |
| TanStack Store | 790 | 15 | No |
Recoil is dead
Recoil was archived by Meta in early 2025. Its 322 open issues will never be resolved. If you're still using it, migrate now. Jotai is the closest spiritual successor.
What stands out:
- Zustand and Jotai have remarkably few open issues (4 and 3) despite massive adoption, a sign of well-maintained, stable codebases.
- Valtio also shows excellent maintenance with only 2 open issues.
- Legend State has 210 open issues for only 4K stars, which is concerning.
- TanStack Store has low stars because it's new and mostly used internally by TanStack Router/Query, but its download numbers tell a different story.
Last Published to npm
How recently a library shipped tells you if it's actively developed or in maintenance mode.
| Library | Version | Last Published |
|---|---|---|
| @xstate/store | 3.16.0 | Feb 25, 2026 |
| Jotai | 2.18.0 | Feb 19, 2026 |
| @legendapp/state | 2.1.15 | Feb 19, 2026 |
| @tanstack/react-store | 0.9.1 | Feb 17, 2026 |
| @preact/signals-react | 3.9.0 | Feb 13, 2026 |
| XState | 5.28.0 | Feb 12, 2026 |
| Zustand | 5.0.11 | Feb 1, 2026 |
| Effector | 23.4.4 | Jan 13, 2026 |
| Valtio | 2.3.0 | Jan 1, 2026 |
| Redux Toolkit | 2.11.2 | Dec 14, 2025 |
| Nanostores | 1.1.0 | Nov 19, 2025 |
| @nanostores/react | 1.0.0 | Nov 16, 2025 |
| MobX | 6.15.0 | Sep 26, 2025 |
| Recoil | 0.7.7 | Feb 12, 2024 |
Every library except Recoil and MobX has been published within the last 3 months. Nanostores reaching 1.0.0 and 1.1.0 suggests it has stabilized its API.
Bundle Size
Bundle size matters, especially for client-side state that ships to every user. Approximate sizes based on Bundlephobia data and package analysis:
Bundle Size Comparison (min + gzip)
Kilobytes shipped to the client. Nanostores at 0.3 KB is 40x smaller than Zustand.
Developer Sentiment
The State of React 2025 survey (published February 2026) gave us hard usage numbers across three years:
- Zustand crossed the 50% usage mark (28% → 41% → 50% from 2023 to 2025), nearly doubling in two years.
- Redux (plain) is still the most widely used at 75.5%, but it's declining from 80.5% in 2023. Redux Toolkit has flatlined at ~54%.
- Jotai grew steadily from 13% to 19% — still niche, but accelerating.
- 34% of respondents don't use any state management library at all —
useStateanduseContextare enough. - The top pain points across all libraries: excessive complexity (20%) and boilerplate (15%) — both areas where Zustand and Jotai excel.
- The trend is unmistakable: developers are moving toward simpler, less opinionated tools.
Real-World Comparison: Same Feature, Five Ways
Numbers are great, but what does each library actually feel like in a real codebase? Let's find out.
The scenario: A notifications panel. A <NotificationBell> shows the unread count, a <NotificationPanel> shows the list with filtering, and any component can mark items as read. Server data comes from TanStack Query with generated hooks (via OpenAPI codegen). Each library wraps everything — server data, client UI state, derived values, and mutations — behind a single useNotifications() hook. The component just consumes it.
All examples are TypeScript. Server data comes from TanStack Query with codegen-generated options (the modern pattern — no wrapper hooks, just query option factories spread into useQuery/useMutation):
ts// Generated by your OpenAPI/GraphQL codegen (e.g. @hey-api/openapi-ts, orval, graphql-codegen)const { data: notifications = [] } = useQuery({ ...getNotificationsOptions() });const { mutate: markAsRead } = useMutation({ ...markNotificationAsReadOptions() });
React Context (baseline)
useNotifications.tsxtype Filter = "all" | "unread";const NotificationCtx = createContext<ReturnType<typeof useNotificationsInner> | null>(null);function useNotificationsInner() {const { data: notifications = [] } = useQuery({ ...getNotificationsOptions() });const { mutate: markAsRead } = useMutation({ ...markNotificationAsReadOptions() });const [isOpen, setIsOpen] = useState(false);const [filter, setFilter] = useState<Filter>("all");const filtered = filter === "unread" ? notifications.filter((n) => !n.read) : notifications;const unreadCount = notifications.filter((n) => !n.read).length;return {notifications: filtered,unreadCount,isOpen,toggle: () => setIsOpen((o) => !o),filter,setFilter,markAsRead,};}export function NotificationProvider({ children }: { children: React.ReactNode }) {const value = useNotificationsInner();return <NotificationCtx value={value}>{children}</NotificationCtx>;}export function useNotifications() {const ctx = useContext(NotificationCtx);if (!ctx) throw new Error("Missing NotificationProvider");return ctx;}
NotificationBell.tsxfunction NotificationBell() {const { unreadCount, toggle } = useNotifications();return (<button onClick={toggle}>Notifications {unreadCount > 0 && `(${unreadCount})`}</button>);}
The component is clean — but the cost is ~30 lines of Provider boilerplate. The Provider must wrap your tree, and every consumer re-renders when anything changes: toggling isOpen re-renders the bell, the filter buttons, the list, everything. You'd need to split into multiple contexts to fix that, which multiplies the boilerplate.
Zustand
useNotifications.tstype Filter = "all" | "unread";const useNotificationUI = create<{isOpen: boolean;filter: Filter;toggle: () => void;setFilter: (filter: Filter) => void;}>()((set) => ({isOpen: false,filter: "all",toggle: () => set((s) => ({ isOpen: !s.isOpen })),setFilter: (filter) => set({ filter }),}));export function useNotifications() {const { data: notifications = [] } = useQuery({ ...getNotificationsOptions() });const { mutate: markAsRead } = useMutation({ ...markNotificationAsReadOptions() });const { isOpen, filter, toggle, setFilter } = useNotificationUI();const filtered = filter === "unread" ? notifications.filter((n) => !n.read) : notifications;const unreadCount = notifications.filter((n) => !n.read).length;return { notifications: filtered, unreadCount, isOpen, toggle, filter, setFilter, markAsRead };}
NotificationBell.tsxfunction NotificationBell() {const { unreadCount, toggle } = useNotifications();return (<button onClick={toggle}>Notifications {unreadCount > 0 && `(${unreadCount})`}</button>);}
No Provider. The store holds the UI state, the hook composes it with TanStack Query and exposes a single clean interface. Types are fully inferred. For more granular re-renders, components can also select directly from the store: useNotificationUI((s) => s.isOpen).
Jotai
useNotifications.tsconst isOpenAtom = atom(false);const filterAtom = atom<"all" | "unread">("all");export function useNotifications() {const { data: notifications = [] } = useQuery({ ...getNotificationsOptions() });const { mutate: markAsRead } = useMutation({ ...markNotificationAsReadOptions() });const [isOpen, setIsOpen] = useAtom(isOpenAtom);const [filter, setFilter] = useAtom(filterAtom);const filtered = filter === "unread" ? notifications.filter((n) => !n.read) : notifications;const unreadCount = notifications.filter((n) => !n.read).length;return {notifications: filtered,unreadCount,isOpen,toggle: () => setIsOpen((o) => !o),filter,setFilter,markAsRead,};}
NotificationBell.tsxfunction NotificationBell() {const { unreadCount, toggle } = useNotifications();return (<button onClick={toggle}>Notifications {unreadCount > 0 && `(${unreadCount})`}</button>);}
Atoms are the thinnest store definition possible — two lines. The composing hook looks nearly identical to the Zustand version. Where Jotai shines is derived state: if you needed a computed atom that depends on both filterAtom and query data, atom((get) => ...) handles that elegantly. The trade-off: atoms are loose pieces, not a single coherent store, which needs discipline in larger apps.
Nanostores
notification-store.tsimport { atom } from "nanostores";export const $isOpen = atom(false); // $ prefix is a Nanostores convention, not requiredexport const $filter = atom<"all" | "unread">("all");export const toggle = () => $isOpen.set(!$isOpen.get());export const setFilter = (f: "all" | "unread") => $filter.set(f);
useNotifications.tsimport { useStore } from "@nanostores/react";export function useNotifications() {const { data: notifications = [] } = useQuery({ ...getNotificationsOptions() });const { mutate: markAsRead } = useMutation({ ...markNotificationAsReadOptions() });const isOpen = useStore($isOpen);const filter = useStore($filter);const filtered = filter === "unread" ? notifications.filter((n) => !n.read) : notifications;const unreadCount = notifications.filter((n) => !n.read).length;return { notifications: filtered, unreadCount, isOpen, toggle, filter, setFilter, markAsRead };}
NotificationBell.tsxfunction NotificationBell() {const { unreadCount, toggle } = useNotifications();return (<button onClick={toggle}>Notifications {unreadCount > 0 && `(${unreadCount})`}</button>);}
State and actions live in a plain module — no hooks, no React API until the composing hook calls useStore. This is why Nanostores works identically across React, Vue, Svelte, and Astro islands. The $ prefix convention is borrowed from Svelte stores.
Valtio
notification-store.tsimport { proxy } from "valtio";export const notificationUI = proxy({isOpen: false,filter: "all" as "all" | "unread",});export const toggle = () => {notificationUI.isOpen = !notificationUI.isOpen;};export const setFilter = (f: "all" | "unread") => {notificationUI.filter = f;};
useNotifications.tsimport { useSnapshot } from "valtio";export function useNotifications() {const { data: notifications = [] } = useQuery({ ...getNotificationsOptions() });const { mutate: markAsRead } = useMutation({ ...markNotificationAsReadOptions() });const { isOpen, filter } = useSnapshot(notificationUI);const filtered = filter === "unread" ? notifications.filter((n) => !n.read) : notifications;const unreadCount = notifications.filter((n) => !n.read).length;return { notifications: filtered, unreadCount, isOpen, toggle, filter, setFilter, markAsRead };}
NotificationBell.tsxfunction NotificationBell() {const { unreadCount, toggle } = useNotifications();return (<button onClick={toggle}>Notifications {unreadCount > 0 && `(${unreadCount})`}</button>);}
The store uses plain mutations — notificationUI.isOpen = true — while useSnapshot gives you an immutable view for rendering. Valtio tracks which properties each component accesses and only re-renders when those change. The trade-off: proxy behavior can surprise (equality checks, frozen objects, prototype chains).
Head-to-head
Every component looks identical — const { unreadCount, toggle } = useNotifications(). The difference is entirely in how the hook is built.
| Context | Zustand | Jotai | Nanostores | Valtio | |
|---|---|---|---|---|---|
| Store definition | N/A (inline) | ~10 lines | ~2 lines | ~6 lines | ~10 lines |
| Composing hook | Built into Provider | ~10 lines | ~12 lines | ~10 lines | ~10 lines |
| Provider needed | Yes | No | No* | No | No |
| Type inference | Manual | Automatic | Automatic | Explicit on atoms | as assertion |
| Re-renders | All consumers | Selected slices | Per-atom | Per-atom | Accessed properties |
| Works outside React | No | Yes | No | Yes | Yes |
*Jotai needs a Provider only for SSR or test isolation
The conclusion: every modern library lets you build the same clean useNotifications() hook without a Provider. TanStack Query handles server data, the state library handles UI state, and the composing hook merges them into one interface. The choice comes down to how you prefer to define state — one store (Zustand), independent atoms (Jotai/Nanostores), or mutable objects (Valtio).
The Contenders: Deep Dive
Zustand — The New Default
Zustand has become the "just use this" recommendation in the React community, and the numbers back it up.
tsximport { create } from "zustand";const useStore = create((set) => ({count: 0,increment: () => set((state) => ({ count: state.count + 1 })),}));function Counter() {const count = useStore((state) => state.count);const increment = useStore((state) => state.increment);return <button onClick={increment}>{count}</button>;}
Strengths:
- Minimal API, almost no boilerplate
- No Provider wrapper needed
- Works outside React (vanilla JS, middleware)
- Excellent TypeScript support
- Selector-based re-render optimization
- Middleware ecosystem (persist, devtools, immer)
- Active development by the pmndrs collective (Dai Shi)
Weaknesses:
- Selector patterns can get verbose for complex state
- No built-in computed/derived state (need middleware)
- Single store pattern can lead to large store files
Best for: Most React applications. The safe, boring, correct choice.
Jotai — Atomic Precision
Jotai takes a bottom-up, atomic approach inspired by Recoil but without the baggage. Each piece of state is an independent atom.
tsximport { atom, useAtom } from "jotai";const countAtom = atom(0);const doubledAtom = atom((get) => get(countAtom) * 2);function Counter() {const [count, setCount] = useAtom(countAtom);return <button onClick={() => setCount((c) => c + 1)}>{count}</button>;}function Display() {const [doubled] = useAtom(doubledAtom);return <span>{doubled}</span>;}
Strengths:
- Derived/computed state is a first-class concept
- Components only re-render when their specific atoms change
- Excellent for complex interdependent state
- Tiny bundle, great TypeScript support
- Atom-level code splitting
- Integration with async data (suspense-compatible atoms)
Weaknesses:
- Mental model shift from centralized stores
- Atom proliferation in large apps needs discipline
- Debugging can be harder (state is distributed)
Best for: Applications with complex, interdependent state. Form builders, dashboards with lots of computed values.
Nanostores — Radical Minimalism
Nanostores is the smallest state management library by a wide margin. Created by Andrey Sitnik (the author of PostCSS and Autoprefixer), it takes a framework-agnostic, atomic approach.
tsximport { atom, computed } from "nanostores";import { useStore } from "@nanostores/react";const $count = atom(0);const $doubled = computed($count, (count) => count * 2);function Counter() {const count = useStore($count);return <button onClick={() => $count.set(count + 1)}>{count}</button>;}
Strengths:
- 286 bytes. That's not a typo.
- Framework-agnostic (React, Vue, Svelte, Solid, Angular, Lit)
- Zero dependencies
- Simple, predictable API
- Great for multi-framework projects (Astro)
- Stable 1.0 API
Weaknesses:
- Smaller community and ecosystem
- Fewer middleware/plugins than Zustand
- Less React-specific ergonomics (imperative
.set()vs hooks) - Documentation is thinner than major competitors
- @nanostores/react adapter adds overhead to the base size
Best for: Bundle-critical applications, multi-framework projects, Astro sites, teams who value radical simplicity.
Valtio — Proxy Magic
Valtio uses JavaScript Proxies to make state mutations feel natural while keeping React happy with immutable snapshots under the hood.
tsximport { proxy, useSnapshot } from "valtio";const state = proxy({ count: 0 });function Counter() {const snap = useSnapshot(state);return <button onClick={() => state.count++}>{snap.count}</button>;}
Strengths:
- Most intuitive API (just mutate objects)
- Automatic tracking of accessed properties
- Works well with nested state
- Low boilerplate
Weaknesses:
- Proxy behavior can surprise (equality, frozen objects)
- Harder to debug (mutations happen outside React's model)
- Smaller ecosystem than Zustand
Best for: Teams coming from MobX or vanilla JS who find immutable patterns annoying.
Redux Toolkit — The Enterprise Standard
RTK modernized Redux with opinionated defaults, but it's still Redux under the hood.
Strengths:
- Battle-tested at scale (Meta, Amazon, etc.)
- RTK Query for data fetching
- Time-travel debugging with Redux DevTools
- Massive ecosystem and hiring pool
- Structured patterns benefit large teams
Weaknesses:
- Still heavy (~12 KB gzipped)
- Boilerplate has been reduced but not eliminated
- Slices, selectors, dispatching: steeper learning curve
- High negative sentiment across State of React surveys
Best for: Large enterprise teams with existing Redux infrastructure. New projects should think twice.
XState & @xstate/store — State Machines
XState brings formal state machines to JavaScript. The new @xstate/store extracts the simple event-driven pattern without the full state machine overhead.
Best for: Complex workflows (wizards, payment flows, multi-step processes). The full XState is overkill for simple UI state.
TanStack Store — The Quiet Giant
TanStack Store is a surprising entry on the charts. At 7.2M weekly downloads it looks like a major player, but context matters: the vast majority of those downloads come from being an internal dependency of TanStack Router, TanStack Table, and other TanStack tools — not from standalone adoption. Its 790 GitHub stars tell a different story than the download numbers.
tsximport { Store } from "@tanstack/store";import { useStore } from "@tanstack/react-store";const store = new Store({ count: 0 });function Counter() {const count = useStore(store, (s) => s.count);return (<button onClick={() => store.setState((s) => ({ ...s, count: s.count + 1 }))}>{count}</button>);}
Strengths:
- Framework-agnostic (React, Vue, Solid, Angular, Svelte)
- TypeScript-first with excellent type inference
- Backed by the TanStack ecosystem (Tanner Linsley)
- Selector-based re-render optimization
- No Provider needed
Weaknesses:
- Most downloads are transitive, not intentional adoption
- Tiny community (790 stars, limited discussions)
- Sparse documentation — mostly API reference, few guides
- No middleware ecosystem (no persist, no devtools)
- Immature compared to Zustand and Jotai
Best for: Projects already deep in the TanStack ecosystem (Router, Table, Query) that want a consistent API across their stack. Not yet compelling enough as a standalone choice.
The Others
- MobX (28K stars, 2.9M downloads): Mature and stable, but hasn't published since September 2025. Observable-based reactivity. If you're already using it, no rush to migrate. If you're not, there's no reason to start.
- @legendapp/state (4K stars, 45K downloads): Claims to be the fastest with fine-grained reactivity and built-in persistence/sync. Interesting for React Native. But 210 open issues on 4K stars is a red flag.
- @preact/signals-react (4.4K stars, 246K downloads): Bypasses React's rendering model entirely for maximum performance. Fragile across React versions and incompatible with Server Components. The React team has explicitly said signals go against React's model.
- Effector (4.8K stars, 62K downloads): Event-driven reactive state with a strong following in the Russian-speaking community. Excellent for complex business logic but niche.
The Native React Story
Before reaching for any library, consider whether React's built-in primitives are enough.
React 19 brought:
usehook for consuming promises and contextuseActionStatefor form state with server actionsuseOptimisticfor optimistic UI updates- Improved Context with the
<Context value={}>shorthand
React Compiler (v1.0, October 2025):
The React Compiler shipped as stable and provides automatic memoization at build time. This weakens one of the main arguments for external state management: performance optimization. If React itself handles memoization, the re-render optimization argument for signals and proxies is significantly reduced.
Meta's own data shows up to 12% faster initial loads and 2.5x faster interactions with the compiler enabled.
When built-in state is enough:
The developerway.com analysis breaks it down well:
- Server state (~80% of what used to be Redux): Use TanStack Query or SWR
- URL state (~10%): Use nuqs or your router
- Local component state:
useState/useReducer - Shared component state: Prop drilling or Context for 1-2 concerns
You only need an external state management library when you have 3+ shared client-side concerns that Context can't handle without "Provider hell."
The TC39 Signals Proposal
The TC39 Signals proposal remains at Stage 1 as of February 2026. The committee is taking a conservative approach, requiring real-world integration into multiple frameworks before advancing. Angular, Solid, Vue, and Preact support it. The React team does not, stating that signals conflict with React's "UI as a function of state" model.
Don't bet your architecture on TC39 Signals. If they ever land in the spec, it will be years from now, and React may never adopt them directly.
Decision Matrix
| Criteria | Zustand | Jotai | Nanostores | Valtio | RTK | TanStack Store |
|---|---|---|---|---|---|---|
| Bundle size | 1.2 KB | 3.5 KB | 0.3 KB | 3.2 KB | 12 KB | 1.4 KB |
| Learning curve | Low | Medium | Low | Low | High | Low |
| TypeScript DX | Excellent | Excellent | Good | Good | Good | Excellent |
| Computed state | Via middleware | Built-in | Built-in | Automatic | Via selectors | Via selectors |
| DevTools | Redux DevTools | Jotai DevTools | None | Valtio DevTools | Redux DevTools | None |
| SSR/RSC compat | Good | Good | Good | Good | Good | Good |
| Framework agnostic | Partial | No | Yes | Partial | No | Yes |
| Community size | Very large | Large | Medium | Medium | Very large | Small |
| Maintenance | Excellent | Excellent | Good | Excellent | Good | Active |
| Weekly downloads | 22.6M | 2.9M | 1.5M | 1.2M | 11.4M | 7.2M* |
*Most TanStack Store downloads are transitive (from TanStack Router, Table, etc.), not standalone adoption.
Our Recommendation
There's no universal answer, but here's how we think about it:
For most React projects: Zustand is the safe, pragmatic default. It's tiny, well-maintained, has the largest modern community, and the simplest mental model. The 4 open issues on 57K stars speaks volumes.
If you have complex derived state: Jotai is the better choice. Its atomic model with first-class computed atoms handles complex interdependencies more elegantly than Zustand's selectors.
If bundle size is critical: Nanostores wins at 286 bytes. It's also the right choice if you're in a multi-framework project (Astro, mixing React with other frameworks). But you're trading ecosystem size and React-specific ergonomics for minimal footprint.
If you need proxy-based mutations: Valtio offers the most natural API for developers who find immutable patterns verbose. Same author as Zustand (Dai Shi), so quality is there.
If you're already on Redux: Redux Toolkit is fine. No need to migrate for the sake of it. But for new projects, the overhead is hard to justify.
If you're deep in the TanStack ecosystem: TanStack Store gives you a consistent API alongside Router and Table. But as a standalone state management choice, it doesn't yet offer enough over Zustand or Jotai to justify the smaller community and missing ecosystem.
The TanStack Query + Zustand stack (or Jotai) is the emerging default for 2026: server state in TanStack Query, client state in a lightweight store.
Sources
All data in this article was collected on February 25, 2026 from:
- npm Downloads API
Weekly download counts for all packages, fetched February 25, 2026.
- GitHub REST API
Stars, open issues, last push dates, and archive status for all repositories.
- State of React 2025 Survey
Developer satisfaction and usage data for state management libraries (2025 edition).
- State of React 2024 Survey
Developer satisfaction and usage data for state management libraries (2024 edition).
- React State Management in 2025: What You Actually Need
Nadia Makarevich's analysis of when you actually need a state management library.
- Top 5 React State Management Tools in 2026
Syncfusion's comparison of the leading state management solutions.
- State Management Trends in React 2025
When to use Zustand, Jotai, XState, or something else.
- React Compiler v1.0 Release
Automatic memoization at build time, stable since October 2025.
- TC39 Signals Proposal
The Stage 1 proposal to add reactive signals to JavaScript.
- npm Trends Comparison
Interactive download trend comparison across state management libraries.
