---
title: "How to Debug NgRx Using Redux DevTools in Angular"
date: "2024-05-29"
slug: "how-to-debug-ngrx-using-redux-devtools"
author: "Dany Paredes"
canonical: "https://danywalls.com/how-to-debug-ngrx-using-redux-devtools"
description: "Learn how to debug NgRx state in modern Angular with Redux DevTools: configure provideStoreDevtools, trace actions, inspect diffs, and use time-travel debugging."
---


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 ⚡

| Feature | `console.log()` Debugging | Redux DevTools with NgRx |
|---|---|---|
| **Action History** | Manual logs, lost on page refresh | Complete chronological timeline |
| **State Diffs** | Hard to compare deeply nested objects | Color-coded visual JSON tree & diff |
| **Time Travel** | Impossible | Step backward/forward through actions |
| **Action Stack Trace** | Requires manual breakpoints | Automatic file and line number tracing |
| **Production Safety** | High risk of leaking logs | Cleanly toggleable via `isDevMode()` |

---

## 1. Installing @ngrx/store-devtools 📦

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

```bash
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:

```typescript
// 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:
- [Redux DevTools for Chrome](https://chromewebstore.google.com/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd)
- [Redux DevTools for Firefox](https://addons.mozilla.org/en-US/firefox/addon/reduxdevtools/)

Once installed, start your development server:

```bash
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:

![](/images/posts/how-to-debug-ngrx-using-redux-devtools/64e21383-0b95-4_73561.gif)

---

## 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:

```typescript
// 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:

```typescript
// 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:

![](/images/posts/how-to-debug-ngrx-using-redux-devtools/7832c628-48e1-4_73561.gif)

### 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`:

```typescript
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:
- [Creating Dynamic Forms in Angular: A Step-by-Step Guide](/creating-dynamic-forms-in-angular-a-step-by-step-guide)
- [Combine Async Pipes in Angular: From combineLatest to Signals and @let](/combine-async-pipes-in-angular-how-to-avoid-multiple-pipes)
- [Understanding When and Why to Implement NgRx in Angular](/understanding-when-and-why-to-implement-ngrx-in-angular)

