Skip to content
JavaScript Debugging: Tools, Tips & DevTools Guide

21 Minutes

JavaScript Debugging: Tools, Tips & DevTools Guide

Fix Bugs Faster! Log Collection Made Easy

Get started

What is JavaScript debugging?

Well, you know the basic answer, right? Debugging is about identifying and fixing errors in our code to ensure it runs as expected. But if we’re talking specifically about JavaScript, it gets more complex because we need to dig into the specific errors, tools and challenges of this language.

We’re going to unpack all this stuff in the article that follows, and we’ll

  • Look at how console.log() can reveal basic information.
  • Explore how a specific JavaScript debugger allows us to pause execution, set breakpoints, inspect variables, and step through code to find the exact cause.
  • Provide detailed analysis of specific built-in browser tools like Chrome DevTools, Firefox Developer Tools, or Safari Web Inspector.

We’ll also talk about how remote logging solutions like Bugfender capture real-time error data from user devices, making it easier to fix problems that testing alone might miss. But honestly, we’re not going to give you the hard sell – you’re not here for that, we know.

What we’ll be covering today

We’re going to get into all the essentials of JavaScript debugging, zooming into three key areas:

  1. Debugging JavaScript without developer tools.
  2. Using JavaScript debugging tools provided by our browsers (Chrome DevTools, Firefox Developer Tools etc).
  3. Using code editors (specifically, Visual Studio).
  4. Tips and hacks.

As an example, we’ll be using the Greet Me app. This JavaScript app asks for our name and invites us to submit a ‘wish’, which will deliver a personalized greeting.

Image
Figure 1: The Greet Me app showing an error

You see the problem, right? The greeting message has gone rogue and inserted a random word, NaN. Thankfully, Chrome DevTools will enable us to identify the issue.

