Debugging a React app can be a slow and painful process. Adding a console.log()
statement here and there until you’re finally in the right spot. Or a more advanced version: Jumping around between setting breakpoints in the Chrome dev tools and editing code in your IDE until the bug is fixed.
But with the right tools and a strategic approach debugging can become much easier. Maybe even fun?!
Turns out much beloved VS Code makes it very simple to debug a React app directly from the IDE. The result: Super easy setup and a more productive debugging workflow.
On this page, you can see how to set up VS Code as a debugger for your React app and see it in action. We’ll debug a small problem with a Next.js application and use (conditional) breakpoints, step into functions, and inspect and edit variables directly from the VS Code. All of this paired with a structured debugging approach and the bug is fixed in no time.
Here’s a short video showing how to set up and use the VS Code debugger. Alternatively, you can find a detailed step-by-step tutorial with screenshots on this page.
Table Of Contents
Launch Chrome via VS Code
Starting to debug your React app with the VS Code debugger is surprisingly simple. You let VS Code create a launch.json
config for you and slightly adjust it. You can find the file in the .vsocde
folder in your repository.
Depending on your app you need to
- adjust the
url
field (here I change the port to 3000) - adjust the
webRoot
entry (e.g. if your code is in thesrc
folder like create-react-app apps you change${workspaceFolder}
to${workspaceFolder}/src
)
Now you can hit the play button ▶️ to start a Chrome browser in debug mode. The VS Code debugger is automatically attached to this browser.
Note: You could run Chrome in debug mode manually and use the “attach” launch config to attach the VS Code debugger. But I don’t see a reason to overcomplicate things.
If you already have a launch.json
file or want to add another config to it you can simply use Intellisense (e.g. by pressing cmd + space on Mac) and get suggestions.
Here is my launch.json
file:
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Chrome",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}"
}
]
}
Using The VS Code Debugger
Two use the debugger you need two things:
- Run your React app (e.g. by running
npm start
). - Start Chrome via VS Code by pressing the play button ▶️.
You can now use the debugger as you would in the Chrome dev tools. If you’re not familiar with that you can see an example below.
The VS Code Debugger In Action
Let’s imagine the following situation: We’re a developer working on a Next.js app that is in production. Real users, nothing should break.
One of the most important pages used to look like this (you can see a deployed version of it here):
But suddenly we get reports that something is broken (here a deployed version):
Damn, how did this bug make it into production? Our customers are upset. They’re paying good money and rely on our app. Obviously, our managers are going crazy as well.
But we as the developers stay calm. With the right debugging approach we can probably fix this bug in no time.
The error message in production doesn’t tell us a lot. So first we run the app on our local machine.
Since we know that we need to debug the code we use the VS Code debugger to start a Chrome instance.
This opens the browser at localhost:3000 as defined in launch.json.
Now when we navigate to the problematic page we see this error.
Using Breakpoints in VS Code
This error is much better than what we see on the production website. It provides us with two important pieces of information:
- The error message “Cannot read properties of null”.
- The name of the file and the line where the error occurred: line 49 in
issue-row.tsx
.
Opening the file in VS Code is really simple. The file path in the error message is in fact a link:
When we click it, the file issue-row.tsx
opens in VS Code. Magic.
The error message tells us that the issue
prop is likely null
at some point. Let’s verify that by adding a breakpoint. Simply click inside the empty space next to the line number.
Once you press the green “Restart” button ↻ the page refreshes.
To speed up the process you can also use a key combination (Cmd + Shift + F5 in my case). You can hover over the button to find out yours.
The code execution stops at the breakpoint. At the same time, the website freezes in its loading state.
As you can see in the screenshot above the issue
prop is defined during the first render.
So we press the “Continue” button ⏯️ or F5 a couple of times. The problem is that there are quite a few IssueRow
components being rendered. So hitting “Continue” until we find the right issue becomes quickly annoying.
Conditional Breakpoints To The Rescue
Instead of hitting “Continue” all the time, we’d like to skip all the issues that are defined and only stop at the nullish one.
The easiest way to do that is by adding a condition to the breakpoint. Right-click on the breakpoint and select “Edit Breakpoint”.
Now we can enter a JavaScript expression. In our case, we want to stop at issue === null
.
We hit enter and continue code execution. And voila we can confirm that at least one issue is null
.
To be honest, this isn’t exactly news to us as we already knew this from the error message. But at least we were able to confirm the problem.
Finding The Root Cause
So let’s dig a bit deeper. In a simple JavaScript program, we could just follow the call stack or press the “Step Out” button (⬆️) to find the root cause of the problem.
But with React (and other frameworks) it’s not that easy:
Except the IssueRow
we can’t see any of our code files in the “Call Stack” panel. Everything else is internal React files.
This doesn’t help us much.
So, unfortunately, we need to find out where the IssueRow
component is rendered manually. The global search function of VS Code is our best friend here.
We open the file and add another breakpoint just before the return statement of the component.
Then hit the refresh button ↻ in the debug controls or F5.
Great, now we see that one item in the items
array is indeed null
.
Inspect And Edit Variables In The Debugger
Our assumption is that this null
value causes the bug. But before we start messing around with our code we can easily verify this assumption by editing the variable inside the debugger.
When we continue the code execution now we can see that the error disappears on the website. The 7th and 8th items in the issue list are now duplicates as expected.
This verifies our assumption that the null
value in the data array is the problem.
That makes the solution to our bug simple: we can just filter out all null
values from the data to fix the bug. Of course, we could implement the filter function in the component. But maybe a place closer to the data source would be more suitable. This way other (future) components could potentially benefit from the same fix.
Step Into A Function
To dig a bit deeper we have a look at the beginning of the component. The items
array with the null
value comes from the issuePage
variable. And that one comes from a hook.
So let’s set another breakpoint there and hit the refresh button again. Code execution stops at the breakpoint.
Now we can use the “Step Into” button ⬇️ to investigate that hook. The first file that opens is again some internal React file.
But this time we’re better off. After hitting the “Step Into” button a couple of times we end up in the useIssues
hook.
Having a closer look the getIssues
function seems like a good candidate to filter the data.
Unfortunately, for some reason, we neither can step into the getIssues
function nor does code execution stop at a breakpoint inside. If you have a bit of debugging experience you know that there are inconveniences like this from time to time.
Anyway, let’s not allow that to hold us back. We add a bit of code to filter the issues.
Once we hit return we can confirm that the bug is fixed.
Commit, push, deploy. Everyone’s happy.
Latest comments (13)
Nice post. Thanks for sharing.
When i signup and get the invitation for the repo it seems like the code/url in the instructions are the already fixed one, as there's no bug showing for me when navigating to the "issues" tab. Any ideas?
You need to check out the branch "debugging-exercise" :)
Thanks for that question btw. I added that info to the email but didn't realize that the code formatting was broken. Wouldn't have found out without your comment
Thank you Johannes, forgot to check the remote branches and thought it was stuck on "main" :)
The kind of React debugging tutorial I've been looking for! Thank you so much Johannes!
Thank you very much, very helpful post
Thanks for the feedback :)
Great! Thanks a lot for so nice explanation!
Thanks for the feedback :)
Cool! Finally someone that explain in a clear way. Thanks a bunch, mate!
Thanks for the feedback :)
That was a nice read! Liked, bookmarked and followed, keep the good work! 🙌
Thanks a lot for the nice words :)