Tips & Tricks

How to Debug With WebStorm

Note: This post was updated in January 2024.

The debugger is one of the most essential features of WebStorm. With it, you can debug all kinds of applications written in JavaScript and TypeScript: Node.js, React Native, Electron apps, and client-side apps written using different frameworks. In addition to that, you can also debug unit tests and build scripts.

One of the main benefits of using a debugger inside the IDE is you can add breakpoints and step through your source code (even if you then compile it. Thank you, source maps!). If you want to edit the code or quickly navigate to the usages or definitions of methods while debugging, you don’t need to switch back to the editor.

And no matter what kind of code you debug, your experience with the debugger will be the same.

To see how the debugger works in WebStorm, we’ll try to debug a simple Express Node.js application generated by the New Project wizard. We’ll start by creating a new run/debug configuration. Then, we’ll add some breakpoints, see what’s happening in the Debugger’s Variables view, step through the code, evaluate expressions, and use the interactive console.

Prepare for debugging: create a run/debug configuration

Run/Debug Configurations is an entry point to, as the name suggests, running and debugging apps in WebStorm.

To debug different types of apps and files, you need to use different types of Run/Debug Configurations.

Depending on the type, the information you need to provide in the configuration varies.
For example, in the Node.js configuration, you need to specify a file to run. In the test configuration, you can select the name of a suite, test, or test file. Some configurations attach to the already started app. In this case, you need to specify the URL and port to attach to.

To create a run/debug configuration:

  1. Click the drop-down list on the toolbar and select Edit configurations…. The Run/Debug Configurations dialog opens.
  2. In the left-hand pane, click the + icon on the toolbar and select the configuration type: we’ll choose Node.js.
  3. In the right-hand pane, add the missing information about the debug environment: we need to specify bin/www, which is the main file that runs our app.
  4. Name and save the configuration.

Create a run/debug configuration.
All the created configurations are saved in the workspace.xml file in the .idea folder in the root of your project, so you can reuse them the next time you open this project.

If you want to share your configuration with your team, check the Share checkbox in the configuration and then commit the .idea/runConfigurations folder to the version control.

Starting the debugger

Once our configuration is ready, select it from the drop-down list on the toolbar and click the green debug icon next to it.

Start a debugging session.

Our Node.js configuration will run the file we specified in debug mode. Many configurations will do the same, but some others will instead attach the debugger to an already running app.

In our case, we could have started the debugger without creating a configuration beforehand. Instead, we could have right-clicked the bin/www JavaScript file in the Project tool window or the editor and selected Debug, or we could have pressed ^D on macOS or Ctrl+Shift+D/Ctrl+Shift+F9 on Windows and Linux.

Start a debugging session from the Project tool window.

This creates a temporary configuration that, if you want to reuse it, you can save later in the Run/Debug Configurations dialog (temporary configurations are greyed out).

Saving a temporary run/debug configuration.

Debug tool window

We can now see a new Debug tool window at the bottom of the IDE – a control panel for everything related to debugging.

The toolbar above it shows icons for stopping and re-running our configuration and for stepping through the application.

The Frames and Variables view will be activated when the app stops at a breakpoint (we’ll talk about it in a moment).

The Debugger Console tab can be used for evaluating expressions.

Debug tool window.

The Process Console tab shows the messages logged by the app and error messages.

The Scripts tab lists all the files loaded into the currently running process. We can double-click on the file to see its content in the editor.

Adding breakpoints to pause the code

We want to see what’s going on in our app at a specific point. With the breakpoints, we can pause the app when a specific line of code is executed and then look at the application state: what variables are now available and what their values are.

Let’s put a breakpoint on a line in which our demo app handles the GET request: click the line number in the editor, and you will see a red circle, a breakpoint.

Setting and removing breapoints.

(Here’s the content of the file on this screenshot)

If you click the breakpoint again, it will be removed. You can additionally configure how the breakpoint works if you right-click it. For example, you can set specific conditions for hitting the breakpoint.