(If you want to try out the tips provided below, you can find the Greet Me app at https://greet-me-debugging.vercel.app/. You can also clone the app code from GitHub and run it locally).

Part 1: Debugging JavaScript without developer tools

Anticipating common JavaScript problems

JavaScript errors typically show up in predictable patterns, and anticipating them can save lots of stress later on. Here are some of the most common errors:

  • undefined and null errors — trying to access properties of missing values.
  • Off-by-one errors — classic mistakes in loops and array indexing.
  • Type coercion issues — unexpected results when JavaScript converts types automatically ('5' + 1 = '51').
  • Scope leaks — accidentally using global variables when we meant to use local ones.
  • Asynchronous bugs — callbacks, promises, or async/await code not resolving as expected.
  • DOM manipulation errors — elements not yet loaded or removed from the page.
  • Silent failures in production — errors users see but we don’t notice unless logged.

Understand the error message

When JavaScript throws an error, the browser typically gives us lots of vital info. We should read the details carefully before touching the code, as it often tells us exactly what’s wrong and where to look.

  • Identify the error type (ReferenceError, TypeError, etc).
  • Note the file name and line number.
  • Click the console link to jump to the source code.
  • Search the exact message online for explanations.
  • Check whether the error repeats in the same place.

Reproduce the bug consistently

Before fixing an issue, we need to make it happen consistently so we can study it under controlled conditions. Replicating the bug empowers us to identify the right problem and verify the fix later.

The traditional approach relies on four stages:

  • Try to recreate the exact sequence of actions leading to the bug.
  • Test with the same inputs, browser, and environment.
  • Record any error messages or console logs.
  • Repeat until you can trigger it consistently.

But of course, a debugging tool can make the process waaaaaaay simpler.

💡 Humble flex: With Bugfender, you can search the dashboard for the affected device or user; review recorded console logs, errors, UI events, and network calls; see the exact sequence that led to the bug without guessing; replicate locally only to confirm and test your fix.

Benchmark loops with console.time() and console.timeEnd()

Measuring how long a loop takes to run is useful for performance tuning and comparing different implementations. Specifically, it’s helpful for spotting slow loops, optimizing algorithms, or testing changes.

We can do this using console.time() and console.timeEnd().

  • Start the timer with console.time('label').
  • Run the loop or code block.
  • End the timer with console.timeEnd('label') to display the elapsed time in milliseconds.
  • Use the same label for both methods so the results match.

Example:

console.time('loop');
for (let i = 0; i < 1000000; i++) {}
console.timeEnd('loop');

Use console.trace() to debug JavaScript function calls

console.trace() helps us see the sequence of function calls that led to a specific point in our code. This is useful for understanding complex flows or finding where a function was triggered.

Here are the steps:

  • Call console.trace() inside the function you want to inspect.
  • The browser will print a stack trace showing the chain of calls (this works well for debugging unexpected or multiple function triggers).
  • Combine with breakpoints or logs for deeper analysis.
  • Avoid leaving it in production code to prevent unnecessary console noise.

Example:

function test() {
  console.trace();
}
test();

Part 2: Using JavaScript debugging tools

What are JavaScript debuggers?

A JavaScript debugger helps developers inspect, pause, and control the execution of their code to find and fix bugs better. Unlike simple logging, a debugger lets us set breakpoints at specific lines, monitor variable values in real time, and step through code line by line.

Most modern browsers include built-in debuggers—such as Chrome DevTools, Firefox Developer Tools, and Safari Web Inspector—that work in similar ways. These tools make it easier to pinpoint the exact cause of issues, whether they’re syntax errors, logic mistakes, or unexpected browser behavior.

Combined with remote logging platforms like Bugfender, debuggers help capture and resolve even the most elusive JavaScript problems.

Using JavaScript debugging tools in popular browsers

In most modern browsers, we can open the built-in JavaScript debugging tools by:

  • Pressing F12.
  • Pressing Ctrl+Shift+I (Windows/Linux) or Cmd+Option+I (Mac).
  • Right-clicking anywhere on the page and selecting Inspect.

Chrome DevTools

In Google Chrome, the main workspace for JavaScript debugging is the Sources panel. This is where we’ll load and view your scripts, prepare them for inspection, and control how the code runs during the debugging process.

We can also reach it from the browser menu by selecting More tools → Developer tools, then clicking the Sources tab.

Image
Figure 1: Opening the Sources Panel

The Sources panel has three primary sections.

Image
Figure 3: Sources Panel sections
  1. File Navigator Section: All the files that our Greet Me page requests are listed here.
  2. Code Editor Section: When we select a file from the navigator pane, the content of the file will be listed here. We can edit the code from here too.
  3. Debugger Section: There’s loads of tools available here to set breakpoints, inspect variable values, watch for changes etc.

If the DevTools window is wide or undocked in a separate window, the debugger section will be displayed to the right of the Code Editor pane.

Image
Figure 4: DevTool window is wide open

Firefox Developer Tools

In Mozilla Firefox, JavaScript debugging happens in the Debugger panel, found within its Developer Tools section.

This panel lets us view loaded scripts and set up our workspace before starting the debugging process. We can also reach it from the browser menu by selecting Web Developer → Debugger.

Microsoft Edge DevTools

In Microsoft Edge, the main workspace for JavaScript debugging is also the Sources panel, similar to Google Chrome. Here, we can view scripts, prepare them for inspection, and manage the debugging process.

We can also reach it from the browser menu by selecting More tools → Developer tools, then opening the Sources tab.

Opera DevTools

In Opera, JavaScript debugging takes place in the Sources panel within its Developer Tools, which works much like Chrome’s. It allows us to load scripts and prepare them for inspection before starting the debugging process.

We can also reach it from the browser menu by selecting Developer → Developer tools, then opening the Sources tab.

Safari Web Inspector

In Safari, JavaScript debugging is done through the Sources panel in the Web Inspector. Before we can access it, we’ll need to enable the Develop menu by going to Preferences → Advanced and checking Show Develop menu in menu bar.

Once enabled, open the menu and select Show Web Inspector, then navigate to the Sources tab to prepare the scripts for debugging.

Now, let’s debug JavaScript with developer tools

Set breakpoints in JavaScript

To begin debugging, the first thing to do is to set breakpoints. Breakpoints are the logical point you want the code execution to pause, so that you can debug it.

DevTools allows us to set breakpoints in many different ways. As we get into the process of debugging our application, we’ll cover how to set them…

  • At the line of code.
  • At conditional statements.
  • At the DOM node.
  • On Event listeners.

Set breakpoints at the line of code

To set a line-of-code breakpoint:

  • Click the Sources tab.
  • Browse the source file from the File navigation section.
  • Go to the line of the code in the Code Editor section on the right.
  • Click on the line number column to set a breakpoint on a line.
Image
Figure 5: Set a line-of-code Breakpoint

Here we have set a breakpoint at the line number 6. The code execution will be paused here.

Tips: Use this when you do not know the exact region of the code to investigate. Even if you just start from somewhere, based on a random guess, it will lead to the bug eventually. You can also set up multiple line-of-code breakpoints and investigate. We will see that in the latter part of the article.

Set a conditional breakpoint

Conditional breakpoints allow us to set conditions which will determine whether the debugger breaks or skips. To set a conditional breakpoint:

  • Click the Source tab.
  • Browse the source file from the file navigation section.
  • Go to the line of the code in the code editor section on the right.
  • Right-click on the line number and select the Add conditional breakpoint option.
Image
Figure 6a: Right-click on the line number

  • A dialog box appears below the line of code. Start typing the condition. As we type, we see the autocomplete option suggesting we pick up a condition.
Image
Figure 6b: Enter a condition
  • Press Enter to activate the breakpoint. An orange icon should appear on top of the line number column.
Image
Figure 6a: A conditional breakpoint has been activated

The code execution will be paused whenever the function print() is invoked with the name Joe.

Tip: Use the conditional breakpoint when you know the specific region of code to investigate. As you may be aware of the region of the code, you can inspect further using conditions to find the root cause of the problem.

Set breakpoint on event listeners

In JavaScript, an event listener waits for an event to happen, then emits a response. We can set breakpoints on these event listeners via the following method:

  • Click the Sources tab.
  • Expand the Event Listener Breakpoints pane in the debugger section.
  • Select the list of event listeners from the category list to set breakpoints. We have a button click event in our application. We will be looking to select the click checkbox under the mouse option.
Image
Figure 7: Set a breakpoint on the click event listener

Tips: Use this when you want to pause the event listener code that runs after an event is fired.

Set breakpoint at the DOM node

Another great point about DevTools is the power it offers with DOM inspection and debugging. In fact, we can set breakpoints to pause a code execution whenever something is added, removed or, changed in the DOM.

To set breakpoints on DOM, all we’ve got to do is:

  • Click the Elements tab.
  • Go to the element that we want to set the breakpoint on.
  • Right-click on the element to get a context menu. Then, select Break on and choose one of: Subtree modifications, Attribute modifications, or Node removal.
Image
Figure 8: Adding a breakpoint on the DOM change

As shown in the above figure, we are setting a breakpoint on the DOM change of the output DIV, with a condition of Subtree modifications. We are aware that a greeting message will be added into the output DIV, and the subtree will be modified to break on it.

Tips: Use this when you suspect a DOM change is causing the bug. The related JavaScript code execution will be paused automatically when it breaks on the DOM change.

Use the JavaScript debugger statement

The debugger statement is a built-in JavaScript feature that tells the browser to pause code execution at that exact line, just like a manual breakpoint.

To use this feature, we can insert debugger; in our code where we want execution to stop. When the browser runs that line with DevTools open, it will pause automatically, allowing us to inspect variables, check the call stack, and step through code from that point.

This is especially useful when we can’t easily set a breakpoint in the Sources panel, for example, inside dynamically generated code or complex event handlers. One thing though: we must remember to remove or comment out debugger; before deploying to production.

Step through JavaScript code line by line

Now, we know all the important methods to set breakpoints. However, it’s not enough to understand them individually; in a complex debugging situation, we may have to use them in combination. So, let’s look at how to step through the breakpoints to figure out an issue.

The debugger section provides five controls to step through the code.

Image
Figure 9: Step through controls

Step (Key shortcut – F9)

This option enables us to step through line by line as the JavaScript code executes. If there is a function call on the way, the step-through also gets inside the function, executes it line by line, and then steps out of it.

Image
Figure 9a: Performing step line-by-line

Step Over (Key shortcut – F10)

Occasionally, we may be certain that some functions are working properly and not want to spend time inspecting them. In this situation, we can execute a function without stepping into it, using step over.

In the example below, we are stepping over the logger() function.

Image
Figure 9b: Step Over the function

Step Into (Key shortcut – F11)

When stepping through, we may get the sense that a function is behaving unexpectedly and want to inspect it. In this case, we can use step into to get inside the function and debug.

In the example below, we are stepping into the function logger().

Image
Figure 9c: Step into the next function call

Step out (Key shortcut – Shift + F11)

While stepping through a function, we may not want to continue and come out of it. In this situation, the step out feature gives us a clean exit.

In the example below, we are stepping inside the logger() function and then stepping out of it immediately.

Image
Figure 9d: Step out of the current function

Resume/Jump (key shortcut – F8)

At times, we may want to jump from one breakpoint to another without debugging any code in between. This option allows us to jump to the next breakpoint.

Image
Figure 9e: Resume or Jump to the next breakpoint

Use the JavaScript debug console to inspect and test code

The JavaScript Debug Console lets us interact directly with our page’s code while it’s running. Combined with console.log() and related methods (console.warn(), console.error()), it’s one of the fastest ways to inspect and test code.

Here’s how to use it effectively:

  • Open the console in the browser’s DevTools.
  • Add console.log() in the code to output variable values or checkpoints.
  • Check logs for errors, warnings, or messages you’ve added.
  • Type variable names to see their current values instantly.
  • Run JavaScript commands to test changes without reloading.
  • Call functions manually to verify behavior.

💡 With Bugfender, all console outputs can be captured remotely, so you can review them even for production issues.

Inspect global scope and this in JavaScript

Using the scope panel, we can find out what the global scope contains and what its variables are. We can also find out the value of the this keyword.

Image
Figure 10a: Scope panel

Debug function execution with the call stack

The call stack panel helps to identify the function execution stack.

Image
Figure 10b: Call stack

Inspect variable and expression values in DevTools

Inspecting values is the primary way to identify a bug in our code. When stepping through, we can inspect a value simply by doing a mouseover on a variable.

In the example below, we are selecting the variable name to inspect its value at the code execution stage.

Image
Figure 10c: Inspect a value with mouseover

Additionally, we can select a section of the code as an expression to check the value of it. In the example below, we have selected an expression document.getElementById('m_wish') to inspect the value.

Image
Figure 10d: Inspecting value of an expression.

Monitor JavaScript expressions with the watch panel

The Watch section enables us to add one or more expressions and watch their values at execution time. This feature is very useful when we want to run some computation outside our code logic.

We can combine any variables from the code region and form a valid JavaScript expression. At the time of stepping through, we will be able to see the value of the expression.

Here are the steps required to add a Watch:

  • Click on the + icon above the Watch section
Image
Figure 11a: Add a watch expression
  • Add an expression to watch. In this example, we have added a variable wish to watch its value.
Image
Figure 11b: Watch expression value

Another way to watch for an expression is from the console drawer. Take a look at the example below.

Image
Figure 11c: Activate the console drawer

Disable & remove breakpoints

To disable all the breakpoints at once, we can click on the Deactivate Breakpoints button (it is circled below.)

Image
Figure 12a: Disable all breakpoints

Note that the above method doesn’t remove the breakpoints. It just deactivates them for the duration we require. To activate the breakpoints, just click on the same button again.

We can remove one or more breakpoints from the Breakpoints panel by unchecking the checkboxes. We can remove all the breakpoints permanently by doing a right-click and selecting the Remove all breakpoints option.

Image
Figure 12b: Remove one, more or all the breakpoints

Finally, apply the fix

Now, given all that we’ve covered so far, some readers might already have figured out the final step to make the Greet Me app functional as expected. But in case not, all we need to do is add an extra + before the wish variable while constructing the message.

// This is the line where the issue is.
// Identify the extra '+' before the wish.
const message = 'Hello ' 
                        + name 
                        + ', Your wish `' 
                        + + wish 
                        + '` may come true!';

Now, how can we find this in a realistic debugging scenario? Well, take a look at this short video demo (without audio):

JS Debugging: Greet Me app Fix

We can also play around with the fixed version from here.

Part 3: How to debug JavaScript with Visual Studio code

As well as specialist debugging tools, we can also use the built-in features provided by code editors. And Visual Studio is one of the best around (we think, anyway).

Visual Studio code is ridiculously simple, and we can enable a similar kind of debugging environment using VS Code with just a few simple steps.

VS code setup for debugging

VS Code has several extensions (like plug-ins) for enabling various features and capabilities. To enable JavaScript debugging, we need to install an extension called Debugger for Chrome. You can install it in either of these ways:

  • Go to the Debugger for Chrome homepage and click on the Install button. This will launch the VS Code and start the installation for you automatically.
  • You can search this extension in the Extensions panel of VS Code and install.
Image
Figure 13a: VS Code extension install

After installation, click on the Run option from the left and create a configuration to run/debug a JavaScript application.

Image
Figure 13b: Enable debugging with configuration

A file called launch.json will be created with some settings information in it. It may look like this:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: <https://go.microsoft.com/fwlink/?linkid=830387>
    "version": "0.2.0",
    "configurations": [
        {
            "type": "chrome",
            "request": "launch",
            "name": "Debug the Greet Me app",
            "url": "<http://localhost:5500>",
            "webRoot": "${workspaceFolder}"
        }
    ]
}

