The Console Is More Than log
console.log is the first debugging tool most people learn and the last one many stop using. It works, but the console object has a dozen other methods that make debugging faster and clearer. And once you're comfortable in DevTools proper — breakpoints, the call stack, watch expressions — you'll reach for log less often than you used to.
Start with a quick survey:
All four print to the same console, but browsers style them differently — warnings get a yellow background, errors get red and an icon, and both include a stack trace. Filters in DevTools let you hide or isolate each level, which matters once an app is producing hundreds of messages.
Log Multiple Values the Lazy Way
When you want to see several variables at once, don't concatenate them into a string. Pass them as separate arguments — or better, wrap them in an object so each one is labeled:
The { user, count } trick uses object shorthand: the variable name becomes the key. In the console you'll see { user: {...}, count: 3 } and you can expand user to inspect it. You don't lose the object's structure by stringifying it.
console.table for Arrays of Objects
When you're staring at an array of objects, console.log gives you a collapsed mess. console.table renders it as an actual table:
In a browser console, the first call prints all three rows with sortable columns. The second call limits it to just name and role. This is a huge quality-of-life upgrade for anything data-shaped — API responses, query results, parsed CSVs.
console.dir vs console.log
They look similar, but behave differently for DOM elements:
const el = document.querySelector("button");
console.log(el); // prints the HTML: <button>Click me</button>
console.dir(el); // prints the JS object view with all properties
log formats elements as HTML. dir shows you the object — every property, every event handler, every computed style reference. When you want to know what methods or attributes an element has, dir is the one.
Grouping Related Output
Long debug sessions fill the console with noise. console.group and console.groupEnd wrap related messages into a collapsible block:
Each call produces a named group you can collapse. Use console.groupCollapsed if you want groups to start folded up by default — useful when you only want to open the ones that look suspicious.
Timing Code with console.time
For quick performance checks, console.time and console.timeEnd are hard to beat:
The label in time and timeEnd has to match. You can have several timers running at once with different labels. For anything more serious than "is this loop slow?", switch to the Performance panel in DevTools, which records a full timeline and flame chart.
Assertions: Log Only When Something's Wrong
console.assert only prints when the condition is falsy. It's a quiet way to leave sanity checks in your code without spamming the console when everything's fine:
Good for invariants that should always hold. Bad for things that might legitimately fail — for those, throw an error.
Stack Traces On Demand
console.trace prints the current call stack without throwing anything. Handy when you're trying to figure out who called a function:
The output shows inner → outer → (top level). In a real app this is how you discover that the click handler you're debugging is being triggered by three different places.
The debugger Statement
The fastest way to pause JavaScript execution is a single word:
function computeTotal(items) {
const subtotal = items.reduce((s, i) => s + i.price, 0);
debugger;
return subtotal * 1.08;
}
When DevTools is open, debugger; acts like a breakpoint — execution pauses on that line and you get the full debugger: variables in scope, the call stack, step-over and step-into controls, the ability to evaluate expressions against the current state. When DevTools is closed, debugger; does nothing.
The first time you use a real debugger instead of console.log, it feels like a superpower. You can see every variable in scope without deciding in advance what to print. You can step through conditionals and watch which branch runs. Fixing a tricky bug drops from minutes to seconds.
Remove the debugger line before committing — or better, set breakpoints in DevTools directly by clicking a line number in the Sources panel.
DevTools Tricks Worth Knowing
A few features people miss for years:
- Conditional breakpoints: right-click a line number in Sources and set a condition like
user.id === 42. The breakpoint only fires when the condition is true. - Logpoints: same menu, "Add logpoint." Prints a message without pausing and without editing your code.
$_in the console: the last evaluated expression. Run something, then use$_to grab it.$0: the currently selected element in the Elements panel.$0.textContentinspects whatever you clicked on.- Copy as object: right-click any value in the console and pick "Store as global variable." You get
temp1,temp2, etc., to poke at. - Network panel → Copy as fetch: turns any request into a
fetch()call you can paste into the console and tweak.
None of these are essential. All of them save time once they're in your muscle memory.
Cleaning Up Before You Ship
Leftover console.log calls are harmless in development and noisy in production. A few habits that help:
- Use a lint rule (
no-consolein ESLint) to flag stray logs, with exceptions forwarnanderror. - Wrap verbose logging in a check:
if (process.env.NODE_ENV !== "production") console.log(...). - Prefer
console.debugfor trace-level output — most bundlers and log aggregators can filter it out. - Better still: use a small logger module (or a library like
debug) so you can turn categories of logs on and off without editing code.
Logging isn't free. Each call serializes its arguments and writes to a buffer. In a hot loop, a forgotten log can measurably slow things down.
Next: Regular Expressions
You'll do a lot of debugging of string-processing code, and a lot of that code involves regex — the most compact and most cryptic feature in the language. The next chapter starts with a gentle tour of JavaScript regular expressions and the methods that use them.
Frequently Asked Questions
What's the difference between console.log and console.dir?
console.log prints values using the browser's default formatting — for DOM elements, that means rendering them as HTML. console.dir always prints the JavaScript object view with its properties, which is what you want when you need to inspect an element's properties instead of its markup.
How do I debug JavaScript in Chrome DevTools without console.log?
Open the Sources panel, find your file, and click a line number to set a breakpoint. When execution hits that line, it pauses and you can inspect variables, step through code, and evaluate expressions in the console. You can also drop a debugger; statement in your code to trigger a breakpoint from the source.
How do I measure how long JavaScript code takes to run?
Wrap the code in console.time('label') and console.timeEnd('label') with matching labels. The console prints the elapsed time in milliseconds. For more detailed profiling, use the Performance panel in DevTools to record a flame chart of everything that ran.
What is console.table used for?
console.table renders arrays and objects as a sortable table in the console, which is much easier to scan than a nested object dump. It's perfect for arrays of objects — each object becomes a row, and its keys become columns. You can pass a second argument to limit which columns appear.