7 Minutes
JavaScript Debugging in Chrome
Fix Bugs Faster! Log Collection Made Easy
What is JavaScript debugging in Chrome?
Imagine you’re mechanic trying to fix a car.
There’s this magic piece of kit that allows you to pause the engine and see inside every moving part. You can tweak parts live, test changes instantly and measure which parts are slowing the whole thing down.
This is JavaScript debugging in Chrome.
Using Chrome DevTools, you can pause execution, inspect variables and scope, and follow code as it runs. So you can see what the code is actually doing at runtime, without assumptions.
Today we’re going to dive into the technology: every gear, every lever, every efficiency gain. Here’s a full rundown of what we’ll cover.
How to debug JavaScript in Chrome
Like most aspects of coding, effective debugging is about a repeatable process, not random trial and error.
In real projects, JavaScript debugging in Chrome usually follows this workflow:
- Open DevTools and the Sources panel
- Reproduce the bug consistently
- Pause execution at the right moment
- Add targeted breakpoints
- Reload correctly
- Inspect call stack and runtime values
- Step through execution
- Verify the fix and prevent regressions
Each step answers a specific JavaScript debugging question:
- Where does execution stop?
- Which values are wrong (and when do they change)?
- What line or condition introduces the bug?
(If you’ve got this far and you’re thinking ‘this is too narrowly focused on Chrome DevTools’ no problem: we’ve got a broader guide on JavaScript debugging).
A real JavaScript bug example
To anchor this run-through, we’ll keep running it back to a small demo app called Greet Me.
What the app does:
- Takes a name and a short message.
- Builds a greeting string.
- Produces an incorrect output containing
NaN.
There’s no exception and no Console error.
The app runs, but the result is wrong – a scenario that you may have seen before if you’ve debugged with JavaScript.
We’ll use this as a reference example to explain where each debugging step applies, even when the interaction isn’t shown inline.
- Try the demo: https://greet-me-debugging.vercel.app/
- Clone the source: https://github.com/atapas/greet-me-debugging
- Watch the fix walkthrough: https://www.youtube.com/watch?v=SNZbq0qctzE
Step 1: open Chrome DevTools
To control JavaScript execution, open Chrome DevTools and switch to the Sources panel.
You can open DevTools by:
- Right-clicking the page → Inspect
- Using the shortcut
- macOS:
⌘ + ⌥ + I - Windows/Linux:
Ctrl + Shift + I
- macOS:
- Chrome menu ⋮ → More tools → Developer tools
Once open:
- Select the Sources tab
- Make sure the code editor and debugger panels are visible
For a full overview of the interface, see Chrome DevTools and its panels.
Step 2: reproduce the bug consistently
For effective debugging, the bug must be repeatable.
In this app we:
- Enter a name and a wish
- Click Create Greeting
- The output includes
NaN
As we mentioned, in this example the Console error appears and the app keeps running.
As long as the same steps always produce the same wrong result, we’re ready to pause execution and inspect what’s happening.

Step 3: pause JavaScript execution
This bit’s crucial: we must stop execution at exactly the right time.
| Pause method | When to use it |
|---|---|
| Automatic pause in Chrome DevTools | Stops execution when an error is thrown or a breakpoint is hit. Best when failures happen deep in the call stack or the breaking line isn’t obvious yet. |
debugger statement | Pauses execution exactly at that line in code. Useful for hard-to-reach paths, loops, or dynamic conditions. Works consistently across reloads. |
| Pause from the JavaScript Console | Interrupts execution on demand without editing source files. Ideal for exploratory debugging when the execution path is unclear. |
Once execution is paused, inspection works the same regardless of how the pause happened.
If execution pauses unexpectedly or not at all, see JavaScript debugger statement explained.
Step 3a: Set JavaScript breakpoints
Breakpoints are great because they let us stop execution only where it matters.
| Breakpoint type | When to use it |
|---|---|
| Line-of-code | When a specific block looks suspicious |
| Conditional | When the bug appears only for certain values |
| Event listener | When debugging clicks or user actions |
| DOM | When issues are triggered by page updates |
For a full walkthrough, see How to set and use JavaScript breakpoints.
Step 4: reload the page correctly
With DevTools open and the breakpoints set:
- Reload the page normally (
⌘R/Ctrl+R) - Reproduce the bug
If scripts appear stale:
- Right-click Reload
- Select Empty Cache and Hard Reload
Use hard reloads only when caching interferes with debugging.
Step 5: understand what’s happening in the code
Once execution is paused, the goal is to understand the current runtime state before changing anything. At this stage, Chrome DevTools helps us answer three questions:
- How did execution get here?
- What data is the code working with right now?
- Which assumptions are already broken?
| DevTools signal | What it tells you |
|---|---|
| Call stack | Which functions were executed and in what order |
Scope & this | Which variables are available and what context the code is running in |
| Value inspection | Whether variables and expressions hold the expected values |
| Watch expressions | How key values change as execution progresses |
In many cases, the root cause handily reveals itself at this point.


