Debugging JavaScript with Breakpoints: Complete Guide

Stop guessing what your code does. Learn every breakpoint type in Chrome DevTools: line breakpoints, conditional breakpoints, logpoints, DOM breakpoints, and event listener breakpoints.

7 min read

Breakpoints let you pause JavaScript execution at a specific line and inspect everything: variable values, the call stack, and the current scope. They are more powerful than a print statement because you do not need to know what to log in advance.

You can explore the entire program state at the moment of pause, then step forward one line at a time to watch it change.

Open Chrome DevTools (F12 or Ctrl+Shift+I), click the Sources tab, and navigate to your JavaScript file in the left file tree. You are ready to set breakpoints.

Line breakpoints

The simplest breakpoint: click a line number in the Sources panel. A blue marker appears. When that line is about to execute, the debugger pauses.

javascriptjavascript
function calculateTotal(items) {
  let total = 0;
  for (const item of items) {
    total += item.price; // Click the line number here to set a breakpoint
  }
  return total;
}

When paused, you see:

  • Scope pane (right): all local variables, closure variables, and global variables with their current values
  • Call Stack pane: the chain of function calls that led to this point
  • The line itself: highlighted in blue, about to execute

From here you can inspect anything. Hover over the item variable to see its value, type total in the Console to check the running sum, or open the Scope pane to browse every variable at once.

Stepping through code

Once paused, use these controls to move forward:

KeyActionWhat it does
F9ResumeContinues execution until the next breakpoint
F10Step OverRuns the current line and moves to the next one in the same function
F11Step IntoIf the current line calls a function, enters that function
Shift+F11Step OutRuns the rest of the current function and pauses at the caller
javascriptjavascript
function outer() {
  const x = computeX(); // Step Over: runs computeX, pauses on next line
  const y = process(x); // Step Into: enters the process function body
  return y;
}

Step Over is for when you trust the function being called and just want to see the result. Step Into is for when you need to debug inside that function.

Step Out is your escape hatch when you have seen enough inside a function and want to jump back to the caller.

Conditional breakpoints

Right-click a line number and choose Add conditional breakpoint. Enter any JavaScript expression, and the debugger pauses only when that expression evaluates to true:

javascriptjavascript
for (let i = 0; i < 10000; i++) {
  process(items[i]);
  // Conditional breakpoint on next line: i === 5000
}

The loop runs 10,000 times but the debugger pauses only once, when the counter reaches 5,000. Without the condition, you would have to press Resume 5,000 times.

Common conditional breakpoint patterns:

ConditionUse case
i === 99Pause on the 100th loop iteration
item.id === 42Pause when processing a specific data item
value === undefinedPause when a variable unexpectedly becomes undefined
error.message.includes("timeout")Pause only on timeout errors
items.length > 100Pause when a list grows too large

Logpoints

A logpoint prints a message to the console every time the line would execute, without pausing. Right-click a line number and choose Add logpoint:

javascriptjavascript
function processItems(items) {
  for (const item of items) {
    applyDiscount(item); // Logpoint: "Processing item", item.id, item.price
    updateUI(item);
  }
}

Each iteration prints the item ID and price. The code runs at full speed, no pausing, but you see the values flow through the console.

Use logpoints instead of adding temporary print statements to your code. They live in DevTools, not in your source files, so there is no risk of committing debug logging to production.

DOM breakpoints

DOM breakpoints pause when a specific DOM element changes. Right-click an element in the Elements panel and choose:

  • Break on subtree modifications -- pauses when a child element is added, removed, or changed
  • Break on attribute modifications -- pauses when an attribute like class, style, or disabled changes
  • Break on node removal -- pauses when the element is removed from the DOM

These are essential for tracking down "who changed my element?" bugs. Instead of searching every line that touches the DOM, set a DOM breakpoint, trigger the bug, and the debugger pauses at the exact line that modified the element.

Event listener breakpoints

In the Sources panel, expand the Event Listener Breakpoints section in the right sidebar. You can pause on entire categories or specific events:

  • Mouse > click -- pauses when any click event fires
  • Keyboard > keydown -- pauses when any key is pressed
  • Timer > setTimeout -- pauses inside setTimeout callbacks

This is how you find what code runs when a user clicks something, without searching for every event listener registration in the codebase. Enable the breakpoint, click the element, and the debugger pauses inside the handler.

The debugger statement

You can set a breakpoint directly in your code with the debugger keyword:

javascriptjavascript
function suspiciousFunction(data) {
  const result = transform(data);
  debugger; // Pauses here if DevTools is open
  return validate(result);
}

When DevTools is open, the debugger statement acts as a hardcoded breakpoint. When DevTools is closed, it has no effect.

This is useful during development, but remember to remove these statements before committing. A linter rule can catch any that slip through.

Watch expressions

In the Sources panel, the Watch section lets you monitor any JavaScript expression that updates with each step:

javascriptjavascript
// Add these watch expressions:
//   items.length
//   total + tax
//   user.name !== ""

Watch expressions re-evaluate every time the debugger pauses or steps. They are good for tracking derived values that are not simple variables, such as a filtered array's length or the number of keys on an object.

Practical debugging workflow

When you encounter a bug, follow this sequence:

  • Reproduce the bug. Know the exact steps that trigger it.
  • Set a breakpoint at the earliest point where you suspect things go wrong.
  • Trigger the bug so the debugger pauses.
  • Inspect the Scope pane. Are variables what you expect?
  • Step through line by line with F10. Watch values change.
  • Identify the exact line where a value diverges from what you expect.
  • Fix the code, remove breakpoints, and verify.
Breakpoint debugging workflow

Most bugs are found on the first or second breakpoint. If the first breakpoint shows correct values, step forward. If the values are wrong at the first breakpoint, the bug is earlier in the call stack. Use the Call Stack pane to click up to the caller and inspect where the bad value originated.

For performance-focused debugging, see the Chrome DevTools performance tuning guide. For programmatic error handling, see the try...catch guide.

Rune AI

Rune AI

Key Insights

  • Click a line number in Sources to set a regular line breakpoint.
  • Right-click and choose Conditional Breakpoint to pause only when an expression is true.
  • Use logpoints to print values in hot code without stopping execution.
  • DOM breakpoints pause on subtree modifications, attribute changes, or node removal.
  • Event listener breakpoints pause when specific events fire, like click or keydown.
  • Use Step Over (F10), Step Into (F11), and Step Out (Shift+F11) to navigate paused code.
RunePowered by Rune AI

Frequently Asked Questions

What is the difference between Step Over and Step Into?

Step Over (F10) executes the current line and moves to the next one in the same function. Step Into (F11) enters the function being called on the current line so you can debug inside it.

Can I set breakpoints that only pause on the 100th iteration of a loop?

Yes, use a conditional breakpoint. Right-click a line number, choose 'Add conditional breakpoint', and enter a condition like `i === 99`. The debugger pauses only when the condition is true.

What is a logpoint and when should I use it?

A logpoint prints a message to the console without pausing execution. It is useful in hot loops or frequently called functions where stopping would be impractical, but you still want to see variable values.

Conclusion

Breakpoints turn debugging from guesswork into certainty. Line breakpoints let you pause anywhere. Conditional breakpoints let you pause only when it matters. Logpoints let you observe without stopping. DOM and event listener breakpoints catch problems that line breakpoints miss. The debugger is the most powerful tool in your DevTools. Use it before console.log.