The debugger statement
debugger is a statement that sets a breakpoint right inside the code. When the JavaScript interpreter reaches that line, execution pauses and the developer gets access to variable values, stepping, the call stack and the whole execution context.
Theory
TL;DR
debugger;pauses execution on its own line, it is the language level equivalent of a breakpoint.- The syntax is minimal: one word, no arguments, no parentheses.
- It fires only when DevTools is open or a debugger is attached, otherwise the line is ignored.
- While paused you get Scope, Call Stack, a console bound to the paused frame and the
Step over,Step into,Step outcontrols. - Putting it under an
ifis handy: you stop only on the problematic data. debuggermust not reach production code: strip it with a linter or a bundler setting.
Quick example
function sum(a, b) {
debugger; // execution will stop on this line
return a + b;
}
sum(2, 3);What happens:
- If DevTools is closed, the code just runs as if
debuggerwere not there. - If DevTools is open, execution stops on the
debuggerline. You see the program state, the local variables and the call stack.
Syntax
debugger;No arguments, no parentheses, just one word. It is a real language statement rather than a function, so you cannot call it as debugger() or pass it around as a value.
Conditional pauses and loops
The most useful pattern is to put debugger behind a condition so that it stops only in the case you care about. The code keeps running normally and halts only when the data is genuinely interesting.
function checkUser(user) {
if (!user.isActive) {
debugger; // debug only the problematic users
}
return user.name;
}The same trick saves you in loops, where stopping on every iteration would be unbearable:
for (let i = 0; i < 5; i++) {
if (i === 3) debugger; // pauses only when i is 3
console.log(i);
}Console output (with DevTools open):
0
1
2and then execution stops at i = 3, where you can inspect variables, the stack and the context.
Debugging async/await and error handling
The statement works inside asynchronous functions too. It is a convenient way to see exactly what the server returned before the response body is parsed:
async function fetchData() {
const response = await fetch('https://api.example.com/users');
debugger; // inspect the response before parsing JSON
const data = await response.json();
return data;
}
fetchData();In the same way debugger goes into a catch block so that you can analyse the error immediately instead of guessing from the console text:
try {
const user = JSON.parse('{"name":"Tom"}');
console.log(user);
} catch (err) {
debugger; // inspect err right where it happened
console.error('Parse failed:', err);
}If the JSON turns out to be malformed, the debugger opens and shows the contents of err, the failing line and the call stack.
What is available while paused
When execution reaches debugger:
- the program is put on pause,
- the debugger panel opens in Chrome DevTools or in VS Code,
- and from there you can:
- inspect Scope Variables: locals, globals and closure variables,
- see the Call Stack, the chain of calls that led to this point,
- run arbitrary code in the Console, in the context of the paused function,
- move step by step:
Step over,Step into,Step out, - change variable values while paused and resume execution with the new data.
The difference between debugger and console.log()
| Feature | console.log() | debugger |
|---|---|---|
| Shows values | Yes | Yes, in the debugger UI |
| Pauses execution | No | Yes |
| Shows the call stack | Partly, via console.trace() | Fully |
| Works without DevTools | Yes | No |
| Lets you change variable state | No | Yes, right while debugging |
Summary:
| Question | Answer |
|---|---|
| What it does | Stops code execution on its line |
| When it fires | Only with DevTools open or a debugger attached |
| What you can do while paused | Inspect variables and the call stack, execute code step by step |
| When to use it | On tricky logic bugs, once console.log no longer helps |
| What it does not do | Prints nothing to the console and has no effect without DevTools |
Common mistakes
- Leaving
debugger;in a production build. For a user with DevTools open the page simply freezes. Theno-debuggerlint rule plus stripping at build time solves this. - Assuming
debuggerlogs something. It prints nothing at all: if you need a trace in the console, that is a separateconsole.log()orconsole.trace(). - Expecting a pause with DevTools closed. Without an open panel or an attached debugger the statement has no effect, and that is not a bug.
- Putting
debuggerin a hot loop without a condition. Stopping on every iteration makes debugging impossible; add anifas in the example above. - Forgetting that the pause freezes the whole page. Timers, animations and network responses all wait, so any timing measured during a pause is meaningless.
- Debugging minified code without source maps.
debuggerwill stop, but you will be looking at an unreadable line; enable source maps.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.