Quick debugging one-liners for logging, tracing, timing, and inspecting JavaScript values in any project.
console.log() and inspect with console.dir().debugger statement in DevTools.console.time() and console.timeEnd() using matching labels.console.table() for arrays of objects, since it shows each property as a column for fast visual scanning.%c format specifier in console.log() to add custom CSS styling to messages, making important output easier to spot.debugger statements 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.console.log()The obvious choice, but several other console methods solve specific logging problems faster.
console.log('user:', user);Labeled logsLabel your logs by passing a string prefix as the first argument.
console.log('typeof myVar:', typeof myVar);console.table()Displays an array of objects as a clean, scannable table instead of logs.
console.table(users);Combined valuesCombine multiple values in one log call to compare them side by side.
console.log('before:', before, 'after:', after);%o SpecifierEmbeds an inspectable object directly inside a formatted log string.
console.log('user: %o', user);debugger;A single statement pauses your entire script wherever DevTools is open.
debugger;console.trace()Prints the full call stack leading up to the current line of code.
console.trace();Conditional debuggerPauses only when a condition is true, instead of every single time.
if (i === 5) debugger; // pause onceconsole.assert()Logs a message only when the given condition turns out false.
console.assert(user.id, 'Missing user id');console.count()Counts and logs how many times a labeled line has run.
console.count('render'); // render: 1Strict equalityMost "why is this undefined" bugs resolve faster with a strict equality check.
console.log(myVar === undefined);Nullish coalescingLog a fallback when values are missing using the ?? operator.
console.log(myVar ?? 'fallback');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'); // falseJSON.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'));console.time/timeEndA matching label is the only thing connecting console.time() to its console.timeEnd().
console.time('label');
// code here
console.timeEnd('label');console.group()Nests related log messages in a collapsible group for easier scanning.
console.group('info');
// related logs...
console.groupEnd();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');console.log() prints a string representation of a value and is best for primitives and quick output. console.dir() renders an interactive property tree instead, which is far more useful for inspecting DOM nodes or complex objects. Use console.dir() when you need to drill into nested properties.
Add the debugger statement directly in your code. When DevTools is open and execution reaches that line, it pauses automatically. From there you can step through the call stack, inspect variables, and evaluate expressions in the console.
Wrap the code with console.time('label') before it runs and console.timeEnd('label') after. The browser logs the elapsed milliseconds to the console using the matching label. This identifies which measurement the output belongs to.
console.log() captures a live reference to objects, so expanding the output later shows the current state, not the logged state. Use console.log(JSON.stringify(obj)) or console.dir() to snapshot the value at the moment of logging.
In DevTools, open the Sources panel and enable 'Pause on exceptions' (the stop-sign icon). The debugger automatically breaks at the exact line that throws. This lets you inspect the local scope and call stack without manually adding breakpoints.
Debugging a Slow Data Fetch
Combines console.time, console.table, and try/catch to time a fetch call, inspect its output, and catch failures.
async function loadUsers() {
console.time('loadUsers');
try {
const res = await fetch('/api/users');
const users = await res.json();
console.table(users);
return users;
} catch (error) {
console.error('Failed to load users:', error.message);
return [];
} finally {
console.timeEnd('loadUsers');
}
}
loadUsers();
// logs a table of users, then "loadUsers: 42.3ms"Pair console.time/timeEnd with try/catch so you always see how long code took, even when it fails.