The browser console is the most important debugging tool you have. console.log() is only the beginning: the console object has methods for warnings, errors, tables, grouped output, counters, and checks. Using the right one makes the answer jump out instead of being buried in a wall of text.
In Chrome or Edge: right-click anywhere on the page, choose Inspect, then click the Console tab (or press F12, or Cmd+Option+J on a Mac). The console shows errors, warnings, and messages from your code, and you can type JavaScript into it directly to try things out.
Prints any values, separated by spaces:
let name = "John";
let age = 25;
console.log(name);
console.log("x =", 10, "y =", 5);
console.log({ name, age }) prints { name: "John", age: 25 }. It uses the object shorthand from JS Objects.
These print like log, but styled as a yellow warning or a red error, so they stand out. You can also filter the console to show only warnings or only errors:
console.warn("Stock is low:", 2);
console.error("Something went wrong!");
Use warn for "this looks suspicious, but the code can continue," and error for "this failed." There is also console.info() for general information.
Log an object and the console shows it with a small arrow. Click the arrow to expand it and explore every property, including nested ones:
let user = { name: "John", age: 25 };
console.log(user);
console.log ran. If the code changed the object in between, you see the new values. To freeze a snapshot, log a copy: console.log(structuredClone(user)) or console.log(JSON.stringify(user)).
For an array of objects, console.table() draws a real table, which is far easier to scan than nested braces:
let users = [
{ name: "John", age: 25 },
{ name: "Anna", age: 30 }
];
console.table(users);
The console shows:
| (index) | name | age |
|---|---|---|
| 0 | John | 25 |
| 1 | Anna | 30 |
Indent related messages under a heading, and close the group with console.groupEnd(). In the browser, a group can be collapsed with a click:
console.group("Checkout");
console.log("items: 3");
console.log("total: $42");
console.groupEnd();
console.log("done");
Counts how many times a line runs, with an optional label. It is perfect for "is this function being called too often?":
function save() {
console.count("save called");
}
save();
save();
save();
Prints an error only if the condition is false. Sprinkle assertions where you are sure something should be true, and the console stays quiet until it is not:
let stock = 5;
console.assert(stock >= 0, "stock should never be negative");
stock = stock - 8;
console.assert(stock >= 0, "stock should never be negative");
Measure how long something takes. Give both calls the same label:
console.time("loop");
let sum = 0;
for (let i = 0; i < 1000000; i++) sum += i;
console.timeEnd("loop");
The console prints something like loop: 3.2 ms. The number changes on every run and every computer.
| console.trace() | Prints the chain of function calls that led to this line |
| console.dir(x) | Shows x as an expandable object (handy for page elements) |
| console.clear() | Empties the console |
| console.info() | Like log, marked as information |
Beginners guess what a value is. Professionals log it and know. And when you are finished, remove your debugging logs, or they will fill up the console for everyone who uses your page.
What does this log?
console.count();
console.count("x");
console.count();
default: 1, x: 1, default: 2. Without a label, count uses "default", and each label keeps its own count.Which prints something only when a check fails?
console.log()console.warn()console.assert()console.table()console.assert(condition, message) stays silent while the condition is true.You log an object, then change one of its properties. When you expand the logged object, it shows the new value. Why, and how do you see the old one?
structuredClone(obj) or JSON.stringify(obj).{ name, age } label trick. Log two variables of your own the same wayconsole.table()console.count() inside a loop and inside a functionconsole.assert() that fails, then fix the code so it passes