๐ฅ This is not console.log()
Seriously.
Every junior dev learns console.log() on day one. And then they never learn anything else.
That's like learning to use a hammer and ignoring the entire toolbox.
The browser console is one of the most powerful debugging environments in existence. And 95% of developers only use it to print strings.
Let's fix that.
๐ฏ 1) console.table() โ Display data as a real table
Stop printing arrays as [object Object],[object Object].
const users = [
{ name: "Ada", role: "Engineer", level: 5 },
{ name: "Linus", role: "Architect", level: 8 },
{ name: "Grace", role: "Lead", level: 7 },
];
// โ Old way
console.log(users);
// [{...}, {...}, {...}] โ useless
// โ
New way
console.table(users);
This prints a clean, sortable table right in your console. With columns. And row numbers.
๐ง What's actually happening?
console.table() takes an array of objects (or an object) and renders it as a formatted table. Each object property becomes a column. Each array item becomes a row.
You can also pick which columns to show:
// Only show name and level
console.table(users, ["name", "level"]);
Works with plain objects too:
console.table({ a: 1, b: 2, c: 3 });
// Shows index | key | value
Use it when: inspecting API responses, checking arrays of data, comparing config objects, debugging state.
โก 2) console.group() and console.groupCollapsed() โ Organize your logs
Your console looks like a mess? That's because you haven't grouped anything.
console.group("๐ User Login Flow");
console.log("1. Validating input...");
console.log("2. Sending API request...");
console.log("3. Response received:", { status: 200 });
console.groupEnd();
This creates a collapsible group in the console. Click to expand/collapse.
console.groupCollapsed() does the same thing but starts collapsed:
console.groupCollapsed("๐ฆ Loading user preferences");
console.log("Theme: dark");
console.log("Language: en");
console.log("Notifications: enabled");
console.groupEnd();
๐ Real-world example:
function processOrder(order) {
console.group("๐ Processing order #" + order.id);
console.group("๐ฐ Payment");
console.log("Method:", order.paymentMethod);
console.log("Amount:", order.total);
console.groupEnd();
console.group("๐ฆ Items");
order.items.forEach(item => {
console.log("- " + item.name + " x" + item.qty);
});
console.groupEnd();
console.groupEnd();
}
Nested groups. Clean hierarchy. Your console reads like documentation instead of a wall of noise.
๐จ 3) console.log() with CSS styling
Yes. You can style console output. Most people don't know this.
console.log(
"%c Success! %c User created successfully",
"background: #00b894; color: white; padding: 4px 8px; border-radius: 4px; font-weight: bold;",
"color: #00b894; font-size: 14px;"
);
The %c placeholder applies the CSS string that follows it.
๐งฒ More examples:
// Error style
console.log(
"%c ERROR ",
"background: #d63031; color: white; padding: 4px 8px; border-radius: 4px;",
"Something went wrong"
);
// Warning style
console.log(
"%c WARN ",
"background: #fdcb6e; color: black; padding: 4px 8px; border-radius: 4px;",
"Deprecated API used"
);
// Big title
console.log(
"%c MyApp v2.0",
"color: #6c5ce7; font-size: 24px; font-weight: bold;"
);
Use it when: building CLI-style debugging output, branding your app's console, making errors stand out from noise.
๐ 4) console.trace() โ See exactly where things are called
When you're deep in nested function calls and don't know WHERE something is being triggered:
function handleClick() {
validateForm();
}
function validateForm() {
checkEmail();
}
function checkEmail() {
console.trace("Email validation called from:");
// Shows the full call stack
}
Output:
Email validation called from:
checkEmail @ app.js:42
validateForm @ app.js:30
handleClick @ app.js:15
(anonymous) @ app.js:5
No more guessing. You see the exact path from root to current call.
Use it when: debugging event handlers, tracing callback chains, finding which code path triggered a function, understanding complex flows.
โฑ๏ธ 5) console.time() โ Measure how long things take
Stop guessing if your code is slow. Measure it.
console.time("API Call");
fetch("/api/users")
.then(res => res.json())
.then(data => {
console.timeEnd("API Call");
// API Call: 234.56ms
});
๐ง With labels for multiple timers:
console.time("Total");
console.time("Database");
// ... database query ...
console.timeEnd("Database");
// Database: 45.23ms
console.time("Render");
// ... rendering ...
console.timeEnd("Render");
// Render: 12.87ms
console.timeEnd("Total");
// Total: 58.10ms
You can run multiple timers simultaneously with different labels. No libraries. No performance.now() boilerplate.
Use it when: comparing algorithm speeds, measuring API latency, profiling render times, benchmarking loops.
๐ก๏ธ 6) console.assert() โ Log only when something is wrong
This one is criminally underused.
const user = { name: "Ada", age: -5 };
// Only prints if the condition is FALSE
console.assert(user.age > 0, "Age must be positive! Got:", user.age);
// Output: Assertion failed: Age must be positive! Got: -5
console.assert(user.name, "Name is missing!");
// Nothing โ assertion passed
๐ค Why this matters
Instead of:
if (!condition) {
console.log("something is wrong");
}
You write:
console.assert(condition, "something is wrong");
One line. Same behavior. Cleaner code.
Use it when: validating data shapes, checking preconditions, defensive programming, sanity checks during development.
๐งน 7) console.count() โ Count how many times something runs
function handleRequest(req) {
console.count("Request received");
// ... process ...
}
// After running 5 times:
// Request received: 1
// Request received: 2
// Request received: 3
// Request received: 4
// Request received: 5
Reset the counter:
console.countReset("Request received");
๐ Real-world example:
// How many times is this re-render happening?
function MyComponent() {
console.count("MyComponent render");
return <div>Hello</div>;
}
// If you see the count climbing fast, you have a re-render problem.
Use it when: detecting infinite loops, counting re-renders, tracking function calls, finding performance bottlenecks.
๐ 8) console.warn() and console.error() โ Different severity levels
Stop using console.log() for everything.
// Informational
console.log("User logged in");
// Something might be wrong
console.warn("API response took 3000ms โ consider caching");
// Something IS wrong
console.error("Failed to fetch user:", error);
๐ง Why this matters
-
console.warn()shows a yellow icon and stack trace -
console.error()shows a red icon, stack trace, AND breaks on error if DevTools is set to "Pause on exceptions" - You can filter by level in DevTools (show only errors, hide info, etc.)
Your logs become searchable and filterable instead of a wall of identical white text.
// In the DevTools console filter:
// -error โ hides all errors
// warn โ shows only warnings
// -log โ hides all regular logs
๐ช 9) console.dir() โ Inspect DOM elements as objects
When you console.log() a DOM element, you get the HTML tree. Sometimes you want the JavaScript object instead.
const button = document.querySelector("#submit");
// Shows HTML representation
console.log(button);
// <button id="submit">Submit</button>
// Shows the JS object with all properties
console.dir(button);
// โถ HTMLButtonElement
// - id: "submit"
// - className: "btn-primary"
// - dataset: DOMStringMap { ... }
// - style: CSSStyleDeclaration { ... }
// - ... hundreds more properties
Use it when: inspecting DOM properties, checking dataset values, debugging element state, exploring event listeners.
๐งช 10) The $ shortcuts in DevTools
These aren't console methods โ they're DevTools built-in shortcuts that most people never learn.
// Last console result
$0 // Currently selected element in Elements panel
$1 // Previously selected element
$2 // Two selections ago
$3
$4
// jQuery-like selectors (native, no jQuery needed)
$$("div") // Returns array of all divs
$("div.container") // Returns first match (like querySelector)
$x("//button") // XPath selector
// Quick access
$_ // Last expression result
๐ง The power combo:
// 1. Click an element in the Elements panel
// 2. In console, type:
$0.style.outline = "3px solid red"
// โ That element now has a red outline
// 3. Get all buttons on the page
$$("button").map(b => b.textContent)
// โ ["Save", "Cancel", "Delete", "Edit"]
// 4. Find all images with no alt text
$$("img").filter(img => !img.alt)
// โ Shows accessibility violations
No more writing document.querySelectorAll() every single time.
๐ง TL;DR Cheat Sheet
| Method | What it does | Use when |
|---|---|---|
console.table() |
Display as table | Inspecting arrays/objects |
console.group() |
Collapsible groups | Organizing complex logs |
console.log("%c...", css) |
Styled output | Making logs readable |
console.trace() |
Print call stack | Finding where code runs |
console.time() |
Measure duration | Benchmarking performance |
console.assert() |
Log on failure | Validation checks |
console.count() |
Count executions | Detecting loops/re-renders |
console.warn() / error()
|
Severity levels | Filtering important logs |
console.dir() |
Inspect as object | DOM element debugging |
$0, $$(), $_
|
DevTools shortcuts | Quick element access |
๐ Your console is a debugger, not a notepad
The next time you're about to type console.log(something) โ stop.
Ask yourself:
- Is this an array? โ
console.table() - Is this a performance issue? โ
console.time() - Is this a conditional check? โ
console.assert() - Is this a complex flow? โ
console.group() - Do I need to find where this is called? โ
console.trace()
Your console can do so much more than print strings. Start using it properly.
Your debugging speed will 10x. Not exaggerating.
Top comments (0)