Get eBook BundleVolume I index
Project Tooling and the First Milestone

React DevTools First Pass

Ishtmeet Singh @ishtms/July 20, 2026/12 min read
#react#devtools#debugging#components#profiler

When something goes wrong on a React screen, I usually have two DevTools panels I care about. The normal browser Elements panel, and React DevTools.

They answer different questions.

The Elements panel shows you the actual DOM sitting in the browser. React DevTools shows you the React components which produced that DOM, along with the props they received, their state Hooks, context values, parent components, etc.

Take this component:

jsx
function LessonCard({ lesson }) {
  return (
    <article>
      <h2>{lesson.title}</h2>
      <button type="button">Open</button>
    </article>
  );
}

Open the browser Elements panel and you'll see an article, h2, and button. You won't find a <LessonCard> HTML tag anywhere because no such DOM element was created.

Open React DevTools though, and now you'll see LessonCard. Select it and you can inspect its lesson prop.

So while debugging, first question should usually be: am I trying to inspect a browser element, or am I trying to inspect the React component which produced it?

That decides which panel you open.

Install React Developer Tools

React Developer Tools is available as a browser extension for Chrome and Firefox, and you can use it with Edge too. I'd install it using the link from React's official documentation instead of randomly searching the extension store, since there are other extensions using similar names.

Once installed, open your local Vite app and then open browser DevTools. If React gets detected, you should get two extra panels: Components and Profiler.

If they're not showing up, don't start debugging your React project immediately. Reload the page first. If still nothing, check whether the extension is actually enabled in the browser profile you're using and whether it has permission to run on the site.

Reading the Component Tree

Let's start with a few named components:

jsx
function App() {
  return (
    <LessonPlanner>
      <LessonList />
    </LessonPlanner>
  );
}

Open the Components panel and you'll get a tree something around this:

text
App
  LessonPlanner
    LessonList
      LessonCard
      LessonCard

Again, don't confuse this with the DOM tree.

There is no <LessonPlanner> element sitting inside the browser document. This panel is showing React component nesting. That's why it can show things which the normal Elements panel cannot.

Select one of the LessonCard entries and you'll see its props on the side. Select LessonPlanner and you'll see its Hooks and their current values.

Suppose our component has these:

jsx
const [query, setQuery] = useState("");
const [showCompleted, setShowCompleted] = useState(true);

React DevTools may show those Hook entries simply as State, depending on what information it has available. If that happens, use their call order. query was created first, so it appears before showCompleted.

This is one reason I really prefer proper component names while developing. Finding LessonPlanner inside DevTools is much easier than looking through five components named Item, Wrapper, or some anonymous function and wondering which one you're actually looking at.

Search the Component Tree Instead of Opening Everything

Once the app gets even slightly bigger, manually expanding the entire Components tree becomes annoying very quickly.

You might have providers, layouts, router components, repeated cards, wrappers, and ten other things you're not interested in right now. So if you're debugging LessonCard, search for LessonCard.

If there are twenty cards, move through the matches and inspect the props. Usually the parent location and incoming data tells you which actual card you've selected.

Filters are useful here too, but be a little careful with them. You can hide components you don't care about, then completely forget that you've done it. Later you're sitting there asking, "Where the hell is my provider? I know it's rendered."

Reset the filters before spending half an hour questioning your code.

Inspect the Prop Where It Arrives

Say one lesson card is showing the wrong title.

Instead of immediately adding five console.log() calls, select that exact LessonCard in React DevTools and inspect:

text
lesson.title

What does it contain?

If lesson.title is already wrong before LessonCard renders it, then this component probably didn't create the bad value. It received bad data from somewhere else.

So move upwards and inspect the component which passed lesson.

If the prop is correct but the DOM text is still wrong, then now I'd inspect the rendering code inside LessonCard. Also make sure you're looking at the correct card. Repeated components make this surprisingly easy to mess up. You think you've selected card number five while DevTools is actually showing card number four.

I generally follow the value backwards until I reach the code which changed it.

text
wrong DOM output
  -> component which produced it
  -> props/state used by that component
  -> component or calculation which supplied the wrong value

React DevTools can also show you the owner of an element. Owner means the component which created that React element, which isn't always the same thing as whichever component happens to appear directly above it in the rendered tree.

You don't need to obsess over that difference right now, but you'll see the term.

Checking State Without Adding Logs Everywhere

Now select LessonPlanner and start typing into its search input.

If that input updates some state variable, you'll see the state value changing inside React DevTools while the app renders.

Say we have:

jsx
const [query, setQuery] = useState("");

You type:

text
react

and DevTools shows query becoming "react".

Okay, good. We already learned something.

The event handler ran far enough to update state, and React rendered using the new state value.

Does that prove the filtering code is correct?

Nope.

Imagine this:

jsx
const visibleLessons = lessons.filter((lesson) =>
  lesson.title.includes(searchText),
);

But the actual state variable is called query.

Your input handler can work perfectly. setQuery() can work perfectly. React can render again with the new value. And the UI can still be wrong because your filtering calculation is reading searchText.

So don't rewrite the input handler just because the list isn't updating correctly. First inspect what state actually contains.

Logs are still useful, obviously. Especially when you're debugging ordering, browser events, network code, or values outside React. But for "what props does this component currently have?" and "what is this Hook value right now?", React DevTools already gives you that information.

Editing Props and State From DevTools

