Frontend
·5 min read·↗

How to Debug NgRx Using Redux DevTools in Angular

Main cover illustration for article: How to Debug NgRx Using Redux DevTools in Angular
Summarize with AI:

When managing complex state in Angular applications, nothing drains productivity faster than guessing why a slice of state suddenly changed or why a selector emitted unexpected data.

Sprinkling console.log() across reducers and components is slow, creates clutter, and gives zero visibility into the historical sequence of dispatched actions.

With Redux DevTools and @ngrx/store-devtools, you get real-time inspection, action diffing, call-stack tracing, and instant time-travel debugging directly inside your browser.


Console.log vs. Redux DevTools: Why Tooling Matters ⚡

Featureconsole.log() DebuggingRedux DevTools with NgRx
Action HistoryManual logs, lost on page refreshComplete chronological timeline
State DiffsHard to compare deeply nested objectsColor-coded visual JSON tree & diff
Time TravelImpossibleStep backward/forward through actions
Action Stack TraceRequires manual breakpointsAutomatic file and line number tracing
Production SafetyHigh risk of leaking logsCleanly toggleable via isDevMode()

1. Installing @ngrx/store-devtools 📦

In modern Angular applications (Angular 17, 18, 19+), install the official store devtools package:

npm install @ngrx/store-devtools

2. Configuring provideStoreDevtools in Standalone Angular 🛠️

Angular uses standalone application configuration in app.config.ts. You configure devtools using provideStoreDevtools().

Here is the recommended production-ready configuration:

// app.config.ts
import { ApplicationConfig, isDevMode } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideStore } from '@ngrx/store';
import { provideStoreDevtools } from '@ngrx/store-devtools';
import { routes } from './app.routes';
import { homeReducer } from './pages/home/state/home.reducer';
 
export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideStore({
      home: homeReducer,
    }),
    provideStoreDevtools({
      maxAge: 25, // Retains last 25 states
      logOnly: !isDevMode(), // Restrict extension to log-only in production
      autoPause: true, // Pauses recording actions when window is not focused
      trace: true, // If set to true, will include stack trace for every action
      traceLimit: 25, // Maximum stack trace frames to capture
      connectInZone: true, // Ensures extension runs within Angular zone
    }),
  ],
};

[!TIP] Setting logOnly: !isDevMode() ensures your application state cannot be modified by users in production through the DevTools panel, while still retaining safe diagnostic capabilities.


3. Installing the Browser Extension 🔌

To view and interact with your store, install the Redux DevTools extension for your browser:

Once installed, start your development server:

npm run dev

Open Chrome DevTools (F12 or Cmd + Option + I) and click the Redux tab. You should see the initial @ngrx/store/init action logged immediately:


4. Dispatched Actions & State Reducers in Practice 🔄

Let's demonstrate how state changes are tracked through actions. Suppose we have a reducer managing terms acceptance:

// home.reducer.ts
import { createAction, createReducer, on } from '@ngrx/store';
import { initialState } from './home.state';
 
export const acceptTerms = createAction('[Home Page] Accept Terms');
export const rejectTerms = createAction('[Home Page] Reject Terms');
 
export const homeReducer = createReducer(
  initialState,
  on(acceptTerms, (state) => ({
    ...state,
    acceptTerms: true,
  })),
  on(rejectTerms, (state) => ({
    ...state,
    acceptTerms: false,
  })),
);

In our component, we dispatch actions and bind state using Angular Signals and control flow:

// home.component.ts
import { Component, inject } from '@angular/core';
import { Store } from '@ngrx/store';
import { acceptTerms, rejectTerms } from './state/home.actions';
import { selectAcceptTerms } from './state/home.selectors';
 
@Component({
  selector: 'app-home',
  standalone: true,
  template: `
    <div class="terms-card">
      @if (!acceptTerms()) {
        <h3>Do you agree to use NgRx for state management?</h3>
        <button class="btn btn-primary" (click)="onAccept()">
          Accept Terms
        </button>
      } @else {
        <h2>Thanks for choosing NgRx! 🎉</h2>
        <button class="btn btn-secondary" (click)="onReject()">
          Reject Terms
        </button>
      }
    </div>
  `,
})
export class HomeComponent {
  private store = inject(Store);
  acceptTerms = this.store.selectSignal(selectAcceptTerms);
 
  onAccept() {
    this.store.dispatch(acceptTerms());
  }
 
  onReject() {
    this.store.dispatch(rejectTerms());
  }
}

5. Tracing Actions and Time-Travel Debugging ⏱️

When you click the buttons in your UI, every action is captured in the DevTools timeline:

Key Tabs in Redux DevTools

  1. Action Tab: Inspects the dispatched action's payload and type.
  2. Diff Tab: Highlights exactly which keys in the store changed (green for additions, red for deletions).
  3. State Tab: Displays a navigable tree of the entire global state tree at that precise millisecond.
  4. Trace Tab: Click on any action to inspect the exact TypeScript stack trace leading back to home.component.ts:32.
  5. Time Travel Slider (Bottom Bar): Drag the slider backward or click Jump on an earlier action. The UI instantly updates to reflect that historical state without reloading!

Production Security Best Practices 🔒

Before deploying to production, follow these rules:

  1. Never expose raw devtools in production: Use logOnly: !isDevMode().
  2. Sanitize sensitive fields: If your state contains auth tokens, passwords, or PII, configure actionSanitizer and stateSanitizer:
provideStoreDevtools({
  maxAge: 25,
  logOnly: !isDevMode(),
  actionSanitizer: (action) => 
    action.type === '[Auth] Login Success' 
      ? { ...action, payload: { ...action.payload, token: '***REDACTED***' } } 
      : action,
})

Summary 🎯

Integrating Redux DevTools into your Angular and NgRx workflow transforms debugging from guesswork into a visual, deterministic science:

  • Configure once in app.config.ts with provideStoreDevtools.
  • Inspect actions, state mutations, and stack traces with zero console noise.
  • Replay tricky edge cases in seconds using time-travel debugging.

For more architectural best practices in Angular, explore our related guides:

Part of the Frontend Series

Modern frontend development — Angular, React, TypeScript, accessibility, and performance.

View Entire Series

Frequently Asked Questions

How do I set up Redux DevTools with modern standalone NgRx in Angular?

Import provideStoreDevtools from @ngrx/store-devtools and register it inside the providers array of your app.config.ts alongside provideStore. Configure options such as maxAge and logOnly: !isDevMode().

What does the time travel feature in Redux DevTools let you do?

Time travel lets you step backwards and forwards through every dispatched action in your application, replaying state mutations to reproduce bugs instantly without page reloads.

How should I configure NgRx Store DevTools for production builds?

Always set logOnly: !isDevMode() or pass maxAge: 25 conditionally to avoid exposing sensitive state data in the browser and eliminate runtime memory overhead in production.

Can Redux DevTools trace where an NgRx action was dispatched in code?

Yes. Setting trace: true and traceLimit: 25 in provideStoreDevtools captures the exact TypeScript stack trace pointing directly to the method that dispatched the action.

Related Articles

Share this article

If you found this guide helpful, consider sharing it with your team or fellow developers.


Real Software. Real Lessons.

I share the lessons I learned the hard way, so you can either avoid them or be ready when they happen.

User avatar
User avatar
User avatar
User avatar
+13K

Join 13,800+ developers and readers.

No spam ever. Unsubscribe at any time.