Dev Tools

VSCode Debugging Primer: From launch.json to Hovering Over a Variable

Compared with sprinkling console.log everywhere, debugging lets you pause and inspect the call stack and every variable at each step. Starting from editing launch.json, this guide gets Node and front-end projects debugging, then walks through the sidebar, watch expressions and conditional breakpoints.

By LaoHand Team·7 min read·Updated 2026-09-06

What Breakpoint Debugging Is and How It Differs from console.log

console.log only prints where you anticipated, and every debug cycle means a full run, then re-editing and re-running. Breakpoint debugging pauses the program at the suspicious line, where you can inspect every in-scope variable, read the call stack and step line by line — without deciding in advance what to log.

It shines on complex logic: when a value hops across several functions, you can follow the stack to see where it came from and where it got overwritten. VSCode ships a built-in debugger, and Node, browser, Python and Go each have extensions, but the entry point is always the same .vscode/launch.json.

// .vscode/launch.json —— minimal working config
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch current file",
      "program": "${file}"
    }
  ]
}

Get Node and Front-End Debugging Running

The classic Node pitfall is "I set a breakpoint and it never fires". The cause is usually that the entry you launch is not the file you set the breakpoint on: launch the current file and point program at the right path, or configure your npm script as the launch target.

Front-end (Vite/Webpack) needs source maps so the debugger can map compiled code back to your sources. Using pwa-chrome, set the launch config to open a URL, which may combine a webServer target or simply attach to a running dev server. The difference between attach and launch: request: launch means the debugger spawns the process; attach connects to one already running.

{
  "type": "pwa-chrome",
  "request": "launch",
  "name": "Debug Vite",
  "url": "http://localhost:5173",
  "webRoot": "${workspaceFolder}/src",
  "sourceMapPathOverrides": {
    "webpack:///./src/*": "${webRoot}/*"
  }
}

Five Actions after Hitting a Breakpoint

After a breakpoint hits, the debug toolbar holds the key actions: Continue (F5) to the next breakpoint, Step Into (F11) to dive into a called function, Step Over (F10) to run the current line without entering functions, Step Out (Shift+F11) to finish the whole function and land back at the caller, plus restart/stop.

Knowing the difference is debugging core skill: Step Into drops you inside the current line’s function, ideal for investigating internal logic; Step Over runs the whole line at once, for viewing this layer’s flow; Step Out quickly ends the current function to see what it actually returns, then lands one level up.

To hop straight into a specific branch on a conditional, make the breakpoint conditional, or use Jump to Cursor (right-click) to move the execution pointer directly to the target line instead of pressing F5 repeatedly. You can remap these keys in the keyboard shortcuts panel, but the defaults already cover most scenarios.

F5            Continue / start debugging
F10           Step over (do not step into called functions)
F11           Step into (enter called functions)
Shift + F11    Step out of the current function
Shift + F5     Stop debugging
Right-click a line number -> Add Conditional Breakpoint  conditional breakpoint

Four Checks for "Breakpoint Not Firing" and "Changes Not Applying"

The most common stalls: the current line never executes because the run went down another branch; source mapping fails and the breakpoint shows gray/hollow (not hit), meaning source map or webRoot is wrong; you changed code but the debugger still runs the old version, so restart the debug session if using -w; or a type/path casing mismatch breaks the mirror across files.

Self-check in order: is the breakpoint solid filled → is the debugged process freshly started → do the breakpoint file and program/webRoot share the same source → does DEBUG CONSOLE show any source-map error. Doing the first two resolves most "not working" cases.

# Force a window reload and confirm how the debug process restarts
# Node: verify --inspect / --watch behavior in the launch command
"runtimeArgs": ["--inspect=9229", "--watch"]
# If a breakpoint is grey/hollow, check that webRoot points to the source directory

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.