★ 58,257TypeScriptMITupdated Jun 2026
best for teams who want a tiny, boilerplate-free global store
Zustand is the one people reach for first, and usually it's the right call. No providers, no boilerplate, no action types — you write a hook, you call it, you're done. The vanilla core means you can share that same store with non-React code, which neither Redux nor MobX makes especially easy.
The TypeScript story has a wart: you have to write create<State>()(), a curried double-call that trips up almost everyone the first time they add types to a store. The set function also merges by default but replaces entirely if you pass true as the second argument — an easy way to wipe your actions along with your data if you're not paying attention. There's no computed state built in either, so derived values get recalculated in every selector unless you reach for a separate package, something Jotai and MobX both handle natively.
Debug more than one store at once and Redux DevTools shows each in isolation rather than a combined picture — the one place the extra ceremony in actual Redux still earns its keep.
Our full take →GitHub ↗
★ 49,222TypeScriptMITupdated Apr 2026
best for anyone with server data to fetch, cache, and keep in sync — the default choice now
If you're fetching anything from a server, this is the default now, and for good reason. The cache model — staleTime, gcTime, automatic deduplication, structural sharing — just works without much tuning, and the ESLint plugin catches the two mistakes that burn everyone (unstable query keys, missing deps) before they turn into a 3am page.
The rough edge is optimistic updates: you're still manually snapshotting the cache, mutating it, and writing a rollback in onError for every mutation that needs one, with no built-in compensation. SWR has the same gap. If your data is relational — updating a user and having every card and list referencing them update too — Reactive Data Client does that automatically where TanStack Query makes you invalidate queries by hand.
Outside React the story thins out fast: the Angular adapter lags noticeably behind the React one in docs, SSR support, and new features, so don't assume parity if your team is split across frameworks.
Our full take →GitHub ↗
★ 61,456TypeScriptMITupdated Oct 2026
best for large teams who want strict, inspectable state plus a huge ecosystem of middleware and devtools
Redux earns its place on this list for what it built, not what it's for today: predictable state, serializable actions, and a debugging story — time-travel, full action logs — that nothing else here fully matches, including Zustand's per-store-only devtools.
But the repo's own README tells you not to install this package directly and to use Redux Toolkit instead, and it shows. No built-in async handling, no selectors, no Immer — you're expected to bolt all of that on yourself if you skip RTK. The examples in this repo are exactly the boilerplate-heavy patterns, action types and action creators and combineReducers, that Toolkit exists to delete.
Learn it if you're maintaining a legacy codebase or want to understand what RTK is wrapping underneath. Don't start a new project on this package directly.
Our full take →GitHub ↗
★ 21,195TypeScriptMITupdated Jun 2026
best for apps built from many small, independent pieces of state that update at different times
Where Zustand gives you one store, Jotai gives you atoms — small, composable pieces of state you wire together instead of centralizing. If your app's state is genuinely scattered across features rather than living in one shape, this fits better than forcing everything into a single Zustand store.
Async atoms lean on Suspense, and Suspense in production is still rougher than the docs suggest — error boundaries and streaming SSR interact in ways that will surprise you. atomFamily is the specific trap: every unique key creates a new atom permanently, with no built-in garbage collection, so a dynamically keyed list of atoms quietly leaks memory unless you remember to call the cleanup API yourself.
Devtools exist but don't give you Redux's time-travel debugging, and server-side hydration needs its own useHydrateAtoms boilerplate that gets uglier as your SSR setup gets more complex.
Our full take →GitHub ↗
★ 28,192TypeScriptMITupdated Jun 2026
best for developers who prefer mutable, OOP-style reactive state over reducers and actions
MobX asks you to stop thinking in reducers and selectors entirely: mutate state directly, decorate a class with makeAutoObservable, and let the dependency graph figure out which components need to re-render. Done well it's the least code you'll write for a given amount of reactivity — no selector-writing overhead like Zustand or Redux require.
The cost is that the dependency graph is invisible by default. When a component silently stops re-rendering, there's no selector to inspect — you're reaching for trace() or the devtools extension to find out why. Strict mode, which forces mutations through actions, is off unless you turn it on, so new developers mutate state directly and ship subtle async bugs before anyone notices.
Class-based stores are still the idiomatic pattern here, and it shows: they sit awkwardly next to hooks and function components, useLocalObservable is a leaky abstraction the moment you try to destructure it, and if you're on an older codebase the MobX 4/5/6 migration docs are genuinely painful.
Our full take →GitHub ↗
★ 32,400TypeScriptMITupdated Jun 2026
best for projects that just need simple stale-while-revalidate fetching without React Query's larger API
SWR does one thing — stale-while-revalidate fetching — with less API surface than TanStack Query. Request deduplication is correct by default: multiple components asking for the same key in a render cycle share one in-flight request without any configuration. The fetcher is just any async function, so GraphQL, REST, or IndexedDB reads all work the same way.
What you give up for that smaller surface is anything resembling batch invalidation. TanStack Query lets you invalidate a whole group of related queries at once; SWR expects you to call mutate with the exact key, every time, which gets tedious once your app has more than a handful of endpoints. Error handling is also opt-in per component — forget to check the error field and you get a silent failure with nothing in the UI telling you it happened.
Good choice if your fetching needs are genuinely simple. The moment you need coordinated invalidation across a dozen related queries, you'll be rebuilding pieces of what TanStack Query already gives you.
Our full take →GitHub ↗
★ 2,031TypeScriptApache-2.0updated Jun 2026
best for REST/GraphQL/websocket APIs that want normalized caching without hand-writing selectors
This is the answer if your data is relational and you're tired of writing the code that keeps it in sync — update a User from one endpoint and every component holding a reference to that user re-renders, no manual wiring, no invalidateQueries calls. Neither TanStack Query nor SWR does this; both expect you to model those relationships yourself.
The price is a real learning curve before you write your first non-trivial fetch: Entity, RestEndpoint, resource(), Collection, Query, Lazy, and the Manager middleware are all things you need up front. And the 'zero updater functions' pitch holds for the happy path but not for complex optimistic rollbacks, where you end up back in the Controller API writing the same imperative calls this library was supposed to save you from.
At roughly 2,000 stars against TanStack Query's tens of thousands, you're also choosing a much smaller community — when something breaks in an unusual way, you're mostly on your own.
Our full take →GitHub ↗
★ 3,607TypeScriptApache-2.0updated Jun 2026
best for local-first apps that need a built-in sync engine, not just a store
Every other library on this list assumes a server that's usually reachable. LiveStore doesn't: it puts SQLite in the browser over WASM and builds event-sourced sync on top, which is the right foundation if your app genuinely has to work offline — field tools, collaborative editors, that kind of thing. If you're managing a CRUD app with a normal backend, it's the wrong tool for the job; TanStack Query or SWR get you there with far less machinery.
It leans hard on Effect-ts internally, so when something breaks below your own code you're debugging in an ecosystem you may not have signed up for, and it's still in beta with real API churn — worker boundary changes, sync predicate changes — so don't adopt it anywhere a breaking upgrade would hurt.
The sync backend story today is Cloudflare Workers or build-your-own, with no self-hosted option for a boring VPS yet, and WASM SQLite adds real weight to the initial load — a tradeoff the project's own performance docs admit rather than hide.
Our full take →GitHub ↗
// questions people ask
Before you choose
Do I need a separate library for server state and client state?
In most real apps, yes — they solve different problems. TanStack Query or SWR handle fetching, caching, and revalidating server data; Zustand, Jotai, or Redux hold the state you own outright, like form values or a selected tab. Forcing server data through a client-state library usually just means reinventing cache invalidation badly.
Is Redux still worth using in 2026?
Not the raw Redux package by itself — its own README tells you to use Redux Toolkit instead, and the core repo is in legacy-maintenance mode. It's still worth understanding if you're on an existing Redux codebase or want to know what Toolkit is wrapping underneath.
What's the real difference between Zustand and Jotai?
Zustand centralizes state in one store you call as a hook; Jotai splits it into small atoms you compose together. Pick Zustand if your state naturally lives in one shape, Jotai if it's genuinely scattered across independent features.
Is LiveStore production-ready?
Not yet for most teams — it's still in beta with real API churn, and self-hosted sync beyond Cloudflare Workers means building it yourself. Worth watching for local-first apps, but don't put something business-critical on it today.
None of these are going away soon, so the real decision is which tradeoffs fit your app, not which one has the most stars today — Redux is proof that star count is a lagging indicator. If you'd rather have picks like these land in your inbox every week than go hunting for them yourself, that's what the finds.dev email does.