How do you debug JavaScript code?
By FrontendPro Editorial Team Updated 9/26/2026
Answer
The short answer: a breakpoint in DevTools beats a console.log, because it lets you inspect every variable in scope instead of the one you thought to print.
The debugger
Open Sources in DevTools, click a line number, reload. When execution pauses you get the full scope chain, the call stack, and a console that evaluates in the paused frame. Step over (F10), step into (F11), resume (F8).
Beyond plain line breakpoints:
- Conditional breakpoint — right-click a line, add
id === 42. Essential inside a loop. - Logpoint — same menu, prints an expression without pausing. A
console.logyou do not have to commit. - DOM breakpoint — right-click a node in Elements, break on attribute or subtree change. Finds whoever is mutating your element.
- Event listener breakpoint — break on every
clickanywhere, to find an unknown handler. - XHR breakpoint — break when a URL containing some string is requested.
debugger;in the source, when the line is hard to reach by clicking.
Better console usage
console.table(rows); // arrays of objects, readably
console.log({ user, items }); // keeps the names — better than two logs
console.trace(); // how did I get here
console.time('x'); console.timeEnd('x');
console.assert(total > 0, total); // only speaks up when wrong
The rest of the panels
Network for request payloads, status, and timing. Performance for a flame chart when something is slow. Memory for heap snapshots when something leaks. Application for storage and cookies.
Outside the browser
Node pauses for the same DevTools frontend with node --inspect-brk app.js. Source maps are what make any of this readable after bundling — if you are staring at minified code, the map is missing or not being served.
The technique that matters most
Reproduce reliably first, then bisect: comment out half, or git bisect across commits. Changing code before you can reproduce the bug reliably is how a fix turns into two bugs.
