Cómo depurar NgRx usando Redux DevTools en Angular

Cuando gestiono estados complejos en aplicaciones de Angular, nada me quita más productividad que intentar adivinar por qué cambió una parte del estado de repente o por qué un selector emitió datos inesperados.
Llenar mis reducers y componentes de console.log() es lento, ensucia el código y no ofrece visibilidad sobre el historial de las acciones despachadas.
Con Redux DevTools y @ngrx/store-devtools, obtienes inspección en tiempo real, comparación visual de cambios (diffing), rastreo de la pila de llamadas y depuración con time travel directamente en tu navegador.
Console.log vs. Redux DevTools: por qué importan las herramientas ⚡
| Característica | Depuración con console.log() | Redux DevTools con NgRx |
|---|---|---|
| Historial de acciones | Logs manuales, se pierden al recargar la página | Línea de tiempo cronológica completa |
| Diferencias de estado | Difícil de comparar objetos anidados profundos | Árbol JSON visual con código de colores y diff |
| Time Travel | Imposible | Avanzar y retroceder a través de las acciones |
| Rastreo de la acción | Requiere breakpoints manuales | Rastreo automático de archivo y número de línea |
| Seguridad en producción | Alto riesgo de filtrar logs | Fácil de alternar mediante isDevMode() |
1. Instalación de @ngrx/store-devtools 📦
En aplicaciones modernas de Angular (Angular 17, 18, 19+), instalo el paquete oficial de las devtools del store con este comando:
npm install @ngrx/store-devtools2. Configurar provideStoreDevtools en Angular Standalone 🛠️
Angular utiliza la configuración de aplicaciones standalone en app.config.ts. Para habilitar las devtools, uso provideStoreDevtools().
Esta es la configuración recomendada y lista para producción que suelo utilizar:
// 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, // Mantiene los últimos 25 estados
logOnly: !isDevMode(), // Restringe la extensión a solo lectura en producción
autoPause: true, // Pausa la grabación de acciones cuando la ventana pierde el foco
trace: true, // Si es true, incluye el stack trace para cada acción
traceLimit: 25, // Límite de llamadas en el stack trace a capturar
connectInZone: true, // Asegura que la extensión corra dentro de la zona de Angular
}),
],
};[!TIP] Configurar
logOnly: !isDevMode()asegura que los usuarios no puedan modificar el estado de tu aplicación en producción mediante el panel de DevTools, manteniendo al mismo tiempo capacidades seguras de diagnóstico.
3. Instalar la extensión del navegador 🔌
Para ver tu store e interactuar con él, instala la extensión de Redux DevTools en tu navegador:
Una vez instalada, inicia tu servidor de desarrollo:
npm run devAbre las Chrome DevTools (F12 o Cmd + Option + I en macOS) y haz clic en la pestaña Redux. Verás la acción inicial @ngrx/store/init registrada de inmediato:

4. Acciones despachadas y reducers en la práctica 🔄
Veamos cómo se rastrean los cambios de estado a través de las acciones. Supongamos que tengo un reducer que gestiona la aceptación de términos:
// 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,
})),
);En mi componente, despacho las acciones y conecto el estado usando Signals de Angular y el nuevo 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. Rastreo de acciones y depuración con Time Travel ⏱️
Cuando haces clic en los botones de la interfaz, cada acción queda registrada en la línea de tiempo de DevTools:

