ScreenshotNeo

BlogHow-to

How to Debug JavaScript in Your Browser’s Developer Console

Find JavaScript errors in the browser Console, trace them with breakpoints, inspect runtime state, and troubleshoot minified scripts with source maps.

By the ScreenshotNeo team4 October 20268 min read

To debug JavaScript in your browser, open the page’s developer tools, find the first relevant error in the Console, and follow its file and line reference into the source debugger. Set a breakpoint, reproduce the problem, then inspect the variables, scope, and call stack at the pause. The Console is useful for errors and quick expressions; the debugger is where you trace execution.

1. Open the Console and reproduce the problem

Open your browser’s developer tools using its current menu or official help, then select the Console panel. Panel names, layouts, and keyboard shortcuts vary by browser and operating system, so use the browser’s current instructions rather than relying on a universal shortcut.

  1. Open the page where the behavior occurs.
  2. Open developer tools and select Console.
  3. Clear old messages if needed, then reload the page or repeat the action that triggers the bug.
  4. Start with the earliest relevant error. Later errors may be consequences of the first failure.
  5. Record the message and its linked script file and line number. Exact wording can vary between browsers.

The Console can also evaluate JavaScript against the loaded page. Use it for small, read-only checks of values or DOM state. For example, on a page with an element whose ID is checkout, you can evaluate:

document.querySelector('#checkout')

If the result is null, the selector did not match an element in the current document at the time you ran it. That can mean the selector is wrong, the element has not been rendered yet, or it exists in a different frame or shadow root.

2. Read the error and locate its source

Select the file-and-line link attached to the Console message. Chrome’s source debugger is called Sources; Firefox calls its debugger panel Debugger. Other browsers have their own developer-tool layouts. The browser tools guide from MDN explains that these tools are built into browsers, while the MDN debugging guide covers the general workflow.

Read the nearby code, not just the reported line. A failure can be caused by an earlier value or call that only becomes invalid at the reported statement. Check whether the line belongs to your source, a dependency, an inline script, or a generated bundle.

3. Pause with a breakpoint and inspect execution

  1. In the source debugger, open the referenced script and click the line number next to a statement that will execute before or at the failure.
  2. Reload the page or repeat the user action that triggers the script.
  3. When execution pauses, inspect the current line, local variables, available scope, and call stack.
  4. Step through the next statements one at a time. Compare actual values with the values the code expects.
  5. Resume execution and repeat with a breakpoint closer to the first point where actual behavior diverges from expected behavior.

A breakpoint pauses execution so you can inspect the state at that point. The call stack shows how execution reached the paused function; scope shows the variables available there. Together they help distinguish a bad input from a bad assumption inside the function. See Chrome DevTools’ JavaScript debugging guide for its Sources workflow.

For a minimal example, save this as an HTML file and open it in a browser. Open the Console and debugger, then click the button. Set a breakpoint on the marked line to inspect total and the call stack:

<!doctype html>
<meta charset="utf-8">
<title>Breakpoint example</title>
<button id="add-item">Add item</button>
<pre id="output"></pre>
<script>
const items = [12, 8];

function renderTotal(values) {
  const total = values.reduce((sum, value) => sum + value, 0); // Set a breakpoint here.
  document.querySelector('#output').textContent = `Total: ${total}`;
}

document.querySelector('#add-item').addEventListener('click', () => {
  items.push(5);
  renderTotal(items);
});

renderTotal(items);
</script>

When paused, inspect values and total. Step over the calculation to see its result; step into a function call when you need to follow its implementation. Resume to let the page continue.

4. Use the Console to test a focused hypothesis

While paused, many browser debuggers let you evaluate expressions in the Console in the current execution context. You can inspect a variable or test a small expression without editing the page. For example, if execution is paused in a function with a values variable, evaluate:

values
values.map(value => typeof value)
values.reduce((sum, value) => sum + value, 0)

Outside a paused function, only globals and page state available in the Console’s current context can be referenced. A local variable from a function that has already returned is no longer available there. Treat Console edits as temporary: a reload usually resets page state, and an expression that mutates the page can affect subsequent debugging.

5. Pause with a debugger; statement

When setting a source breakpoint is awkward, put debugger; at the point you want to inspect:

function calculateTotal(items) {
  debugger;
  return items.reduce((sum, item) => sum + item.price, 0);
}

