When I rebuilt the Angular Movies app shell with StateAdapt, the resulting code was 50% smaller than the original RxAngular version.
What caused the gap?
The application shell's state was minimal — just a boolean flag controlling whether the sidenav was visible.
That simplicity worked in StateAdapt's favor. While RxAngular needs an object to manage, StateAdapt can handle any shape of state, so I used a plain boolean directly. If the requirements change down the road, switching to an object is straightforward.
There was another factor at play. The RxAngular version triggered a method call when the URL changed, rather than responding reactively. StateAdapt handled this reactively from the start, so I suspect the RxAngular implementation could trimmed a few more lines if that pattern had been avoided. With StateAdapt, I made everything as reactive as possible from the outset.
Here's the main StateAdapt code:
urlChange$ = this.router.events.pipe(
filter((e) => e instanceof NavigationEnd),
map((e) => (e as NavigationEnd).urlAfterRedirects),
distinctUntilChanged(),
toSource('urlChange$')
);
sideDrawerOpen = adapt(['app-shell.sideDrawerOpen', false, booleanAdapter], {
setFalse: this.urlChange$,
});
In RxAngular, that same false had to be expressed as:
this.state.set({ sideDrawerOpen: false });
In the StateAdapt template, I referenced the state source directly from the sideDrawerOpen store. RxAngular, however, requires reacting to an observable:
this.state.connect('sideDrawerOpen', this.ui.sideDrawerOpenToggle$);
The number of imperative calls is the same across both: one uses .next() in the template, the other uses set().
The most awkward part of the RxAngular version was this piece:
this.effects.register(
this.router.events.pipe(
filter((e) => e instanceof NavigationEnd),
map((e) => (e as NavigationEnd).urlAfterRedirects),
distinctUntilChanged()
),
() => this.closeSidenav()
);
// ...
closeSidenav = () => {
this.ui.sideDrawerOpenToggle(false);
};
The closeSidenav method was invoked from precisely one location. That same side-effect could have been wired up directly as this:
this.state.connect('sideDrawerOpen', urlChange$, () => false);
// I might be wrong on the exact syntax, but I think that's right.
Everything was nested inside an init method, called imperatively from ngOnInit, which Angular itself invokes imperatively:
init() {
// ...
// RxAngular connections, `set`s, effects
// ...
}
Wrapping up
For the complete diff, check out the pull request — though it's best not to open it.
RxAngular remains my go-to choice for state management in Angular. StateAdapt is still in its early stages. I want to apply it across several more projects before I'm ready to tag it as version 1.0. If it looks promising to you, a star on GitHub would be appreciated — and I'd love to hear what you think if you experiment with it.
Thanks for reading!

