In NgRx, what do provideStoreDevtools' maxAge and logOnly defaults mean for a production build, and how would you configure the DevTools there?
answer
- instrumentation runs without the extension
- unbounded history by default
- full time-travel features by default
- log-only still sends state
- maxAge 25, logOnly from isDevMode
basics
~20 sprovideStoreDevtools defaults to maxAge: false, an unbounded action and state history, and logOnly: false, full time travel and dispatch from the extension. In production, cap maxAge and set logOnly from isDevMode(), or leave the DevTools out of the bundle.
solid answer
~40 s`provideStoreDevtools` installs NgRx's recording instrumentation into the store whether or not the extension is present. Its defaults are `maxAge: false`, so every action and state snapshot is kept, and `logOnly: false`, so a user with the extension can jump, skip, lock and dispatch actions into the app. For production I use what `ng add` writes, `maxAge: 25` and `logOnly: !isDevMode()`, plus `autoPause: true` and a `stateSanitizer` for personal data. `logOnly` only narrows the features to pause, export and test; state is still sent. If nobody inspects production stores, I keep the DevTools in an environment file so the production bundle has none. Note that a `maxAge` of 1 throws at start-up.
code
ts · 22 lines// environments/environment.ts (development)
import { provideStoreDevtools } from '@ngrx/store-devtools';
export const environment = {
production: false,
providers: [provideStoreDevtools({ maxAge: 25 })],
};
// environments/environment.prod.ts (production)
export const environment = {
production: true,
providers: [],
};
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideStore } from '@ngrx/store';
import { environment } from '../environments/environment';
export const appConfig: ApplicationConfig = {
providers: [provideStore(), environment.providers],
};go deeper
Recall that provideStoreDevtools connects the store to the Redux DevTools extension, and that ng add configures maxAge: 25 and logOnly: !isDevMode().
Explain the maxAge and logOnly defaults, what log-only mode disables, and why a maxAge below 2 throws.
Show a production decision: capped, log-only and sanitized DevTools, or none in the bundle, justified by memory, exposure of state and support needs.
Treat DevTools in production as a support and data-exposure policy, decided once for all apps rather than per team.
## What `provideStoreDevtools` installs `provideStoreDevtools(options)` from `@ngrx/store-devtools` connects the NgRx store to the **Redux DevTools browser extension**. It does more than open a connection: it installs NgRx's own **instrumentation** between the actions stream and the state, a lifted reducer that records every action and the state it produced. That record is what the extension's history, time travel and export features read. The instrumentation is installed by the provider itself. Registering `provideStoreDevtools` in a production build therefore puts the recording code in every user's session, whether or not the user has the extension. ## The defaults, from the source | Option | Default | What the default means | |---|---|---| | `maxAge` | `false` | no limit: every action and computed state is kept | | `logOnly` | `false` | all extension features on, including jump, skip, lock, dispatch and import | | `autoPause` | `false` | recording continues even when the extension window is closed | | `trace` / `traceLimit` | `false` / `75` | no stack traces; 75 frames when enabled | | `connectInZone` | `false` | the extension connects outside Angular's zone | | `name` | `'NgRx Store DevTools'` | instance name shown in the monitor | Two defaults matter for production: - **`maxAge: false`** means the history is never trimmed. In a long-lived catalogue tab where a reader keeps changing genres and adding to a wishlist, every action and a full state snapshot per action stays in memory. - **`logOnly: false`** means a user with the extension can jump between past states, skip actions, lock the store and **dispatch arbitrary actions** into the running app. One validation to know: a `maxAge` of **1** throws at start-up with "Devtools 'maxAge' cannot be less than 2". Any number from 2 up, or `false`, is accepted. ## What `logOnly: true` changes, and what it does not With `logOnly: true`, NgRx narrows the extension's feature set to **pause, export and test**. Jumping, skipping, reordering, locking, importing and dispatching from the extension are gone. One precedence rule applies: an explicit `features` object in the options wins over `logOnly`, so passing both re-enables whatever `features` lists. What `logOnly` does **not** do: - it does not stop actions and state being **sent** to the extension, so a user who installs it can still read the store; - it does not remove the package from the bundle; - it does not cap memory; that is still `maxAge`. ## A production-ready configuration The configuration `ng add @ngrx/store-devtools` writes is the usual starting point, `maxAge: 25` with `logOnly: !isDevMode()`, and a production setup adds a few decisions: ```ts import { ApplicationConfig, isDevMode } from '@angular/core'; import { provideStore } from '@ngrx/store'; import { provideStoreDevtools } from '@ngrx/store-devtools'; export const appConfig: ApplicationConfig = { providers: [ provideStore(), provideStoreDevtools({ maxAge: 25, logOnly: !isDevMode(), autoPause: true, stateSanitizer: (state) => ({ ...state, account: undefined }), }), ], }; ``` 1. **Cap `maxAge`** so the history is bounded. 2. **Set `logOnly` from `isDevMode()`** so production users cannot time-travel or dispatch. 3. **Turn on `autoPause`**, which pauses recording while the extension window is closed. 4. **Sanitize** with `stateSanitizer` and `actionSanitizer` if a slice or payload holds personal data. ## Or leave it out of the production bundle The NgRx docs describe a stricter option: keep `provideStoreDevtools` in an environment file that is swapped for a production file without it, and spread `environment.providers` into the application config. The production bundle then carries no instrumentation at all. Which one to choose is a team decision: - **Exclude** when support never inspects production stores, or state is sensitive. - **Keep, log-only** when reproducing user reports from an exported action log is worth the cost, and the state has been sanitized. ## Why actions matter here The DevTools log is a list of action types. The configuration decides how much of that list is kept and who can act on it, but only action hygiene decides whether it reads as a story: `[Catalogue Page] Opened`, `[Genre Filter] Genre Changed`, `[Books API] Books Loaded Success`. ## Checking a deployed build To confirm what production actually ships, open the deployed app with the extension installed: if the NgRx instance appears at all, the instrumentation is in the bundle; if the jump and dispatch controls are available, `logOnly` is off. Both are quick checks worth adding to a release checklist. A strong answer names both defaults, says what `logOnly` really restricts, and picks between a capped log-only setup and excluding the DevTools from production.
- Does logOnly: true stop production users from reading the store's state?No. It narrows the extension's features to pause, export and test, so users cannot jump, skip or dispatch, but actions and state are still sent to the extension. To hide data, sanitize it with `stateSanitizer` and `actionSanitizer`, or keep the DevTools out of the production bundle.
- Why does provideStoreDevtools({ maxAge: 1 }) fail at start-up?NgRx validates the config when the provider is created and throws "Devtools 'maxAge' cannot be less than 2" for any truthy value below 2. Use `false` for no limit, or a number of 2 or more; the `ng add` default of 25 is a common choice.
saying these in an interview costs you the question
- Without the browser extension installed, the DevTools code does nothing.
- logOnly: true stops the store's state being sent to the extension.
- maxAge defaults to 25, so memory is bounded out of the box.
- autoPause is on by default, so recording stops when the extension is closed.
- logOnly removes the DevTools package from the production bundle.