Look, Redux isn't evil. It's predictable, it has great DevTools, and it works. But after maintaining Redux apps for years, I've hit the same frustrating patterns over and over.
The Boilerplate Hell is Real
Remember setting up your first Redux store? Three files just to increment a counter. Action creators, reducers, middleware setup - and that's before you even touch RTK Query. I've spent entire afternoons just wiring up a new feature that should've taken 20 minutes.
Here's what actually happens: you start with clean Redux patterns, then reality hits. Async actions need thunks. Forms need local state anyway. Server data gets mixed with client state, and suddenly you're debugging why a modal is still open because some reducer 5 files away didn't reset properly. The Redux Style Guide tries to help, but it's a lot of rules to remember.
When Performance Bites You in the Ass
Redux's connect() was a nightmare, but even with hooks it's still manual optimization hell. Forgot to memo that selector? Enjoy watching your entire component tree re-render because someone toggled a sidebar.
I learned this debugging a production issue where our user list re-rendered 40 times on page load. Why? Because our "smart" Redux structure had everything connected to everything, and useSelector without proper equality checks is basically useless. The React DevTools Profiler helped track down all the unnecessary renders.
The Server State Disaster
The worst Redux codebases I've seen try to jam everything into the store. API responses, loading states, error handling, cache invalidation - all mixed with actual client state like "is the modal open."
Spent 3 months on a project where we had actions like FETCH_USERS_START
, FETCH_USERS_SUCCESS
, FETCH_USERS_ERROR
for every API call. The reducers file was 800 lines of handling loading states. Then someone discovered TanStack Query and we deleted 60% of our Redux code overnight. The official TanStack Query documentation shows exactly why mixing server state with client state is a bad idea.
What Actually Works Now
After shipping with everything from Zustand to MobX to just Context API, here's the truth:
For most apps: Zustand + TanStack Query. Zustand handles the "is sidebar open" stuff, TanStack Query handles server data. Done. No actions, no reducers, no middleware ceremony.
For complex apps: MobX if your team can handle the magic, or stick with Redux Toolkit if you're already invested. Don't migrate working apps just to be trendy.
For simple apps: Context API is fine. Seriously. Everyone acts like it's broken, but it works great for themes and user auth. The React docs on Context show exactly when to use it.
The real insight? Stop trying to solve everything with one tool. Server state and client state are different problems that need different solutions. Kent C. Dodds' article on Application State Management with React nails this concept.
So what are your actual options in 2025? Let me break down the tools I've actually used in production, with real bundle sizes and honest assessments - no marketing bullshit.