With debugging functionality attached, execution pauses there. MDN describes it as invoking available debugging functionality, such as setting a breakpoint. Without a debugger available, it has no effect. Remove the statement or make sure it is intentionally handled before shipping code, so users do not encounter an unexpected pause. See the MDN debugger reference.

6. Debug bundled or minified code with source maps

Production JavaScript is often bundled, transpiled, or minified. A source map can connect the deployed script to the original source files so the debugger can show code closer to what you wrote. Chrome documents source-map behavior in its DevTools source maps guide.

  • If original files appear in the debugger, set breakpoints in those files and reproduce the problem.
  • If only generated code appears, check that the deployed script references a source map and that the map is available to the browser.
  • If mappings point to the wrong lines or files, check that the map matches the exact deployed bundle. A stale map cannot reliably describe a newer build.
  • If source files are intentionally unavailable in production, reproduce the issue in a build that includes the matching maps, if your project permits it.

Source maps affect how code is displayed and mapped during debugging; they do not change the browser’s underlying runtime behavior.

7. Troubleshoot common debugging problems

Symptom Likely cause What to do
No error appears The issue may be a logic error, the triggering action was not repeated, or messages were filtered. Reproduce the exact action, check Console filters, and set a breakpoint in the code path that should run. Inspect the resulting state.
The error points into a minified bundle The browser is showing generated code, or a usable source map is missing. Check the bundle’s source-map reference and map availability. Confirm the map matches the deployed build.
The breakpoint never pauses The line did not execute, the wrong frame or file is selected, or the source map maps to a different location. Verify the action reaches that code, check the selected execution context, and move the breakpoint to an executable statement. Try debugger; temporarily if appropriate.
A Console expression says a variable is undefined The variable is local to another function, has gone out of scope, or the Console is attached to another frame. Pause inside the relevant function and select the correct frame or execution context before evaluating it.
The reported line looks correct An earlier call may have supplied an unexpected value, or the failing statement may be downstream of the original issue. Set a breakpoint earlier, inspect inputs and intermediate values, and step forward until a value first differs from expectation.
The error happens only sometimes The code may depend on timing, asynchronous completion, or changing page state. Reproduce the same sequence, add breakpoints around callbacks or promise handlers, and inspect the state each time execution pauses.

8. Keep debugging efficient and reliable

  • Start with the earliest useful failure. A later exception can be a consequence of an earlier one.
  • Change one condition at a time. Reproduce the same action after each change so you know which observation changed.
  • Use breakpoints for state, not just line confirmation. Inspect inputs, scope, and the call stack before stepping.
  • Remember that pausing changes timing. A breakpoint can make race conditions or time-sensitive behavior appear differently. Compare with an unpaused reproduction too.
  • Keep Console experiments small. They run against the live page and can mutate its state; reload to reset transient changes when appropriate.
  • Check generated-code mappings. Debugging against a mismatched source map can lead you to the wrong source line.

This browser workflow has no separate software cost: the Console and debugger are part of browser developer tools. The main practical cost is time spent reproducing the failure and narrowing down the first incorrect state. Browser interfaces can change, but the core approach—find the error, pause execution, inspect state, and trace the call path—remains the same.

9. Or skip the browser setup

If you need a screenshot of a page while documenting or investigating a visual issue, ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns an image or PDF. This does not replace JavaScript debugging; it can capture the page without you setting up a browser automation script.

cURL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

See the ScreenshotNeo API documentation for request options and setup. ScreenshotNeo accepts cookie or consent banners as a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each of those steps can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server offers take_screenshot, get_page_info, and capture_pdf for AI agents and MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for 1,000 free screenshots a month, with no card.

FAQ

Should I debug from the Console or the source debugger?

Use the Console to read errors and evaluate focused expressions. Use the source debugger to pause at a line, step through execution, and inspect scope and the call stack. They work together.

What does the call stack tell me?

It shows the active chain of function calls that led to the currently paused statement, helping explain how execution arrived there.

Why can’t I inspect a local variable from the Console?

It may only exist inside a function that is not currently paused, may have gone out of scope, or may belong to another frame. Pause in the function and select the matching execution context.

Do I need source maps to debug JavaScript?

No. You can debug generated code directly. Source maps are useful when you want the debugger to map bundled or minified code back to original source files.

Does debugger; stop the page for every visitor?

It pauses when debugging functionality is available. Without an attached debugger it has no effect, but remove unintended statements before release to avoid disrupting a debugging session.