Pestañas clave en Redux DevTools
- Pestaña Action: Inspecciona el tipo y la carga útil (payload) de la acción despachada.
- Pestaña Diff: Resalta con exactitud qué propiedades del store cambiaron (verde para adiciones, rojo para eliminaciones).
- Pestaña State: Muestra un árbol interactivo con el estado global completo en ese milisegundo exacto.
- Pestaña Trace: Haz clic en cualquier acción para ver la pila de llamadas (stack trace) de TypeScript que apunta directo a
home.component.ts:32. - Barra de Time Travel (barra inferior): Arrastra el control deslizante hacia atrás o haz clic en Jump en una acción anterior. La interfaz se actualiza de inmediato para reflejar ese estado histórico sin necesidad de recargar la página.
Buenas prácticas de seguridad para producción 🔒
Antes de desplegar a producción, te recomiendo seguir estas reglas:
- Nunca expongas las devtools completas en producción: Usa
logOnly: !isDevMode(). - Limpia campos sensibles: Si tu estado maneja tokens de autenticación, contraseñas o datos personales sensibles, configura
actionSanitizerystateSanitizer:
provideStoreDevtools({
maxAge: 25,
logOnly: !isDevMode(),
actionSanitizer: (action) =>
action.type === '[Auth] Login Success'
? { ...action, payload: { ...action.payload, token: '***REDACTED***' } }
: action,
})Resumen 🎯
Integrar Redux DevTools en tu flujo de trabajo con Angular y NgRx cambia por completo la experiencia de depuración, dejando atrás las suposiciones:
- Configúralo una sola vez en
app.config.tsconprovideStoreDevtools. - Inspecciona acciones, mutaciones de estado y pilas de llamadas sin llenar tu consola de ruido.
- Reproduce casos límite complicados en segundos usando time-travel debugging.
Si quieres conocer más buenas prácticas de arquitectura en Angular, revisa mis guías relacionadas:
Modern frontend development — Angular, React, TypeScript, accessibility, and performance.
Preguntas Frecuentes
¿Cómo configuro Redux DevTools con NgRx standalone en Angular moderno?
Importa provideStoreDevtools desde @ngrx/store-devtools y regístralo dentro del arreglo providers de tu archivo app.config.ts junto a provideStore. Configura opciones clave como maxAge y logOnly: !isDevMode().
¿Qué te permite hacer la función de time travel en Redux DevTools?
Time travel te permite avanzar y retroceder por cada acción despachada en tu aplicación, reproduciendo los cambios de estado para reproducir errores al instante sin recargar la página.
¿Cómo debo configurar NgRx Store DevTools para compilaciones de producción?
Configura siempre logOnly: !isDevMode() o define maxAge: 25 de forma condicional para evitar exponer datos sensibles del estado en el navegador y eliminar el consumo innecesario de memoria en producción.
¿Puede Redux DevTools rastrear en qué parte del código se despachó una acción de NgRx?
Sí. Al configurar trace: true y traceLimit: 25 en provideStoreDevtools, se captura la pila de llamadas exacta de TypeScript, apuntando directamente al método que disparó la acción.
Artículos Relacionados
Cómo crear formularios dinámicos en Angular: Guía paso a paso
Aprende a construir formularios dinámicos tipados y basados en datos en Angular moderno usando Formularios Reactivos, Control Flow (@switch, @for) y validaciones.
Cómo combinar Async Pipes en Angular: De combineLatest a Signals y @let
Aprende cómo evitar múltiples async pipes y suscripciones duplicadas en plantillas de Angular usando combineLatest, Signals modernas (toSignal) y la sintaxis @let.
Cuándo y por qué usar REDUX NgRx en Angular
En un artículo anterior escribí sobre cómo manejar el estado en Angular usando servicios, pero cuando la aplicación crece y necesita gestionar un estado grande, se vuelve complejo de mantener. Te cuento cuándo y por qué conviene usar NgRx.
Cómo manejar efectos secundarios en Angular usando NgRx Effects
Los efectos secundarios son habituales en nuestras aplicaciones. Aprende a manejarlos en Angular con NgRx Effects para evitar sobrecargar tus componentes y mantener tu estado limpio y predecible.
Compartir este artículo
Si esta guía te resultó útil, compártela con tu equipo o con otros desarrolladores.
Software real. Lecciones reales.
Comparto las lecciones que aprendí por las malas, para que puedas evitarlas o estar preparado cuando ocurran.
Únete a 13,800+ desarrolladores y lectores.
Cero spam. Cancela tu suscripción en cualquier momento.