← Back to Chapters

NgRx Router Store

? NgRx Router Store

NgRx Angular Router Router Store

? Quick Overview

The NgRx Router Store integrates the Angular Router state into the global NgRx Store. This lets you select, react to, and test routing information (URL, params, query params, etc.) in a fully reactive way, instead of reaching for ActivatedRoute in every component.

By mirroring the router tree into the store, you can combine routing data with other feature states, drive navigation from Effects, and keep your application routing logic predictable and testable.

? Key Concepts

  • Router State in Store – Router information is stored under a dedicated slice (often router).
  • StoreRouterConnectingModule – Connects Angular Router with NgRx Store.
  • RouterReducerState – Describes the shape of the router slice held in the store.
  • Selectors – Used to derive the url, route params, and query params reactively.
  • Integration with Effects – Effects can listen to router changes or trigger navigation actions.

? Syntax & Setup

To enable Router Store, you need to:

  1. Register your root reducers with StoreModule.forRoot().
  2. Connect the router using StoreRouterConnectingModule.forRoot() in your root module.
? View Code Example
// app.module.ts - configure NgRx Router Store
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule } from '@ngrx/store';
import { StoreRouterConnectingModule } from '@ngrx/router-store';
import { AppComponent } from './app.component';
import { reducers } from './reducers';

@NgModule({
  imports: [
    BrowserModule,
    StoreModule.forRoot(reducers),
    StoreRouterConnectingModule.forRoot()
  ],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule {}

? Selecting Router State

Once Router Store is connected, the router slice is usually available as state.router. You can create selectors to expose exactly the data your components need (e.g., current URL, route params, or query params).

? View Code Example
// selectors/router.selectors.ts - select current URL from Router Store
import { createSelector } from '@ngrx/store';
import { RouterReducerState } from '@ngrx/router-store';

export const selectRouter = (state: any) => state.router;

export const selectCurrentUrl = createSelector(
  selectRouter,
  (router: RouterReducerState<any>) => router?.state?.url
);

You can build more advanced selectors for route params:

? View Code Example
// selectors/router.selectors.ts - select route param "id"
import { createSelector } from '@ngrx/store';
import { RouterReducerState } from '@ngrx/router-store';

export const selectRouter = (state: any) => state.router;

export const selectRouteParams = createSelector(
  selectRouter,
  (router: RouterReducerState<any>) => router?.state?.params
);

export const selectRouteId = createSelector(
  selectRouteParams,
  params => params?.['id']
);

? Using Router Store in Components

Components subscribe to router selectors through the Store. Because selectors return observables, the template can use the async pipe to render the latest router values.

? View Code Example
// app.component.ts - display current URL from Router Store
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { selectCurrentUrl } from './selectors/router.selectors';

@Component({
  selector: 'app-root',
  template: `<p>Current URL: {{ url$ | async }}</p>`
})
export class AppComponent {
  url$: Observable<string | undefined>;

  constructor(private store: Store) {
    this.url$ = this.store.select(selectCurrentUrl);
  }
}

?️ Live Output / Explanation

When the application starts and the user navigates to /products/42?sort=price, the selector returns that URL.

The template renders something like:

Current URL: /products/42?sort=price

As the user navigates, the observable emits new values, and the text updates automatically without manual subscriptions or cleanup.

✅ Tips & Best Practices

  • Use dedicated router selectors instead of exposing the whole router state. This keeps components simple and limits re-renders.
  • Combine router selectors (like selectRouteId) with entity selectors to fetch entities by ID directly from the store.
  • Trigger navigation from NgRx Effects after important events (e.g., redirect after successful login or form submit).
  • Keep router-related logic in selectors and effects, so your components remain focused on displaying data.
  • Write unit tests for router selectors to ensure critical routes and params are always resolved correctly.

? Try It Yourself

  • Create a selector to get route params (like id) and use it to select an entity from your NgRx Entity state by that ID.
  • Build a component that displays the current query params reactively using a selector and the async pipe.
  • Implement an NgRx Effect that, after a successful save action, navigates to a /success page using the router.
  • Add a debug component that prints the full router state from the store to understand how the router tree is represented.