March 26, 20265 min read

Debugging JavaScript Beyond console.log

Chrome DevTools breakpoints, conditional breakpoints, the debugger statement, network debugging, and the mindset that makes debugging faster.

javascript debugging chrome-devtools node
Ad 336x280

Everyone starts debugging with console.log. And honestly, it works. For simple stuff, throwing a log statement in and checking the output is the fastest path to understanding what's happening. No shame in it.

But there's a ceiling. When you're dealing with race conditions, complex state, or a bug that only reproduces under specific conditions, console.log starts to feel like searching a dark room with a lighter. You need better tools.

Chrome DevTools Breakpoints

Open DevTools (F12), go to the Sources tab, find your file, and click on a line number. That's a breakpoint. When execution hits that line, everything pauses and you can inspect every variable in scope.

What makes this better than console.log: you don't need to know in advance what to log. You can explore the entire call stack, hover over variables, and step through execution line by line.

Step controls matter:
  • Step Over (F10): Execute the current line and move to the next one
  • Step Into (F11): If the current line calls a function, jump inside it
  • Step Out (Shift+F11): Finish the current function and return to the caller
  • Continue (F8): Resume execution until the next breakpoint

Conditional Breakpoints

Right-click a line number in DevTools and select "Add conditional breakpoint." Enter a JavaScript expression. The breakpoint only triggers when that expression is truthy.

// Instead of:
if (user.id === 42) {
  console.log('found the problematic user', user);
}

// Set a conditional breakpoint on the relevant line with:
// user.id === 42

This is incredibly useful when you're debugging a loop that runs 10,000 times but only breaks on one specific iteration. No code changes, no redeployment.

Logpoints: console.log Without Touching Code

Right-click a line number, choose "Add logpoint." Type a message with expressions in curly braces: User: {user.name}, Status: {response.status}. DevTools logs to the console every time that line executes — no code modification required.

This is console.log for people who are tired of cleaning up console.log statements before committing.

The debugger Statement

Drop debugger; anywhere in your code. When DevTools is open and execution hits that line, it pauses as if you set a breakpoint. Useful when you're not sure which file to set the breakpoint in, or when the code is dynamically generated.

function processPayment(order) {
  debugger; // execution pauses here when DevTools is open
  const total = calculateTotal(order.items);
  // ...
}

Just don't commit it. Seriously. Add a linter rule: no-debugger.

Network Tab for API Issues

Half the bugs in web apps are data problems — the API returns something unexpected, a request fails silently, or the payload is wrong. The Network tab shows every HTTP request with the full request/response details.

Filter by XHR/Fetch to see only API calls. Click a request to see:


  • Headers: Was the auth token sent? Correct Content-Type?

  • Payload: What did the client actually send?

  • Response: What came back? Is it the shape you expected?

  • Timing: Did the request take 50ms or 5 seconds?


For intermittent issues, check "Preserve log" so the network history survives page navigations.

Node.js Debugging with --inspect

For server-side JavaScript, console.log debugging is even more tempting because there's no browser UI. But Node has a built-in inspector:

node --inspect server.js
# Or break on the first line:
node --inspect-brk server.js

Open chrome://inspect in Chrome, click the inspect link for your Node process, and you get the full DevTools experience for server-side code. Breakpoints, variable inspection, the call stack — everything.

VS Code also connects to the Node inspector natively. Add a launch configuration and you can debug without leaving your editor.

React DevTools (and Vue DevTools, etc.)

Framework-specific devtools are worth installing. React DevTools lets you inspect the component tree, see props and state for any component, and track what caused a re-render. The Profiler tab identifies performance bottlenecks.

The Components tab is especially useful for debugging: find a component, edit its state directly in the panel, and watch the UI update. Way faster than modifying code and refreshing.

The Debugging Mindset

Tools aside, debugging is a thinking process. Here's the loop:

  1. Reproduce: If you can't trigger the bug reliably, you can't fix it. Find the exact steps or conditions.
  2. Isolate: Narrow down where the problem is. Is it the API response? The state update? The rendering logic? Use binary search — comment out half the code and see if the bug persists.
  3. Understand: Before you fix it, understand why it's broken. The first fix that comes to mind is often a band-aid.
  4. Fix: Make the smallest change that addresses the root cause.
  5. Verify: Confirm the fix works and doesn't break anything else. Write a test if one doesn't exist.
The biggest debugging mistake is jumping to step 4 too early. Resist the urge to start changing code before you understand the problem.

Practice Makes It Faster

Debugging is a skill, and like any skill, it gets better with reps. Working through coding challenges on CodeUp is a great way to build that muscle — you hit bugs, you fix them, you build intuition for where things go wrong. That pattern recognition is what separates a 10-minute debug session from a 4-hour one.

Ad 728x90