React DevTools can let you modify some props and state values while you're debugging.

This is very useful, but treat those changes as experiments.

Suppose the UI has:

jsx
const [showCompleted, setShowCompleted] = useState(true);

and you're trying to figure out why completed lessons aren't hiding.

Select the component and temporarily change showCompleted to false from DevTools.

Now watch the UI.

If completed lessons disappear correctly, then rendering for false seems to be working. I'd probably start checking the checkbox handler next, because maybe your UI simply isn't setting the state value you expected.

If changing the value to false still produces the wrong list, then I'd look at the filtering calculation or rendering code instead.

Either way, changing the value inside DevTools didn't repair anything in your source code. The next normal application update can replace your edited value anyway.

We're only using the edit to remove some possibilities while debugging.

Jumping to the Component Source

React DevTools can also help locate the source code for a selected component when source maps and editor integration are available.

Even if clicking through to your editor doesn't work, having a proper component name still cuts down the search a lot. Searching your project for LessonPlanner is pretty easy.

Searching for Item?

Yeah, good luck if you named twelve components that.

Vite normally gives us development source maps, so transformed modules can still be traced back to the .jsx or .tsx files we're actually editing.

Library and framework components may have names which look a bit strange because of wrappers or generated internals. Our own project is small, so most of the tree should still be pretty readable.

Highlighting Components When They Render

React DevTools has an option to highlight components when they render.

Turn it on, then type one character into the lesson search box.

You'll see parts of the page flash as their corresponding components render.

This is useful when you're wondering, "Did this component render when I changed that input?"

But don't read too much into the highlight.

A highlighted component rendered. That's all you're learning from that feature.

It doesn't mean the render was slow. It doesn't mean React removed and recreated that DOM subtree. It doesn't even mean a DOM node changed.

A component can run again, return the same host output as before, and React may have no DOM update to apply for that part.

First Look at the Profiler

The other React DevTools panel is the Profiler.

Profiler records React commits. A commit is when React takes completed render work and applies whatever updates are needed to the host environment, which for us here is the browser DOM.

Let's do one small recording.

Open the Profiler, start recording, type one character into the lesson search, toggle the completed checkbox once, and then stop recording.

You'll now see one or more commits from that interaction.

Select a commit and React DevTools can show which components rendered and roughly how much time React recorded for them during this development session.

There are different views for this. The flamegraph keeps the component tree visible while showing recorded work across it. The ranked view orders components based on their recorded cost for the selected commit.

For this tiny project, don't start optimizing because one component says 0.7ms and another says 0.3ms.

Those tiny numbers by themselves aren't telling you much.

The first useful question is much simpler: when I performed this interaction, which components rendered and how many commits did React perform?

Once you understand that, timings become more useful later.

Development Timings Can Be Weird

Profiling while running the Vite development server is useful, but don't treat those timings as final performance numbers.

Development React does extra work. Strict Mode can cause additional render calls in development. Source maps add work. DevTools itself adds overhead. Other browser extensions can add overhead too. Having the debugger open changes the environment you're measuring.

So I mostly use development profiling to understand what rendered and to spot suspicious work.

If I actually need to make a performance decision based on timings, then I'd test that case in a setup meant for profiling production behavior.

Also write down what you measured instead of saying something useless such as "React feels slow."

For example:

text
interaction: type one character
data: 30 local lessons
browser: current Chrome
mode: Vite development
result: LessonPlanner and LessonList rendered

Now somebody else can perform the same action and compare what they see.

"React is slow" doesn't tell them anything.

Which Panel Should You Use?

You don't need to pick one DevTools panel and use it for every problem.

If I'm trying to find the actual <button> element, inspect its CSS, check an aria-* attribute, see whether a label points to an input correctly, or inspect computed styles, I'm in the browser Elements panel.

If I want to know which React component rendered that button, what props it received, or what state values its Hooks currently contain, I'm in the React Components panel.

And if I'm asking which components rendered during some interaction and what React recorded during the resulting commits, I'm opening Profiler.

Once you're used to switching between those three, the first debugging pass gets a lot easier.

Let's Debug One Actual Bug

Let's finish by intentionally putting a bug into our lesson filter:

jsx
const visibleLessons = lessons.filter((lesson) =>
  lesson.title.includes(query),
);

Maybe one lesson is called:

text
State in React

Now type:

text
state

Nothing shows up.

Why?

Let's not guess.

First open LessonPlanner in the Components panel and check query. It says "state".

Good. Input state is updating.

Now inspect LessonList. Suppose its lessons prop contains an empty array.

Okay, so the wrong result already exists before LessonList tries rendering anything. That means I'd go back to the parent and inspect the code which creates that array.

And there it is:

jsx
lesson.title.includes(query)

includes() here is case-sensitive. "State" does not contain lowercase "state".

So we can normalize both values before comparing:

jsx
lesson.title.toLowerCase().includes(query.toLowerCase())

Now the lesson appears again.

React DevTools didn't somehow read our application and hand us that fix. What it did was tell us that query had the right value, then show us that LessonList was receiving the wrong array.

That reduced the amount of code we needed to inspect.

And that's really what I want you using React DevTools for at this point. Don't try learning every button inside it on day one. Use it to answer small debugging questions: which component am I looking at, what props did it receive, what state does it currently have, did it render, and what happened during this interaction?

You'll learn the other controls when you actually have a reason to use them.