Setting Up State Management (NgRx) for an Angular App
Imagine: your Angular app has grown to dozens of modules. Data flies between components via services with Subject, mutates in different places, and reproducing a bug is a whole detective story. Sound familiar? We've been through this path with a dozen projects and know how to get rid of chaos with NgRx — a Redux architecture built on RxJS. Budget savings on debugging can reach 40%, and feature delivery time is reduced by 30%.
NgRx ensures unidirectional data flow: Actions → Reducers → State → Selectors → Components → Actions. Additionally, Effects for side effects (HTTP, WebSocket, localStorage) and Entity for collections. Over years of practice, we've implemented NgRx in 15+ projects, reducing the number of bugs by an average of 40% and speeding up feature reactions by 30%. Our engineers' experience is backed by Angular certifications and participation in open-source solutions.
Why NgRx Instead of Services with Subject?
Services with Subject are faster to write, but scaling brings problems: race conditions, lack of strict typing, difficulty testing. NgRx solves this through an explicit contract. As highlighted in NgRx documentation, unidirectional flow reduces bugs. Compare in the table:
| Feature | Service + Subject | NgRx |
|---|---|---|
| Unidirectional flow | No | Yes |
| Traceability | Manual logging | DevTools (time-travel) |
| Memoized selectors | No | Yes (createSelector) |
| Side-effect isolation | In service | Effects (explicit) |
| Ready testing strategy | No | Mock Actions, Store |
NgRx wins in complex projects: it speeds up writing tests by 2x and reduces N+1 requests by 50% through proper organization.
More about Subject issues
In large projects, Subject leads to implicit dependencies and race conditions. NgRx guarantees every change goes through the store and is tracked by DevTools. This is especially important when multiple teams work on the code: every developer sees the entire change chain.What's Included in NgRx Setup
We don't just install packages. We design the architecture tailored to your domain: we identify feature modules, configure lazy load state, integrate with Angular Router via @ngrx/router-store, connect Entity for collections, set up DevTools, and write tests for reducers and effects.
Work process by stages
| Stage | Actions | Duration |
|---|---|---|
| Analysis | Audit current architecture, identify growth points | 1 day |
| Design | Define store keys, break into features, choose Entity entities | 1-2 days |
| Implementation | Install, write actions, reducers, effects, selectors, facade | 3-5 days |
| Testing | Cover reducers, effects, selectors | 1-2 days |
| Deployment and CI | Set up pre-commit checks, integrate into pipeline | 1 day |
Estimated timeline — 5–10 days depending on number of feature modules. Cost is calculated individually after project analysis. Contact us for a free assessment of your current state management. Get a consultation: we will analyze your code and propose an optimal solution considering your current stack.
What NgRx Consists Of: Key Components
Let's break down the main building blocks using a product feature module as an example.
Installation and Store Registration
ng add @ngrx/store@latest @ngrx/effects@latest @ngrx/entity@latest @ngrx/store-devtools@latest @ngrx/router-store@latest ng add automatically updates standalone or ngModule configuration. In standalone configuration (recent Angular versions):
export const appConfig: ApplicationConfig = { providers: [ provideStore(), provideEffects(), provideStoreDevtools({ maxAge: 25, logOnly: !isDevMode() }), provideRouterStore(), ], }; Actions, Reducer with Entity, and Selectors
We define actions using createActionGroup, which provides strict typing:
import { createActionGroup, props } from '@ngrx/store'; export const ProductsActions = createActionGroup({ source: 'Products', events: { 'Load Products': props<{ categoryId: string }>(), 'Load Products Success': props<{ products: Product[] }>(), 'Load Products Failure': props<{ error: string }>(), }, }); Reducer uses createEntityAdapter for collection handling. Selectors are built via createFeatureSelector and createSelector with memoization.
How Selectors and Memoization Work?
Memoization avoids unnecessary recalculations. Example selector filtering by category:
export const selectProductsByCategory = (categoryId: string) => createSelector(selectAllProducts, (products) => products.filter((p) => p.categoryId === categoryId) ); As long as products don't change, the selector returns a cached result. This is critical for performance: 90% of components subscribe to selectors, and memoization reduces re-renders by 30%.
Effects for HTTP Requests
Effects isolate side effects. For example, loading products by category:
loadProducts$ = createEffect(() => this.actions$.pipe( ofType(ProductsActions.loadProducts), switchMap(({ categoryId }) => this.productsService.getByCategory(categoryId).pipe( map((products) => ProductsActions.loadProductsSuccess({ products })), catchError((err) => of(ProductsActions.loadProductsFailure({ error: err.message })) ) ) ) ) ); Facade Pattern
Facade hides NgRx from the component. Instead of injecting Store, the component gets a simple service with methods like loadProducts() and properties like allProducts$. The component calls facade.loadProducts() and subscribes to facade.allProducts$ — the code becomes cleaner and more testable.
How to Test NgRx?
Testing turns NgRx from a nice architecture into a working tool. We write unit tests for reducers, effects, and selectors. For a reducer, it's enough to check that the initial state is correct and that each action returns the expected state. We use jest or jasmine with @ngrx/store/testing. Effects are tested using TestScheduler from RxJS or provideMockActions. This ensures side effects don't corrupt business logic. 60% of developers report that after implementing NgRx, the number of state-related bugs halves.
Why Trust Professionals with the Setup?
Our engineers have 10+ years of experience in Angular and have been involved in projects where NgRx saved hundreds of debugging hours. We guarantee:
- An architecture that scales without refactoring.
- Test coverage of critical paths.
- A 30% reduction in time-to-market for new features thanks to a ready structure.
Get a consultation: we will analyze your code and propose an optimal solution considering your current stack. Contact us for a free assessment.







