This has been in my drafts for a while — I’ve decided to publish as is and label it as something close to a more polished journal entry. Since this has gotten stale, I’d recommend using Angular Signals today
The core problem that we were trying to solve using NgRx Component Store was building a stateful web application.
When I began this, I didn’t understand what a stateful web app was, especially compared to a regular website. The stateful bit means that you need to keep track of where the user is during their interaction with the site.
Imagine a weather website — there’s very little state that you actually need to keep track of while the user is checking their local weather. They can check it in New York, and then in San Francisco, but there’s no retained state other than who the user is and some minor preferences like temperature unit.
We were looking to build an order page, where the user can build up a cart and then check out. In this scenario, you need to know where the user is in a given transaction. The page needs to keep track of what’s in the cart, what discounts have been applied, the interactions between the various elements of the user’s identity, among many other things.
If this still isn’t clear, imagine a wizard (like an installer). At each step of the wizard you need to keep track of what the previous steps have been, since they can affect the future steps. This is what keeping track of state means, that the application has a memory.
Given this problem, we decided that it’d be best to use a state management library rather than trying to handle this using built-in Angular capabilities. The easiest and most lightweight choice was NgRx Component Store. We also considered NgRx Store, but we thought it excessive for our needs — too many features and too much boilerplate.
The first question I had when beginning this work was — what is the justification of using a library, rather than trying to do this with Angular by itself? Using Angular by itself would imply using BehaviorSubjects in services, which would then grow to basically become an equivalent of NgRx Component Store anyways. Passing data via Angular inputs and outputs was out of the question entirely, I actually tried doing it and pretty much immediately realized the difficulty of scaling that type of data transfer.
The major difficulty and benefit with using Component Store was the associated use of RXJS. For developers, reactive programming was a barrier to contributing to the codebase. Elsewhere in the codebase RXJS use was limited to unpacking API call results to be used in further declarative code. I had never done serious reactive programming before either, so it was difficult for me to get adjusted to it as well.
Reactive programming is characterized by automatic propagation of changes throughout a system. Data changes automatically flow through predefined streams and transformations, updating all dependent values and UI elements without explicit manual updates.
Rather than writing imperative code that says “when X happens, update Y and Z,” reactive programming establishes relationships between values so that Y and Z automatically reflect changes in X.
Declarative

Reactive

RXJS brings reactive programming to JavaScript. RxJS works by operating on data flows. The data flow is called an Observable, which is then modified using Operators, and then received by Subscription.
RxJS is incredible when combined with NGRX Component Store because it directly allows the state to update from data flows, as well as connecting different parts of the state as dependencies explicitly. I think in order to illustrate this, we should consider the alternative.
Imagine a page that requires state management, but instead of choosing to use reactive programming and a state management library, you decide to code this with imperative programming. What will happen is that you need to manually update the state with explicit calls. If something changes, you need to do an explicit call to a function that will then notify everything that depends on that information. This also means that you need to recalculate every derived value manually yourself as well.
For me personally, the most infuriating part of working with code like this is that you don’t know where the changes to state came from. Any function can modify state, so it’s very difficult to reason about which function causes which change.
There’s also the feature of immutable state which is important for reactive programming. You can’t modify your state, the only thing you can do is replace it with a new state. This concept has practical implications in the sense that if you want to modify state you need to explicitly replace state, not just modify state object properties. This has benefits in debugging because it’s easier to track changes, Angular relies on this to properly propagate state updates, and it also allows for explicit handling of race conditions via RXJS flattening.
For performance benefits, we also decided to use OnPush change detection in Angular which is significantly more performant than regular change detection. This also takes advantage of the immutable state since when state updates, a new reference is created which triggers the onPush change detection automatically.
We settled on a smart / dumb component approach where certain components were display-only and others were connected with state. This simplified our code since we could test state management and presentation separately. We had one store per smart component, joining the two concepts seemed cleaner.
Inspired by the Angular input / output pattern, we decided to keep the stores hierarchically arranged. We had one root store which held the general order data, like user and session information. We then organized stores such that they were hierarchically under the orders component and store, which would grab bits of the order state and then use that, and pass it down to child components.