
Redux DevTools: Install, Set Up and Debug Your Store (Best Practices)
Redux DevTools is a free browser extension that shows every action your Redux store dispatches, the state before and after, and a diff, and lets you jump back to any earlier state ("time travel"). Install the Redux DevTools Chrome extension (or the Firefox add-on). If you create your store with Redux Toolkit's configureStore, it connects automatically, with no code changes (Redux docs).
Install the Redux DevTools extension
| Browser | Where to get it |
|---|---|
| Chrome, Brave, Arc, other Chromium browsers | Chrome Web Store |
| Microsoft Edge | Install the Chrome Web Store version (Edge allows Chrome extensions) |
| Firefox | Mozilla Add-ons |
| Safari, React Native, Node | No extension; use the remote setup below |
After installing, open your app, open the browser's developer tools (F12 or Cmd+Option+I), and select the Redux tab. You can also click the toolbar icon for a popup. If the tab says "No store found", the app isn't connected; see the setup sections below.
Set up with Redux Toolkit (the default)
Redux Toolkit is the officially recommended way to write Redux. Its configureStore has a devTools option that defaults to true, so this is all you need:
import { configureStore } from '@reduxjs/toolkit'
import todosReducer from './todosSlice'
export const store = configureStore({
reducer: { todos: todosReducer },
})
Pass an object instead of true to configure the extension (options reference):
export const store = configureStore({
reducer: { todos: todosReducer },
devTools: process.env.NODE_ENV !== 'production' && {
name: 'Todo app',
maxAge: 100,
actionsDenylist: ['ui/mouseMoved'],
},
})
When DevTools is enabled, configureStore also turns on action stack traces in development builds, so you can see which line of code dispatched each action.
Set up with plain Redux (createStore)
Older codebases using createStore must wire the extension in manually. The recommended way is the @redux-devtools/extension npm package. It replaced the old redux-devtools-extension package, which is deprecated.
npm install @redux-devtools/extension
import { createStore, applyMiddleware } from 'redux'
import { thunk } from 'redux-thunk'
import { composeWithDevTools } from '@redux-devtools/extension'
import rootReducer from './reducers'
const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(thunk)),
)
Without the package, use the global the extension injects, falling back to Redux's compose when the extension isn't installed:
import { createStore, applyMiddleware, compose } from 'redux'
import { thunk } from 'redux-thunk'
import rootReducer from './reducers'
const composeEnhancers =
(typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk)))
window.devToolsExtension, which still shows up in old tutorials, was replaced by these names years ago (extension README).
Using Redux DevTools to debug
- Inspector: the action list on the left. Click an action to see its payload (Action), the full state after it (State), and what changed (Diff). Diff is usually the fastest way to spot a reducer bug.
- Time travel: use the slider at the bottom, or Jump on any action, to put the app back into that state. Skip removes an action from history and recomputes the state without it, which helps answer "what if this action never fired?"
- Trace: shows the call stack that dispatched the selected action. Click a frame to open the source.
- Dispatcher: dispatch a hand-written action, such as
{ "type": "todos/added", "payload": "test" }, to test a reducer without touching the UI. - Persist: keeps the action history across page reloads, useful for bugs that happen during startup.
- Export/Import: save the action history to a JSON file and load it on another machine. Attach it to a bug report so a teammate can replay exactly what you saw.
Options worth knowing
All options are documented in the arguments reference.
| Option | Default | Use it to |
|---|---|---|
name |
document.title |
Tell stores apart when a page has several |
maxAge |
50 |
Keep more (or fewer) actions in history; more costs memory |
trace |
false (RTK: on in development) |
Record the stack trace for each action |
traceLimit |
10 |
Limit stack depth |
actionsDenylist |
none | Hide noisy actions by type (strings are treated as regex) |
actionsAllowlist |
none | Show only matching actions; overrides the denylist |
predicate |
none | (state, action) => boolean for custom filtering |
actionSanitizer, stateSanitizer |
none | Replace large or secret values before they reach the extension |
serialize |
JSON.stringify |
Handle Map, Set, dates, Immutable.js structures |
features |
all on | Turn off specific features, such as jump or dispatch |
Filtering high-frequency actions like scroll, mouse move, or timer ticks with actionsDenylist keeps the action list readable and the extension fast.
Best practices
- Keep state serializable. DevTools serializes state to show and replay it. Class instances, functions, and Promises in state display badly and break time travel. Redux Toolkit's serializability check warns you about this in development.
- Name actions by domain and event.
todos/addedfilters and reads better thanADD.createSlicedoes this for you. - Sanitize big or sensitive data. Large blobs (file contents, huge lists) slow the extension. Tokens and personal data in state are visible to anyone with DevTools open. Use
stateSanitizerandactionSanitizer:
devTools: {
stateSanitizer: (state) =>
state.auth ? { ...state, auth: { ...state.auth, token: '<redacted>' } } : state,
}
- Decide on production deliberately. Because
devToolsdefaults totrue, a Redux Toolkit app exposes its state history in production to any visitor who has the extension installed. Most apps should setdevTools: process.env.NODE_ENV !== 'production', which is what the Redux docs' full example does. If you do keep it on in production for support, sanitize state and consider thefeaturesoption to disabledispatch,jump, andimport. - Raise
maxAgeonly when needed. Long sessions with a highmaxAgeuse a lot of memory.
Redux DevTools in Safari and React Native
There's no Safari extension. For Safari, React Native, or any other environment, use @redux-devtools/remote with the @redux-devtools/cli server:
import { createStore } from 'redux'
import { devToolsEnhancer } from '@redux-devtools/remote'
const store = createStore(
rootReducer,
devToolsEnhancer({ realtime: true, hostname: 'localhost', port: 8000 }),
)
Run the CLI on the same port, then open its monitor (or click Remote in the browser extension) to see the store. Remote monitoring is on by default only when NODE_ENV is development.
For other debugging and state tools, see browser extensions on DevHunt and frameworks and libraries. For catching Redux errors that happen to real users, monitoring tools cover what DevTools can't.
FAQ
Is Redux DevTools free?
Yes. The extension and all @redux-devtools/* packages are open source under the MIT license, maintained in the reduxjs/redux-devtools repository.
Why does Redux DevTools say "No store found"?
The page has no store connected. Check that you use configureStore (with devTools not set to false), or that you added composeWithDevTools or __REDUX_DEVTOOLS_EXTENSION_COMPOSE__ to createStore. Then reload the page with DevTools open. A production build with devTools disabled also shows this.
What happened to the redux-devtools-extension npm package?
It moved to @redux-devtools/extension. The API is the same (composeWithDevTools, devToolsEnhancer), so switching is an import change. Redux Toolkit users need neither package.
Does Redux DevTools work with Zustand or other state libraries?
Several libraries integrate with the same extension. Zustand has a devtools middleware that connects to it, for example. The features available depend on the library's integration.
Should I disable Redux DevTools in production?
Usually yes: set devTools: process.env.NODE_ENV !== 'production' in configureStore. Otherwise anyone with the extension can read your app's state and action history.