JavaScript Debugging Tools
Quick debugging one-liners for logging, tracing, timing, and inspecting JavaScript values in any project.
TL;DR
- Log values with
console.log()and inspect withconsole.dir(). - Pause execution with the
debuggerstatement in DevTools. - Measure performance with
console.time()andconsole.timeEnd()using matching labels.
Logging Basics and Pausing and Tracing
console.log()The default, but other methods solve problems faster.
console.log('user:', user);Labeled logsPrefix your logs with a label string as the first arg.
console.log('type:', typeof myVar);console.table()Shows an array of objects as a scannable table.
console.table(users);Combined valuesCombine values in one call to compare side by side.
console.log('before:', before, after);debugger;Pauses the script wherever DevTools is open.
debugger;console.trace()Prints the call stack up to the current line.
console.trace();console.assert()Logs when a condition is false.
console.assert(user.id, 'No id');console.count()Tallies how many times a line has run.
console.count('render'); // render: 1Checking Values
Strict equalityMost "why is this undefined" bugs resolve faster with a strict equality check.
console.log(myVar === undefined);Falsy checkDetect any falsy value with a simple negation check inside an if.
if (!myVar) console.log('Falsy!');typeofUse typeof when results seem off, to confirm the value's data type.
typeof 'hi' === 'string'; // trueNumber.isNaN()Checks for NaN without the risky type coercion of the global isNaN().
Number.isNaN(NaN); // true
Number.isNaN('x'); // falseSnapshotting Live Objects
JSON.stringify snapshotconsole.log(obj) shows the object's state when expanded, not when logged — this fixes that.
console.log(JSON.stringify(obj, null, 2));structuredClone()Takes a frozen deep copy for logging without mutating the live original.
console.log(structuredClone(state));console.dir()Renders a DOM node as an interactive property tree instead of HTML.
console.dir(document.querySelector('button'));Timing and Errors
console.time/timeEndA matching label is the only thing connecting console.time() to its console.timeEnd().
console.time('label');
// code here
console.timeEnd('label');try...catchWrap risky code in a try...catch block to handle errors safely.
try {
riskyFunction();
} catch (e) {
console.error('Error:', e.message);
}console.error()Prints errors in red with a full, clickable stack trace attached.
console.error('Failed to save:', err);console.warn()Flags non-fatal issues in yellow so they still catch your eye.
console.warn('Approaching API rate limit');Tips
- Use
console.table()for arrays of objects, since it shows each property as a column for fast visual scanning. - Use the
%cformat specifier inconsole.log()to add custom CSS styling to messages, making important output easier to spot.
Warnings
- Remove
debuggerstatements and console logs before shipping production code, since they slow down your app and expose data. console.log()shows a live reference to objects, so an expanded log can differ from the object's state at log time.
In Practice
Combines console.time, console.table, and try/catch to time a fetch call, inspect its output, and catch failures.
- console.time('loadUsers') starts a labeled timer before the fetch begins.
- console.table(users) renders the array of user objects as a scannable table instead of a nested log.
- The try/catch block logs a clear error message if the fetch or parsing fails.
- console.timeEnd('loadUsers') reports the elapsed time after the attempt.
async function loadUsers() {
console.time('loadUsers');
try {
const users = await (await fetch('/api/users')).json();
console.table(users);
} catch (error) {
console.error('Load failed:', error);
}
console.timeEnd('loadUsers');
}FAQ
Use console.log() for quick values and console.dir() for an expandable property tree, especially with DOM nodes or nested objects.
Add a debugger statement. With DevTools open, execution pauses there so you can inspect variables and the call stack.
Call console.time('label') before the code and console.timeEnd('label') after it. The console reports the elapsed milliseconds.
In DevTools Sources, enable Pause on exceptions. The debugger stops on the throwing line so you can inspect its scope and call stack.