React DevTools Component Highlighting: Spot Unnecessary Re-Renders Visually
AI generated
{ }
React · DevTools · Performance
React DevTools Component Highlighting
Spot unnecessary re-renders visually, without recording a profiler session

Before you open the Profiler tab and start a recording, unnecessary re-renders can often be made visible with a single click. The React DevTools option Highlight updates when components render colors every component the moment its render function runs, live in the browser as you click, type, and navigate.

13 min read React DevTools Re-render debugging React.memo

1. Why re-renders stay invisible in everyday development

React re-renders components very frequently for good reason: every state change, every new prop, and every context switch can trigger a render function to run again. In most cases that is completely unproblematic, because React diffs the virtual DOM and only updates the parts of the real DOM that actually changed. The problem is not the re-render itself, it is that it stays invisible in the ordinary browser window. A user only sees that the page responds, not how often or how many components were unnecessarily recomputed along the way.

That invisibility is exactly what makes performance problems in React applications so hard to pin down. A form with thirty input fields can quietly re-render an entire sidebar, header, and an expensive chart component on every keystroke without anything visibly changing. The React DevTools close exactly that gap: instead of reconstructing render cycles from logs or profiler charts, they make them visible as colored flashes right in the running browser window, in real time and without a single line of additional code.

2. Enabling the option in the React DevTools

Enabling it takes a few seconds. Open the browser DevTools, switch to the Components tab, which the React DevTools extension for Chrome, Firefox, or Edge adds automatically, and click the gear icon for settings. In the General tab there is a checkbox labeled Highlight updates when components render. Once it is active, React DevTools draws a briefly flashing colored border around the affected component in the tree on every commit, meaning every render pass that was actually executed.

No build step, no import, and no instrumentation in your own code is required, the option lives entirely inside the browser extension. It works identically in the standalone React DevTools app used for React Native or Electron contexts. The feature is relevant exclusively during development: in a production build without the extension installed, end users see nothing of it, and the option has no effect whatsoever on bundle size or runtime performance.

3. Reading the colored borders and flashes correctly

Every re-render of a component produces a brief, colored flash around its rendered area in the browser. The color roughly encodes render frequency relative to other commits: a bluish flash indicates infrequent updates, a green tone points to more frequent ones, while more intense yellow and red tones signal that a component is re-rendering unusually often in a short span. This color scale is not an exact measurement tool, it is a quick visual heuristic meant to draw the eye to outliers in the component tree immediately.

One important nuance: the highlighting indicates that a component's render function ran, not necessarily that anything visibly changed on screen. React can render a component and produce the exact same virtual DOM as before, so nothing happens to the real DOM even though the render function ran to completion and cost CPU time. Anyone watching only for visible changes misses exactly these cases, where unnecessary work happens without anything moving on screen at all.

4. A concrete example of an unnecessary re-render

Picture a page with a counter button in the header and an ExpensiveList below it that renders a larger product list and needs no data from the counter at all. Both components share the same parent, which keeps the counter value in a useState. Without further measures, every click on the button causes the parent to re-render and, by default, also calls every child component again, even the ones that do not depend on the changed state at all.

With highlighting enabled the result is immediately visible: clicking the counter button flashes not only the number in the header but also the entire border of the ExpensiveList, even though its content does not change at all. This exact pattern, an isolated state change triggering a cascading re-render wave across entirely unrelated subtrees, is the most common category of unnecessary re-renders in React applications and is otherwise nearly impossible to spot with the naked eye.


function Dashboard() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <Header count={count} onIncrement={() => setCount((c) => c + 1)} />
      {/* ExpensiveList does not depend on "count" but re-renders anyway */}
      <ExpensiveList items={PRODUCTS} />
    </div>
  );
}

