NgRx DevTool Debugger
Skill by ara.so — Devtools Skills collection.
NgRx DevTool is a comprehensive debugging and visualization tool for NgRx state management in Angular applications. It provides real-time action monitoring, effect tracking, state visualization with diff viewer, and performance metrics without requiring browser extensions. The tool runs a separate WebSocket server that your Angular app connects to, displaying all NgRx activity in a dedicated UI.
Installation
Install the package as a development dependency:
npm install --save-dev @amadeus-it-group/ngrx-devtool
Or with yarn:
yarn add -D @amadeus-it-group/ngrx-devtool
Basic Setup
1. Configure Your Angular Application
Add the DevTool provider and meta-reducer to your application configuration:
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideStore } from '@ngrx/store';
import { provideEffects } from '@ngrx/effects';
import {
provideNgrxDevTool,
createDevToolMetaReducer
} from '@amadeus-it-group/ngrx-devtool';
import * as fromRoot from './store/reducers';
import { AppEffects } from './store/effects/app.effects';
export const appConfig: ApplicationConfig = {
providers: [
provideStore(
fromRoot.reducers,
{
metaReducers: [createDevToolMetaReducer()]
}
),
provideEffects([AppEffects]),
provideNgrxDevTool({
wsUrl: 'ws://localhost:4000',
trackEffects: true,
enabled: true
})
]
};
2. Module-Based Configuration (Legacy)
If using NgModule instead of standalone components:
// app.module.ts
import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import {
NgRxDevToolModule,
createDevToolMetaReducer
} from '@amadeus-it-group/ngrx-devtool';
import { reducers } from './store/reducers';
import { AppEffects } from './store/effects/app.effects';
@NgModule({
imports: [
StoreModule.forRoot(reducers, {
metaReducers: [createDevToolMetaReducer()]
}),
EffectsModule.forRoot([AppEffects]),
NgRxDevToolModule.forRoot({
wsUrl: 'ws://localhost:4000',
trackEffects: true
})
]
})
export class AppModule {}
CLI Commands
Start the DevTool Server
The primary command to launch both the WebSocket server and UI:
npx ngrx-devtool
This starts:
- WebSocket server on
ws://localhost:4000 - UI server on
http://localhost:3000
Custom Port Configuration
# Change WebSocket port
npx ngrx-devtool --ws-port 5000
# Change UI port
npx ngrx-devtool --ui-port 8080
# Custom ports for both
npx ngrx-devtool --ws-port 5000 --ui-port 8080
Server Only Mode
Run only the WebSocket server without the UI:
npx ngrx-devtool --server-only
Configuration Options
DevTool Configuration Interface
interface NgRxDevToolConfig {
// WebSocket server URL (default: 'ws://localhost:4000')
wsUrl?: string;
// Enable/disable the devtool (default: true in development)
enabled?: boolean;
// Track NgRx effects (default: true)
trackEffects?: boolean;
// Maximum number of actions to store (default: 100)
maxActions?: number;
// Automatically connect on initialization (default: true)
autoConnect?: boolean;
// Reconnection attempts (default: 3)
reconnectAttempts?: number;
// Reconnection delay in ms (default: 1000)
reconnectDelay?: number;
}
Environment-Based Configuration
// app.config.ts
import { isDevMode } from '@angular/core';
import { provideNgrxDevTool, createDevToolMetaReducer } from '@amadeus-it-group/ngrx-devtool';
export const appConfig: ApplicationConfig = {
providers: [
provideStore(
reducers,
{
metaReducers: isDevMode() ? [createDevToolMetaReducer()] : []
}
),
provideNgrxDevTool({
wsUrl: `ws://${window.location.hostname}:4000`,
enabled: isDevMode(),
trackEffects: true,
maxActions: 200,
reconnectAttempts: 5
})
]
};
Production-Safe Configuration
// app.config.ts
import { environment } from './environments/environment';
export const appConfig: ApplicationConfig = {
providers: [
provideStore(
reducers,
{
metaReducers: environment.devToolEnabled
? [createDevToolMetaReducer()]
: []
}
),
provideNgrxDevTool({
wsUrl: environment.devToolWsUrl,
enabled: environment.devToolEnabled,
trackEffects: environment.devToolEnabled
})
]
};
// environments/environment.ts
export const environment = {
production: false,
devToolEnabled: true,
devToolWsUrl: 'ws://localhost:4000'
};
// environments/environment.prod.ts
export const environment = {
production: true,
devToolEnabled: false,
devToolWsUrl: ''
};
Usage Patterns
Monitoring Actions
Once configured, all dispatched actions are automatically tracked:
// user.actions.ts
import { createAction, props } from '@ngrx/store';
export const loadUsers = createAction('[User List] Load Users');
export const loadUsersSuccess = createAction(
'[User API] Load Users Success',
props<{ users: User[] }>()
);
export const loadUsersFailure = createAction(
'[User API] Load Users Failure',
props<{ error: string }>()
);
// Component dispatch - automatically tracked
this.store.dispatch(loadUsers());
Effect Tracking
Effects are automatically tracked when trackEffects: true:
// user.effects.ts
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, map, switchMap } from 'rxjs/operators';
import { of } from 'rxjs';
import * as UserActions from './user.actions';
import { UserService } from '../services/user.service';
@Injectable()
export class UserEffects {
// This effect will be tracked by NgRx DevTool
loadUsers$ = createEffect(() =>
this.actions$.pipe(
ofType(UserActions.loadUsers),
switchMap(() =>
this.userService.getUsers().pipe(
map(users => UserActions.loadUsersSuccess({ users })),
catchError(error => of(UserActions.loadUsersFailure({
error: error.message
})))
)
)
)
);
constructor(
private actions$: Actions,
private userService: UserService
) {}
}
State Visualization
The DevTool automatically captures state before and after each action, showing diffs:
// counter.reducer.ts
import { createReducer, on } from '@ngrx/store';
import * as CounterActions from './counter.actions';
export interface CounterState {
count: number;
lastUpdated: Date | null;
}
export const initialState: CounterState = {
count: 0,
lastUpdated: null
};
export const counterReducer = createReducer(
initialState,
on(CounterActions.increment, state => ({
...state,
count: state.count + 1,
lastUpdated: new Date()
})),
on(CounterActions.decrement, state => ({
...state,
count: state.count - 1,
lastUpdated: new Date()
})),
on(CounterActions.reset, () => initialState)
);
Custom Action Metadata
Add metadata to actions for better debugging:
// product.actions.ts
import { createAction, props } from '@ngrx/store';
export const addToCart = createAction(
'[Product] Add to Cart',
props<{
productId: string;
quantity: number;
metadata?: { source: string; timestamp: number }
}>()
);
// Dispatch with metadata
this.store.dispatch(addToCart({
productId: 'prod-123',
quantity: 2,
metadata: {
source: 'product-detail-page',
timestamp: Date.now()
}
}));
Advanced Configuration
Selective Action Tracking
Filter which actions to track by customizing the meta-reducer:
// app.config.ts
import { ActionReducer, Action } from '@ngrx/store';
import { createDevToolMetaReducer } from '@amadeus-it-group/ngrx-devtool';
function createFilteredDevToolMetaReducer() {
const devToolMetaReducer = createDevToolMetaReducer();
return (reducer: ActionReducer<any>) => {
const wrappedReducer = devToolMetaReducer(reducer);
return (state: any, action: Action) => {
// Skip tracking for high-frequency actions
if (action.type.includes('[Router]') ||
action.type.includes('[Internal]')) {
return reducer(state, action);
}
return wrappedReducer(state, action);
};
};
}
export const appConfig: ApplicationConfig = {
providers: [
provideStore(reducers, {
metaReducers: [createFilteredDevToolMetaReducer()]
}),
provideNgrxDevTool()
]
};
Multiple Environment Setup
// app.config.ts
const devToolConfig = (() => {
const hostname = window.location.hostname;
if (hostname === 'localhost') {
return { wsUrl: 'ws://localhost:4000', enabled: true };
} else if (hostname.includes('staging')) {
return { wsUrl: 'ws://staging-devtool.example.com:4000', enabled: true };
}
return { enabled: false };
})();
export const appConfig: ApplicationConfig = {
providers: [
provideNgrxDevTool(devToolConfig)
]
};
Troubleshooting
Connection Issues
Problem: DevTool UI shows "Disconnected" status
Solutions:
- Verify the DevTool server is running:
npx ngrx-devtool
- Check WebSocket URL matches server configuration:
provideNgrxDevTool({
wsUrl: 'ws://localhost:4000' // Must match server port
})
- Check browser console for connection errors:
WebSocket connection to 'ws://localhost:4000' failed: Connection refused
- Verify no firewall blocking WebSocket connections
Actions Not Appearing
Problem: Actions are dispatched but not showing in DevTool
Solutions:
- Ensure meta-reducer is registered:
provideStore(reducers, {
metaReducers: [createDevToolMetaReducer()] // Must be present
})
- Verify DevTool is enabled:
provideNgrxDevTool({
enabled: true // Check this is true
})
- Check for action filtering that might be excluding actions
Effects Not Tracked
Problem: Effects execute but don't appear in DevTool
Solutions:
- Enable effect tracking:
provideNgrxDevTool({
trackEffects: true // Must be true
})
- Ensure effects are properly registered:
provideEffects([UserEffects, ProductEffects])
- Verify effects use the
createEffect()function
Performance Issues
Problem: Application slows down with DevTool enabled
Solutions:
- Reduce action history limit:
provideNgrxDevTool({
maxActions: 50 // Default is 100
})
- Filter high-frequency actions:
// Use custom meta-reducer to skip router/polling actions
- Disable in production builds:
provideNgrxDevTool({
enabled: !environment.production
})
Port Conflicts
Problem: Port 4000 or 3000 already in use
Solutions:
- Use custom ports:
npx ngrx-devtool --ws-port 5000 --ui-port 8080
- Update configuration to match:
provideNgrxDevTool({
wsUrl: 'ws://localhost:5000'
})
State Diff Not Showing
Problem: State changes occur but diff viewer is empty
Solutions:
- Ensure reducer returns new state reference:
// ❌ Bad - mutates state
on(updateUser, (state, { user }) => {
state.user = user; // Don't do this
return state;
})
// ✅ Good - returns new state
on(updateUser, (state, { user }) => ({
...state,
user
}))
- Check state serialization for circular references
Browser Compatibility
The DevTool requires WebSocket support. All modern browsers support WebSockets, but if you encounter issues:
- Ensure browser is up to date
- Check corporate proxy/firewall settings
- Try different browser to isolate issue
- Check browser console for specific WebSocket errors