Monitor expressions with the Watch panel
The Watch panel gives us a microsocope to track how specific values change while execution is paused.
Instead of repeatedly inspecting the same variables, we can pin expressions and observe their values as execution moves forward. This is especially useful for derived values, conditions, or state that changes across multiple function calls.
To add a watch expression:
- Click the
+icon in the Watch panel - Enter any valid JavaScript expression (for example, a variable or computed value)

Once added, the expression updates automatically whenever execution pauses, making it easy to see whether values evolve as expected.

Watch expressions can also be managed from the Console drawer, which provides quick access while inspecting state.

Using the JavaScript console while paused
While execution is paused at a breakpoint, the Console can be used to:
- Inspect variable values.
- Test expressions in the current scope.
- Call functions without reloading the page.
This is useful for quick checks and validating assumptions without modifying source code.
For deeper console usage patterns and logging techniques, see JavaScript console.log explained
Step 6: step through JavaScript execution
Once execution is paused and the runtime state is understood, we can start moving execution forward in a controlled way.
Stepping lets us follow the exact path JavaScript takes, without guessing or adding logs.
| Action | When to use it |
|---|---|
| Step | Move line by line and enter function calls |
| Step over | Skip over functions you trust |
| Step into | Inspect a function that may be misbehaving |
| Step out | Exit the current function early |
| Resume | Jump to the next breakpoint |

These controls help confirm where logic diverges from expectations and why.
If you need a more detailed breakdown of stepping actions, shortcuts, and real examples, see How to step through JavaScript code in Chrome DevTools
Step 7: apply the fix
So we’ve found the root cause.
The unintended unary plus forces JavaScript to coerce wish into a number, producing NaN when the value is not numeric.
Removing that extra + fixes the issue.
// Fix: remove the unintended unary '+'
const message ='Hello '
+ name
+', Your wish `'
+ wish
+'` may come true!';
This change ensures the wish value is treated as a string, not coerced into a number.
After applying the fix, the greeting renders correctly and no longer produces NaN.
You can also test the behavior in the fixed version of the app.
Step 8: verify the fix and prevent regressions
After applying the fix, it’s important to confirm that the original issue is resolved and that no new problems were introduced.
At this stage, we should:
- Re-run the original reproduction steps
- Confirm the incorrect output (
NaN) no longer appears. - Remove temporary breakpoints or
debuggerstatements. - Consider adding guards or defaults to prevent similar issues.
Note that the debugging process complete once we’ve done the verification stuff.
Final takeaway: a practical JavaScript debugging workflow
JavaScript debugging in Chrome works best when bugs are local, visible, and reproducible.
Following a structured process—pausing execution, inspecting runtime state, stepping through code, and verifying fixes—helps us understand why issues happen instead of guessing.
Chrome DevTools are usually enough.
However, they reach limits when bugs:
- Only affect real users
- Can’t be reproduced locally
- Happen in production-only conditions
In those cases, collecting real runtime data becomes necessary.
Tools like Bugfender complement Chrome DevTools by capturing logs and runtime context from real user sessions, without requiring reproduction.
Use DevTools for what you can see.
Use production logging for what you can’t.
Expect The Unexpected!
Debug Faster With Bugfender