function ExpensiveList({ items }) {
  console.log("ExpensiveList renders");
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

5. Common causes of unnecessary re-renders

The most frequent cause is structural and has little to do with the specific example: in React, a parent component re-renders all child components by default whenever its own state changes, regardless of whether those children even consume the affected props. This gets worse with inline object, array, and function props, for example a handler like onClick={() => doSomething()} written directly in JSX. On every render a new function reference is created, even if its content stays identical, which defeats any downstream identity comparison.

A second common source is React Context: if the value passed to a provider is created as a new object literal on every render of the parent, for example value={{ user, theme }} written directly in JSX, then every consumer of that context re-renders on every change, even if only one of the two fields actually changed. Global contexts for theme, auth, or language in particular then potentially affect the entire application and produce exactly the kind of cascading highlighting that jumps out immediately in the component tree.


// Problematic: a new object on every render
function AppProvider({ children }) {
  const [user, setUser] = useState(null);
  const [theme, setTheme] = useState("light");

  // { user, theme } is a new object reference on every render
  return (
    <AppContext.Provider value={{ user, theme }}>
      {children}
    </AppContext.Provider>
  );
}

6. Fixing the root cause with React.memo, useCallback, and useMemo

The fix addresses two spots at once, because they depend on each other: the child component gets wrapped in React.memo so it skips a re-render as long as its props are shallowly unchanged. That alone is not enough if the parent keeps generating new prop references on every pass anyway. That is why callback functions need to be stabilized with useCallback and object or array literals with useMemo, each with a dependency array that only produces a new reference on a genuine content change.

After the fix, the very same highlighting technique shows the result: clicking the counter button now flashes only the header, the ExpensiveList stays dark because React.memo recognizes the comparison of the now-stable props and skips the render call. It matters not to reflexively slap React.memo on every component: the comparison itself costs time too, and for small, cheap-to-render components memoization can create more overhead than it saves. Highlighting also helps here, comparing the actual benefit objectively before and after the change.


const ExpensiveList = memo(function ExpensiveList({ items }) {
  console.log("ExpensiveList renders");
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
});

function Dashboard() {
  const [count, setCount] = useState(0);
  // useCallback keeps the function reference stable
  const handleIncrement = useCallback(() => setCount((c) => c + 1), []);
  // items already arrives as a stable reference from outside (e.g. a constant)

  return (
    <div>
      <Header count={count} onIncrement={handleIncrement} />
      <ExpensiveList items={PRODUCTS} />
    </div>
  );
}

7. When the Profiler tab is the better choice

Component highlighting reliably answers whether a component re-renders at all, but not how expensive that render actually was. That is exactly what the Profiler tab is for: a recording produces a flame chart with render duration per component in milliseconds, the number of commits during the recording, and, with the right option enabled, even hints about which props or which state change triggered a given render. That makes it the right tool once the question moves from a rough first impression to solid numbers.

In practice a clear sequence pays off: highlighting first, to find suspicious candidates in the tree at all, then the profiler, to quantify whether optimizing them is actually worthwhile. A component that visibly flashes but whose render, according to the profiler, only takes 0.1 milliseconds is rarely the cause of noticeable jank. Wrapping every flashing component in React.memo right away often costs more time than the actual performance gain it delivers.

8. Building component highlighting into the daily workflow

The option delivers the most value once it becomes a fixed part of the development routine instead of only being switched on when performance complaints arrive. A short check right after adding new state, a new context, or a newly wired prop chain is particularly worthwhile: a glance at the component tree during a typical interaction immediately shows whether the change ripples out unexpectedly far. Some teams turn this into a fixed step in self-review before every pull request that touches UI-relevant code.

For deeper cases, a small custom debug hook that logs render counts and changed props straight to the console is a useful addition whenever the visual flashing alone is not enough to isolate the cause. For automated, ongoing monitoring beyond manual observation, libraries like why-did-you-render or React Scan are worth adding, since they name the cause directly instead of only visualizing it. Component highlighting remains the fastest first step regardless, since it is available instantly with no extra dependency at all.


function useRenderCount(label) {
  const countRef = useRef(0);
  countRef.current += 1;

  useEffect(() => {
    console.log(`${label} rendered ${countRef.current} time(s)`);
  });

  return countRef.current;
}

function ExpensiveList({ items }) {
  useRenderCount("ExpensiveList");
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

9. Limits and common pitfalls of the method

In Strict Mode, React 18 and 19 deliberately run every render and selected effects twice in the development build, in order to surface hidden side effects early. That causes component highlighting to flash roughly twice as often under Strict Mode as it would in production behavior, which developers new to Strict Mode easily mistake for a real performance problem. A quick look at production build behavior, or consciously ignoring the first duplication, clears this up before chasing a phantom cause.

The second limit is more subtle: no flashing is not proof of good performance, because expensive computations can also live outside the actual render cycle, for example inside a heavy useEffect, an expensive event handler, or a synchronous calculation that runs independently of the component tree. Component highlighting only answers the question of render frequency, not the total cost of an interaction. Relying on it exclusively misses entire classes of performance problems that only surface in the profiler or in real performance traces.

Method Shows Effort Best time to use it
Component highlighting That a component renders, colored, live in the browser One click, no recording needed First quick scan during development
Profiler tab Render duration, commit count, trigger per commit Start and stop a recording Solid numbers before a targeted optimization
why-did-you-render Exact cause: which prop or state value changed Add and configure a library Deep analysis of stubborn individual cases
Custom useRenderCount hook Render count and timing in the console A few lines of your own code Focused debugging of one specific component

Mironsoft

React architecture, performance, and Magento frontend integration

React frontends that stay fast instead of slowing down with every feature?

We review existing React applications for unnecessary re-renders, bloated bundles, and fragile state management, then build a frontend that stays performant and connects cleanly to Magento or other backends.

Performance Audit

Systematically measuring and fixing re-renders, bundle size, and load times.

State Architecture

Cleanly separating context, client state, and server state instead of mixing everything.

Magento Integration

Building robust, type-safe GraphQL or REST integration with Magento.

10. Summary

React DevTools Highlighting: The Essentials at a Glance

Enabling it

Open the Components tab in React DevTools, click the gear, check Highlight updates when components render.

Color code

Blue to red indicates relative render frequency, a quick heuristic rather than an exact measurement.

Most common cause

Inline object, array, and function props, plus context values that are not kept stable.

The fix

React.memo on the child component, combined with useCallback and useMemo at the source of the props.

11. FAQ: React DevTools Highlighting: The Essentials at a Glance

1Where do I find the Highlight updates when components render option?
In the Components tab of the React DevTools, click the gear icon and enable the checkbox under General settings. It is available both in the browser extension and in the standalone React DevTools app.
2Does component highlighting work in production builds too?
Technically yes, provided the React DevTools extension is installed and React is not fully decoupled from the DevTools in that build, but practically the feature is meant for development builds. In production environments without the extension installed, end users see nothing of it.
3What do the different flash colors mean?
The color roughly encodes a component's relative render frequency: blue tones indicate infrequent renders, yellow and red tones indicate unusually frequent ones. It is a quick visual heuristic, not an exact time measurement.
4Does the highlighting show up even when nothing visibly changes on screen?
Yes, the highlighting reacts to a component's render function having executed, not to whether the resulting virtual DOM actually differs from the previous one. That is exactly what makes it valuable for spotting invisible but unnecessary render work.
5Why does every component flash twice as often under Strict Mode?
React 18 and 19 deliberately run renders and certain effects twice in the development Strict Mode, in order to surface hidden side effects early. It is not a bug and not a real performance problem, it is a deliberate development aid that does not occur in the production build.
6Is React.memo alone enough to fix unnecessary re-renders?
Usually not. React.memo compares props shallowly, but that does not help if the parent keeps passing new object, array, or function references as props on every render. Those values also need to be stabilized with useCallback or useMemo.
7When should I use the Profiler tab instead of highlighting?
As soon as solid numbers matter, for example the actual render duration in milliseconds or the number of commits during an interaction. Highlighting is suited for a quick first impression, the profiler for the prioritization that follows.
8Can frequent flashing also be harmless?
Yes, many React applications re-render components regularly without any noticeable impact, especially for small, cheap-to-compute components. Only the combination of frequent flashing and a measurably high render duration in the profiler justifies an optimization.
9Are there alternatives that name the cause automatically?
Libraries like why-did-you-render automatically log which specific prop or state value triggered a re-render, instead of only visualizing that a render happened. For ongoing monitoring beyond individual debugging sessions, that is often the more practical addition.
10Does enabling component highlighting affect the application's performance?
No, the option lives entirely inside the browser extension and changes neither the application code nor the bundle size. While active, drawing the colored borders may create a tiny bit of extra browser load, but that has no effect on production environments.