Why Redux Lost Its Grip on React State Management
For half a decade, Redux was the default answer to "how do I manage state in React?" Every tutorial mentioned it. Every job listing required it. And every new developer spent their first week wondering why updating a single boolean needed three files, two action creators, and a reducer.
The core issue was never Redux itself. It was the ceremony around it. Dispatching actions, writing switch statements, normalizing state shapes, and wiring up mapStateToProps all added friction that slowed teams down. Redux Toolkit cleaned up much of this boilerplate starting in 2019, but by then, the React community had already started looking elsewhere.
The tipping point came when React Server Components and the App Router in Next.js changed the calculus entirely. Suddenly, a large portion of state that teams had been shoving into Redux belonged on the server. Client state shrank. And when your client state is small, reaching for a heavyweight store with middleware, selectors, and normalized entities feels like driving a semi truck to the grocery store.
By early 2026, Zustand overtook Redux in weekly npm downloads. Jotai carved out its own growing niche. Redux is not dead, but its era as the unquestioned default is over. If you are starting a new React project today, you owe it to yourself to evaluate all three options with fresh eyes.
Zustand: The Simple Store That Scales
Zustand (German for "state") takes a radically minimal approach to state management. There are no providers to wrap your app in, no context boundaries to worry about, and no boilerplate beyond a single create call. You define a store as a plain function, and your components subscribe to exactly the slices they need.
Here is what a typical Zustand store looks like in practice: you call create, pass in a function that returns your state and actions, and you are done. Components call the hook, pick the slice they care about, and React re-renders only when that slice changes. No selectors library required. No memoization hacks.
What makes Zustand compelling for production applications is its flexibility without complexity. Need middleware? Zustand ships with persist (localStorage, sessionStorage, AsyncStorage), devtools (Redux DevTools compatible), immer (immutable updates with mutable syntax), and subscribeWithSelector for fine-grained subscriptions outside of React. You compose these as wrappers around your store function. No plugin system to learn.
Zustand also works beautifully outside of React. Because stores are vanilla JavaScript objects with a subscribe method, you can use them in utility functions, server-side logic, or even other frameworks. This makes it a strong choice for teams that share business logic across a React frontend and a Node.js backend.
The tradeoff? Zustand stores are top-down by design. You define the shape of your state upfront, and all consumers read from that centralized store. For most applications this is exactly what you want. But if your state is highly granular and spread across dozens of independent atoms, Jotai might be a better mental model.
Jotai: Atomic State from the Bottom Up
Jotai takes the opposite approach to Zustand. Instead of defining a single store with a known shape, you create individual atoms that each hold a piece of state. Components subscribe to specific atoms, and Jotai tracks dependencies automatically. If atom B derives from atom A, updating A re-renders only the components that read B (and only if B's value actually changed).
This bottom-up model shines in applications with lots of independent, fine-grained state. Think of a complex form builder where each field is an atom, a dashboard with dozens of independent widgets, or a collaborative editor where each block manages its own state. In these cases, Jotai's atomic model avoids the "god store" problem where a single state tree becomes unwieldy.
Derived atoms are Jotai's secret weapon. You create a read-only atom that computes its value from other atoms, similar to Recoil's selectors but with a simpler API. Async atoms let you fetch data and integrate it into the atom graph. Write-only atoms let you create actions that update multiple atoms at once. The composition is elegant and TypeScript-friendly.
Jotai also integrates tightly with React's concurrent features. Atoms work naturally with Suspense for async operations, and Jotai's provider model (optional but available) supports scoped state for things like modals or multi-step wizards where you want isolated state trees.
The tradeoff with Jotai is organizational. When your state lives in dozens of independent atoms scattered across your codebase, it can be harder for new team members to understand the full state picture. There is no single file you can open to see "here is everything this app tracks." For teams that value that kind of visibility, Zustand's centralized stores can be easier to reason about. If you are weighing React frameworks alongside state management, our React vs Vue vs Svelte comparison covers how each framework handles reactivity at its core.
Redux Toolkit: Modern Redux for Large Teams
Redux Toolkit (RTK) is Redux, but without the pain. It bundles Immer for immutable updates, configureStore for sensible defaults, createSlice for defining reducers and actions in one place, and createAsyncThunk for handling side effects. If you tried Redux in 2018 and hated it, RTK deserves a second look.
RTK Query is the headline feature that keeps Redux competitive. It is a full data-fetching and caching layer built directly into Redux Toolkit. You define API endpoints, and RTK Query generates hooks, manages cache invalidation, handles optimistic updates, and provides loading/error states out of the box. For teams that need a robust data layer tightly coupled with their state, RTK Query eliminates the need for separate libraries like React Query or SWR.
Redux also has the most mature ecosystem of any React state library. Redux DevTools lets you time-travel through state changes, inspect action payloads, and export state snapshots for bug reports. Redux Saga, Redux Observable, and dozens of middleware options give you fine-grained control over side effects. For large teams with strict architectural requirements, this ecosystem is a genuine advantage.
The cost is complexity. Even with RTK's improvements, Redux still requires more code than Zustand or Jotai for equivalent functionality. The mental model of actions, reducers, and selectors adds a learning curve that smaller teams may not need. And the bundle size, while reduced from legacy Redux, is still meaningfully larger than either alternative.
Redux Toolkit makes the most sense for large applications with big engineering teams that benefit from strict patterns, comprehensive DevTools, and an established hiring pool of developers who already know Redux. If your team has ten or more frontend engineers working in the same codebase, the enforced structure of Redux can prevent the kind of state spaghetti that emerges when everyone invents their own patterns.
Bundle Size, TypeScript, and Performance Compared
Let's talk numbers. In a production React application, every kilobyte matters for initial load time, and the differences between these three libraries are significant.
Bundle size (minified + gzipped):
- Zustand: approximately 1.1 kB. This is remarkably small. You are adding almost nothing to your bundle.
- Jotai: approximately 2.4 kB for the core package. Add-on packages (jotai/utils, jotai-immer) increase this, but you only import what you use.
- Redux Toolkit: approximately 11 kB for RTK alone. Add RTK Query and you are looking at 20 kB or more. This is the cost of a batteries-included approach.
For applications where Next.js performance and Core Web Vitals scores matter, Zustand's tiny footprint gives it a clear edge. That said, if you are already using RTK Query and it replaces both a state library and a data-fetching library, the total bundle impact may be comparable.
TypeScript experience: All three libraries have excellent TypeScript support in 2026, but the developer experience differs. Zustand infers types from your store definition with zero extra annotations in most cases. Jotai's atom typing is clean and composable, with derived atoms automatically inferring their return types. Redux Toolkit has strong types but requires more explicit annotations, especially for thunks and selectors. RTK Query's generated hooks are well-typed, though the endpoint definition syntax can be verbose.
Re-render behavior: This is where architectural choices create real performance differences. Zustand uses shallow equality checks by default. If your selector returns the same reference, the component does not re-render. You can also pass a custom equality function for fine-tuned control. Jotai re-renders only the components subscribed to atoms that actually changed, which gives you granular reactivity without manual optimization. Redux uses strict equality by default with useSelector, which means returning new object references from selectors triggers unnecessary re-renders unless you use createSelector from Reselect for memoization.
In practice, Jotai tends to produce the fewest unnecessary re-renders out of the box because its atomic model naturally scopes subscriptions. Zustand is close behind if you write focused selectors. Redux requires more discipline to avoid performance pitfalls, but a well-structured Redux store with memoized selectors performs just fine at scale.
Server-Side Rendering and Next.js Compatibility
If you are building with Next.js (and in 2026, most new React projects are), SSR compatibility is not optional. Each library handles server rendering differently, and the differences matter more than you might expect.
Zustand works with SSR but requires some care. Because Zustand stores are module-level singletons by default, you need to avoid sharing state between requests on the server. The recommended pattern is to create stores per request or use Zustand's createStore function paired with React context to scope stores to individual requests. With the Next.js App Router, Zustand works well in Client Components. You cannot use it directly in Server Components (no hooks there), but you can pass server-fetched data as initial state to a Zustand store on the client.
Jotai has strong SSR support through its optional Provider component. By wrapping your app (or a subtree) in a Jotai Provider, each server request gets its own atom scope, preventing state leakage between requests. Jotai also provides a useHydrateAtoms hook that lets you initialize atoms with server-fetched data, making the handoff from server to client seamless. For Next.js App Router projects, this pattern works cleanly.
Redux Toolkit has the most established SSR story, largely because Redux has been solving this problem for years. The typical pattern involves creating a new store instance per request, serializing its state, and rehydrating on the client. Libraries like next-redux-wrapper automate this, and RTK Query supports server-side prefetching for data that needs to be available on first render. The downside is that this setup involves more boilerplate than either alternative.
One important trend to note: with React Server Components handling more data fetching on the server, the amount of state you need to manage on the client is shrinking. Many teams find that a lightweight client store (Zustand or Jotai) combined with server-side data fetching covers their needs without the overhead of a full Redux setup with RTK Query.
DevTools, Debugging, and Ecosystem Maturity
When something breaks in production at 2 AM, the quality of your debugging tools matters more than any API elegance. Here is how each library stacks up.
Redux DevTools remains the gold standard. Time-travel debugging, action logs with full payloads, state diffing between dispatches, the ability to export and import state snapshots, and integration with error-reporting tools like Sentry make Redux the most debuggable state library available. If your team spends significant time debugging complex state flows, this is a legitimate reason to choose Redux.
Zustand integrates with Redux DevTools through its devtools middleware. You get action logging, state inspection, and time-travel debugging, though the experience is not quite as polished as native Redux. Named actions help readability in the DevTools panel. For most teams, this level of debugging support is more than sufficient.
Jotai offers a dedicated DevTools browser extension and a jotai-devtools package that provides a visual atom graph, current atom values, and the ability to modify atoms at runtime. The atom graph visualization is unique to Jotai and genuinely useful for understanding dependency chains in complex applications. However, the tooling is newer and less battle-tested than Redux DevTools.
Ecosystem maturity is another factor. Redux has thousands of packages, blog posts, Stack Overflow answers, and battle-tested patterns for every conceivable use case. Zustand's ecosystem is growing rapidly, with strong community middleware and excellent documentation. Jotai benefits from being part of the pmndrs (Poimandres) collective, which also maintains Zustand, React Three Fiber, and other popular libraries. If you want to understand how TypeScript compares to JavaScript across these ecosystems, that matters too, since all three libraries are TypeScript-first in 2026.
When to Use Each: A Decision Matrix for Your Project
After building production applications with all three libraries across dozens of client projects, here is our honest recommendation matrix.
Choose Zustand when:
- You want the simplest possible API with the smallest bundle impact.
- Your team values pragmatism over architectural patterns. You want state management to get out of the way.
- You are building a small to mid-size application (or even a large one) where a handful of well-organized stores cover your needs.
- You need state outside of React (shared logic, server utilities, cross-framework code).
- You are migrating away from Redux and want a familiar store-based model with less ceremony.
Choose Jotai when:
- Your application has lots of independent, fine-grained state (form builders, dashboards, collaborative tools).
- You want automatic re-render optimization without writing selectors or memoization logic.
- You enjoy a bottom-up composition model where state emerges from small, composable pieces.
- You are building with React Suspense and want first-class async atom support.
- Your team is comfortable with a less centralized state architecture.
Choose Redux Toolkit when:
- You have a large engineering team (10+ frontend developers) that benefits from enforced patterns and strict conventions.
- You need RTK Query's integrated data fetching, caching, and cache invalidation tied directly to your global state.
- Debugging and DevTools quality is a top priority for your workflow.
- You are maintaining a large existing Redux codebase and want to modernize incrementally rather than rewrite.
- Your hiring pipeline favors candidates with Redux experience.
For most new React projects in 2026, we default to Zustand. It covers 90% of client state needs with minimal overhead, plays well with Next.js, and lets your team focus on building features instead of managing state infrastructure. When a project has genuinely complex, granular state requirements, we reach for Jotai. And when a large enterprise client needs the structure and tooling that Redux provides, RTK is still a strong, well-supported choice.
The best state management library is the one your team can use effectively without fighting the tool. If you are unsure which approach fits your project, or if you are dealing with a messy state architecture that is slowing your team down, we can help you sort it out. Book a free strategy call and we will walk through your specific situation together.
Need help building this?
Our team has launched 50+ products for startups and ambitious brands. Let's talk about your project.