Why I Didn't Reach for NgRx, NGXS, or a Service Full of Subjects
Criticizing popular state management tools is bound to ruffle some feathers, and I know that the folks behind these libraries have poured serious effort into them. I'm genuinely thankful for everyone who has contributed to the ecosystem. Still, people have asked me to explain why I built StateAdapt rather than leaning on an existing option.
Before going further, it's worth reading my introduction to StateAdapt. That piece is far more upbeat than this one.
With that out of the way, let's dig in.
NgRx
The State Pattern Is Tied to the State
In one NgRx project, I built a complex hierarchy of components. Later, it turned out I needed a second copy of that entire component tree on the same page, simultaneously. To support that, I would have had to add an extra field to 30 actions, and the reducer would need to grow more sophisticated to decide whether an action belonged to one branch of the state tree or the other.
If state management followed the same compositional philosophy as UI construction, adding another instance of a state pattern would take just a couple of lines. The state adapter pattern in NgRx/Entity points in that direction, but I suspect most developers would still see that as extra ceremony unless the accompanying boilerplate shrank at the same time. That's exactly what StateAdapt aims to do.
Boilerplate
The boilerplate burden of NgRx is well known. The core team has made admirable progress reducing it, but getting it down to a minimum really requires leaning hard on RxJS — which is precisely what StateAdapt does. StateAdapt delivers the same advantages as NgRx while requiring roughly 60% of the code that modern NgRx calls for at a minimum.
Best Practices?
NgRx tries to keep some distance from Redux, yet at its core it's Redux reimagined as an observable wrapper. Unfortunately, a lot of the accumulated wisdom and best practices from the Redux world didn't carry over. There are helpful resources, like this excellent talk by Mike Ryan, but my impression is that most teams use NgRx in ways that undermine the benefits it could provide.
Back in 2017, I wrote Stop using ngrx/effects for that, hoping people would stop treating effects as the answer to everything. That didn't really take. The first example in the NgRx/Effects docs is exactly the sort of thing effects shouldn't handle: fetching data by dispatching an action. In that article, I argue that plain RxJS makes data dependencies far more flexible and maintainable.
My suggestion didn't gain much traction, and I noticed people seemed put off by the obscure using function from RxJS that my approach relied on. I was also too lazy to write more about it at the time. So one of StateAdapt's main goals became hiding that complexity behind the scenes. I'm quite pleased with how that worked out. With StateAdapt, developers can concentrate on reactive thinking without worrying about how to wire up the store.
NGXS
State Pattern Is Coupled to the State
This is the same fundamental issue I described for NgRx above.
Everything Is an Effect
I already felt people were leaning way too heavily on NgRx/Effects. Then someone went and built a state management library where literally everything is an effect.
Instead of repeating my arguments for pure functions, I'll point you to my earlier article. I appreciate the effort to reduce boilerplate, but I personally find NGXS code difficult to follow. The mechanics of data manipulation tend to obscure the bigger picture of data flow, and vice versa. I prefer a clean separation between the high-level flow and the low-level details of state updates. NgRx provides that (as long as effects aren't overused), but it comes with all that boilerplate. StateAdapt keeps the separation while cutting down the ceremony.
Dispatching Multiple Actions
In Redux, dispatching several actions at once is considered an anti-pattern. NGXS encourages it with dedicated syntax.
One of my early encounters with an NGXS app involved opening Redux DevTools and seeing something like this:
Looking at the code revealed these actions were all fired at the same moment.
The connection between what was triggered and how the application responded got so blurry that I could no longer see clearly what was happening. Logically, there should have been a single action: AnswerCall. If I wanted to inspect the consequences, I could click on it and review the state changes.
There's another problem: jumping to any of those actions in Redux DevTools drops the app into an intermediate state that could never actually exist.
Finally, this approach runs contrary to functional reactive programming. Instead of having multiple action handlers subscribe to one action across separate state files, the event source takes responsibility for driving all downstream changes. An event source that dispatches multiple actions is no longer just an event source.
Akita
Imperative State Management
Akita updates state imperatively, much like NGXS tends to do. Actually, NGXS could learn a thing or two from Akita's syntax. But both libraries share the downsides I outlined in the NGXS section.
Subjects in a Service
No Redux DevTools
Debugging RxJS is notoriously painful. Typically you end up adding a tap(console.log) somewhere in the file, reloading the app, and hoping to reproduce the scenario you were tracking. Redux DevTools, on the other hand, records everything on its own. You can open it any time and trace through what happened.
(Actions and state transitions are central to understanding an application, so Redux DevTools is incredibly useful. Still, I'm on the lookout for a better way to debug RxJS elsewhere in apps. If you know of something, please share.)
No Selectors
Sometimes you want to merge observables, but combineLatest fires once for every input observable even when those inputs emit synchronously. I believe that's the original reason NgRx introduced createSelector — selectors solve exactly this problem.
It's also a relief to keep all derived state computation in pure functions, isolated from the asynchronous RxJS plumbing.
There's an added advantage with derived state. Plain RxJS forces you to chain map, distinctUntilChanged, and shareReplay just to compute derived state efficiently. Selectors handle all of that automatically.
StateAdapt

So that's the reasoning behind StateAdapt. Is it flawless? Absolutely not. Is it meant for everyone? I'd argue it's for anyone who values minimal, reactive, debuggable, and reusable code.
Try it out and tell me what you think.
Others
If I overlooked any libraries, please let me know. I did extensive research before writing StateAdapt, but I might have missed something.
And if anyone wants to see comparisons, I'd be happy to put some together. I already have plans for a few. Show me a feature built with any state pattern, and I'll rebuild it using StateAdapt.
Thank You
Please forgive me. I love you all!