Note that we can change the following parameters:

  • name: This means anything suitable to our app. Changing the name is optional.
  • url: The URL that our app is running on locally.
  • webRoot: By default the value is ${workspaceFolder}, which is the current folder. We may wish to change it to the entry point folder where a file like index.html is located.

The last step is to start the debugging by clicking the small play icon at the top-left corner.

Image
Figure 13c: Start debugging

Understanding VS Code debugger panels

VS Code provides similar tools to DevTools for debugging JavaScript, and it is broadly similar to the Google Chrome JavaScript debugger we have seen so far in this article.

Here are the primary sections we should be aware of:

  1. Enable debugging. Press the play button to enable the debugging option.
  2. Controls for stepping through the breakpoints and to pause or stop debugging. This is similar to what we have seen with Chrome DevTools, except some of the keyboard shortcuts may differ.
  3. Setting breakpoints on the source code. This is similar.
  4. The scope panel to see variable scopes and values. These are completely the same in both cases.
  5. The watch panel to create and watch expressions.
  6. The call stack of the execution function.
  7. The list of breakpoints to enable, disable and remove.
  8. The debug console to read the console log messages.
Image
Figure 13d: Anatomy of the VS Code debugging controls

A quick demo

Here is a quick demo (1 minute) to showcase the VS Code debugging control usages.