Conditional breakpoint.

We can add and remove breakpoints any time during the debug session or before it. There’s no need to restart the debugger after we’ve set or cleared a breakpoint!

Exploring the app state and call stack

So now, when the debugger is up and running and we have a breakpoint in our code, let’s stop on it. For that we should do the actions that will trigger the code with the breakpoint: in our case, we need to open the browser at http://localhost:3000/en.

Now, the Debug tool window is full of content. Let’s have a look at what is going on!

On the right, we see the state of our application – all the local and global variables available at this point and their values. We can also see the values right in the editor: at the end of the lines of code or if we hover over a variable.

Debug tool window during a debugging session.

The Threads view shows the call stack – a list of all the methods that have been executed to get the execution to the code with the breakpoint in reverse chronological order. If we go through the frames, we will see the application’s state at every point of the execution path.

Don’t forget that the code under the breakpoint has not yet been executed. So, if you introduce a variable in this line, you’ll see it in the Variables view, but it will still be undefined.

We can expand the objects and arrays to see their content in the Variables view.

We are specifically interested in the params object that contains the parameters of the request. We can type its name to find it in the list (note that it will search only through the expanded nodes). Let’s add it to Watches – that way, we will always see its value on the top of the Variables view anytime we stop at a breakpoint.

Adding Watches.

Now press the Resume Program button on the toolbar to continue executing the program.

Resuming program.

Stepping through the code

If we want to get a better understanding of how our code is executed, we, of course, can put lots of breakpoints in our code and then move from a breakpoint to a breakpoint by resuming the execution, but there’s a better way – we can step through the code!

The stepping actions are available on the top of the debug tool window and activated when the breakpoint is hit.

Step over action (F8) will move the execution in the current file, line by line, without stepping into any function calls.

Step over.

In our example, if we hit it once, it will check the condition in line 10 and then stop on the statement in the else block because the condition was false.

With Step Into (F7), we will step to the next executed line in this or another file. So, if it’s a function call, it will jump to its definition and stop at the first line.

Step Into.

By default, WebStorm will not step into the source code of JavaScript, Node.js, or dependencies in the node_modules folder of your project. But if you want to step into them, you can use the Force Step Into action (you can clear the Do not step into libraries checkbox in Settings | Build, Execution, Deployment | Debugger | Stepping).

Step Out (⇧F8 / Shift+F8) will finish executing the current function and stop at the next statement after the call.

If we want to skip some lines but then stop on another line that is not yet executed, we can either put a new breakpoint on this line and press Resume or put a cursor in it and use the Run to Cursor action. Which is a much easier and more efficient way than setting and clearing breakpoints whenever you move the cursor.

Evaluate Expression

The Variables view shows the values of objects when the app is stopped on a breakpoint. But what if we want to check the value of an expression? For instance, we want to see if a condition in the if statement is true or false before we step over to the next line.

There are three ways you can do this:

The first way is to type the expression in the Debugger Console. For that, we can either type it or copy and paste it.

Evaluating a expression in the Debugger Console.

A second way is to select the expression and then use the Quick Evaluate Expression action (⌘⌥F8 / Ctrl+Alt+F8): the result will appear next to it in a tooltip.

Quick Evaluate Expression.

And a third way is to invoke the Evaluate Expression popup. Similar to the console, you can type (with the help of code completion) and evaluate any statement it is in.

Evaluate Expression Popup.
Animated image:

The pros of using the Evaluate Expression popup compared to the console are that you can open it using a shortcut (⌥F8 / Alt+F8) and then see all the information about the returned object in a handy tree view.

Evaluate Expression in a popup: view object.

So now we know enough to debug an application: we can stop the app on a breakpoint, see the application state, step through the code, and evaluate expressions. It all works the same way in WebStorm, no matter what kind of app you are debugging.

That’s it for today. You can also find lots of helpful information in the WebStorm documentation.

The WebStorm team

image description