The real problem isn't which tool you pick. It's when you decide to think about it.
Every few months there's a new piece online about state management. Redux vs Zustand. Context vs Signals. TanStack vs everything else. The tools keep changing and the conversation keeps happening and somehow teams keep ending up in the same place: a codebase where nobody is quite sure what lives where, why it got there, or who is responsible for cleaning it up.
I've come to believe the tool debate is mostly a distraction. The real problem isn't which library you chose. It's that most teams don't think seriously about state until the app is already fighting them.
By then, the decisions are already made. You're just living with them.
When you start a new project, state feels like a small problem. You've got a few components, some data coming from an API, maybe a loading spinner to manage. You reach for whatever feels familiar and you move on.
That's not laziness. That's just prioritizing. The feature matters more than the architecture at day one.
But here's where it goes wrong: day one's decisions don't stay at day one. They compound. That component that holds a bit of local state becomes the source of truth for three other components. That API response you cached informally in a parent component is now being passed down through four levels of props. The thing that felt like a quick decision six weeks ago is now the thing you have to work around every single time you add something new.
State management debt doesn't announce itself. It just quietly makes every new feature slightly harder than it should be.
And at some point someone on the team says "we should have used Redux" and someone else says "Redux would have been overkill" and both of them are probably right and also both of them are missing the actual point.
This is the distinction I wish someone had explained to me clearly much earlier, because once you see it you cannot unsee it.
Most of what we call "state management" in a frontend app is really two completely different problems wearing the same name.
Server state is data that lives on a server, that you fetch, cache, and keep in sync. A list of users. A product catalog. An order status. This data has a source of truth that isn't your app. Your app is just a window into it.
UI state is data that only your app cares about. A modal being open. A tab being selected. A form being in the middle of editing. This data doesn't belong on a server. It belongs as close as possible to the component that needs it.
When teams reach for Redux on day one, they often end up managing both of these in the same place. Which means you've got your modal open/closed state sitting next to your entire API response cache, both updated by dispatching actions, both living in the same global store. It works. But it's a lot of ceremony for problems that didn't need the same solution.
When I start something, my default is TanStack Query for anything that comes from a server. It handles fetching, caching, background refetching, loading and error states, all the things you would otherwise end up writing yourself or bolting onto Redux with middleware. For server state it is genuinely hard to beat.
For UI state, I start as local as possible. If only one component needs to know something, that component owns it. If a few related components need to share something, Context is usually enough. I don't introduce a global state library until there's a clear reason to, not because global state is bad but because the overhead isn't worth it until the problem actually needs it.
And Redux? Redux with Redux Toolkit is actually really good now. But it earns its place. Large apps with complex shared state, lots of async logic, teams where traceability and dev tooling matter because multiple people are touching the same state simultaneously. That's where it shines. A three-page dashboard with a couple of API calls is not that.
The tool isn't the decision. The decision is understanding what kind of state you're dealing with before you write a single line.
I think the reason teams end up in state management trouble isn't that they chose the wrong library. It's that they never had the conversation about state at all. Not seriously. Not before the codebase had opinions about it.
Questions worth asking at the start of any project: What data in this app comes from a server and needs to stay in sync? What data only exists in the UI and has no business being global? Which pieces of state do multiple unrelated parts of the app need to share? How much does it matter that state changes are traceable and debuggable?
None of these questions require you to have already picked a library. They just require you to think about the shape of the problem before you're deep inside it.
Answering them well doesn't guarantee you'll choose the right tool. But it almost guarantees you won't choose the wrong one for the wrong reasons.
The honest conclusion I've arrived at after four years of building frontend applications is that there isn't a correct answer to "which state management solution should I use." There's only the answer that fits the project, the team, and where the complexity actually lives.
What there is a correct answer to is when to think about it. And the answer to that is: before it becomes a problem, not after.
The teams that get this right aren't the ones using the trendiest library. They're the ones who sat down early and asked what state even means in their app before they started filling it.
That conversation takes maybe an hour at the start of a project. It saves weeks somewhere in the middle.
1
5
0