JS Debugging: Setting up VS Code for debugging

JS Debugging: Setting up VS Code for debugging

Part 4: Tips and hacks to debug JavaScript faster

Quick gains when debugging

By focusing on the right parts of our code and using modern DevTools features,, we can quickly identify the issues and fix them in minutes, not hours.

  • Focus on key code areas first — start with recent changes, complex functions, or failing tests.
  • Use console.table() — render arrays and objects as readable tables to quickly identify bad values.
  • Unminify minified code — use Pretty Print in DevTools to expand bundled code into readable lines.
  • Black box irrelevant scripts — hide vendor or library code so you only step through your logic.

How to avoid common JavaScript bugs

Most bugs aren’t mysterious — they come from a handful of avoidable mistakes. With the right practices, you can prevent many issues before they appear.

  • Use strict mode — catch undeclared variables and silent errors early.
  • Validate user input — never trust raw input; sanitize and check types.
  • Handle null/undefined properly — guard conditions prevent runtime crashes.
  • Test in multiple browsers — quirks in Chrome, Safari, and Firefox often differ.
  • Automate testing — unit tests and linters spot regressions before release.
  • Log and monitor with Bugfender — catch errors happening in real user devices, not just your test environment, so you can fix issues before they spread (sorry, we had to throw this one in there before we signed off!)

Summary

To sum up what we’ve gone through today:

  • No matter how savvy we are as devs, it’s always better to use a specialist tool to debug JavaScript code, rather than rely on our own smarts. A tool like the Google Chrome DevTools or VS Code debugger extension is much better than relying onconsole.log(), for all its advantages.
  • DevTools Source Panel is extremely powerful, with the capability to inspect variable values, watch expressions, understand scopes, read the call stack etc.
  • There are several ways to set breakpoints and we should use them based on the debugging situation.
  • Managing breakpoints is simple with DevTools.
  • The VS Code debugger extension is equally powerful and a must-try.

💡 And one last thing: Even when using a debug tool for local development, remember that it’s good to add meaningful console.log() commands, because with Bugfender you will be able to gather information about problems that are happening when the app is already in production.

Expect The Unexpected!

Debug Faster With Bugfender

Start for Free
blog author

Aleix Ventayol

Aleix Ventayol is CEO and co-founder of Bugfender, with 20 years' experience building apps and solutions for clients like AVG, Qustodio, Primavera Sound and Levi's. As a former CTO and full-stack developer, Aleix is passionate about building tools that solve the real problems of app development and help teams build better software.

Join thousands of developers
and start fixing bugs faster than ever.