React Developer Tools
React Developer Tools is a free browser extension that turns the abstract component tree in your head into something you can actually click through. Instead of sprinkling console.log calls to figure out what props a component received or why it keeps re-rendering, you open a panel, select the component, and read its props, state, and hooks live. It also ships a Profiler that records exactly how long each render took and what triggered it. This page tours both panels and shares the practical tips that make day-to-day debugging faster.
Installing the extension
React DevTools is published by the React team and adds two new tabs to your browser’s developer tools: Components and Profiler.
| Browser | Where to get it |
|---|---|
| Chrome / Edge / Brave | Chrome Web Store — search “React Developer Tools” |
| Firefox | Firefox Add-ons — search “React Developer Tools” |
| Safari / Electron / other | npm i -g react-devtools then run the standalone app |
For non-Chromium browsers or for debugging React Native and Electron apps, install the standalone version and connect to it:
npm install -g react-devtools
react-devtools
Then add this script tag before any other scripts in your HTML so the running app connects to the standalone window:
<script src="http://localhost:8097"></script>
Tip: When you open DevTools on a site built with React, the React tab icon in your browser toolbar turns colored. A blue or red icon means React is running and in development mode; a faded icon means no React was detected on the page.
The Components panel
The Components tab shows your live component tree. Click any node and the right-hand pane displays its current props, state, and hooks. Editing a value there updates the running app immediately — great for testing edge cases without changing code.
Consider this small component:
import { useState } from "react";
function Greeting({ name, excited }) {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount((c) => c + 1)}>
Hi {name}{excited ? "!" : ""} — clicked {count}×
</button>
);
}
export default function App() {
return <Greeting name="Ada" excited />;
}
Selecting Greeting in the Components panel reveals its inspectable state:
Output:
Greeting
props
name: "Ada"
excited: true
hooks
State: 0 ← from useState(0)
A few things worth knowing:
- Hooks are listed in order. They show up as
State,Effect,Memo,Callback,Ref, etc. You can give a custom hook a readable label withuseDebugValueso it appears with context. - The search box at the top filters the tree by component name — invaluable in a large app.
- The eye icon inspects the matching DOM node in the Elements tab; the
< >icon jumps to the component’s source in the Sources tab. - Right-click a component to “Store as global variable” (
$r), then call methods or read props from the console.
Highlighting re-renders
A common performance question is “why did this update?” Open the Components panel, click the gear (settings) icon, and under the General section enable Highlight updates when components render. Now every component flashes a colored border each time it re-renders — green for cheap, frequent updates and yellow-to-red as render frequency climbs.
If you type in an input and the entire page flashes, you have found an over-rendering problem: a state change near the root is forcing unrelated subtrees to re-render. That is your cue to lift state down, memoize, or split components.
Warning: Highlighting is a development-only diagnostic. Always confirm a fix with the Profiler’s measured timings — a flash tells you that something rendered, not whether it was actually slow.
The Profiler panel
The Profiler measures renders instead of just showing them. Click the record button (●), interact with your app, then stop recording. DevTools groups the work into commits — each commit is one batch of DOM updates React flushed to the screen.
Two views help you read the data:
- Flamegraph — every committed component is a bar; width is render duration. Grey bars did not re-render in that commit, colored bars did, and the color intensity maps to how long each took.
- Ranked — the same commit sorted slowest-first, so the most expensive component is at the top.
Select a component in either view and the side panel shows why it rendered: “Props changed (items)”, “Hook 1 changed”, “Parent component rendered”, or “The first mount”. This single feature usually pinpoints the cause of a slow interaction in seconds.
To get the “why did this render” reasons, enable Record why each component rendered while profiling in the Profiler settings (gear icon) before recording.
You can wrap a region of your tree in a <Profiler> to capture timings programmatically too:
import { Profiler } from "react";
function onRender(id, phase, actualDuration) {
console.log(`${id} (${phase}) took ${actualDuration.toFixed(2)}ms`);
}
export default function App() {
return (
<Profiler id="Sidebar" onRender={onRender}>
<Sidebar />
</Profiler>
);
}
Output:
Sidebar (mount) took 4.81ms
Sidebar (update) took 0.92ms
The phase is "mount", "update", or "nested-update", and actualDuration is how long React spent rendering that subtree for the commit.
Best Practices
- Install the extension in your everyday browser so you can inspect any React site, not just your own.
- Use the Components panel to verify props and state before reaching for
console.log— it is faster and always current. - Turn on “Highlight updates” early when chasing performance issues to spot over-rendering at a glance.
- Profile a real user interaction (typing, navigating, opening a modal), not an idle page, so the captured commits reflect actual cost.
- Read the “why did this render” reason before optimizing — it tells you whether
memo,useMemo, or restructuring state is the right fix. - Strip standalone DevTools
<script>tags from production builds; the in-browser extension only attaches to development builds anyway. - Label custom hooks with
useDebugValueso teammates can read intermediate values directly in the panel.