Console Methods Beyond console.log
Most developers learn console.log first and never look further. The browser console has a much richer API worth knowing.
console.error and console.warn
console.error() outputs red-highlighted messages with a stack trace. console.warn() outputs orange warnings. Both are more visible than plain log statements and automatically capture the call stack at the point they're called.
console.table
console.table(data) renders arrays of objects as a formatted table — invaluable when debugging API responses or arrays of data:
const users = [
{ id: 1, name: 'Alice', role: 'admin' },
{ id: 2, name: 'Bob', role: 'user' }
];
console.table(users); // Renders as a readable table
console.group
Group related log statements so they collapse in the console:
console.group('User validation');
console.log('Name:', name);
console.log('Email:', email);
console.groupEnd();
console.time and console.timeEnd
Measure how long code takes to run — useful for finding performance bottlenecks:
console.time('data-processing');
processLargeArray(data);
console.timeEnd('data-processing'); // "data-processing: 142.3ms"
console.assert
Only logs if the first argument is falsy — great for catching unexpected states without cluttering the console:
console.assert(user.age > 0, 'Age must be positive', user);
Using Breakpoints in DevTools
Breakpoints are far more powerful than console statements because they pause execution and let you inspect the entire state of your program at that moment. Open DevTools (F12), go to the Sources tab, find your file, and click any line number to set a breakpoint. When execution reaches that line, it pauses.
Types of Breakpoints
Line breakpoints: Pause at a specific line. The most common type.
Conditional breakpoints: Right-click a line number and choose "Add conditional breakpoint." The debugger only pauses when a condition you specify is true — essential when debugging inside loops or functions called hundreds of times.
Logpoints: Like a console.log without modifying your code. Right-click a line and choose "Add logpoint" to print values when that line executes, with no code change needed.
Exception breakpoints: In the Sources panel, click the pause icon (⏸) to pause on all exceptions or uncaught exceptions. This catches errors the moment they're thrown, showing you the exact state that caused them.
Event listener breakpoints: Expand the "Event Listener Breakpoints" panel to pause when specific DOM events fire — useful for debugging click handlers or form submissions.
Stepping Controls
Once paused, you have four navigation controls: Step over (execute current line, move to next), Step into (enter the function being called), Step out (finish current function and return to caller), and Continue (run until next breakpoint).
Reading the Call Stack
The Call Stack panel in DevTools (visible when paused) shows every function call that led to the current execution point, from newest (top) to oldest (bottom). Clicking any frame in the stack jumps to that code and shows local variables at that point in time.
When you see an error in the console, the stack trace tells the story of what happened. Read it from top to bottom: the topmost entry is where the error occurred; the entries below are the chain of calls that led there. Often the bug isn't at the very top — look for the first function in your own code (not library code) to find where you went wrong.
Common JavaScript Errors Explained
TypeError: Cannot read properties of undefined
The most common error. You're accessing a property on a value that is undefined. Typical causes: API data hasn't loaded yet, a variable isn't initialized before use, or a typo in a property name. Use optional chaining (user?.profile?.name) to safely access nested properties.
ReferenceError: X is not defined
You're referencing a variable that doesn't exist in the current scope. Check for typos, ensure the variable is declared, and verify it's accessible from the current scope (block, function, module).
SyntaxError: Unexpected token
Your code has invalid syntax. Modern editors with ESLint catch these before runtime, but if you see this in production it usually means minified code has a syntax error. Check for missing commas, unmatched brackets, or reserved word conflicts.
TypeError: X is not a function
You're calling something as a function that isn't one. Check that the method name is spelled correctly, the object has that method, and you're not accidentally overwriting a function with a non-function value.
Error Handling with try/catch
Wrap code that might fail in a try/catch block to handle errors gracefully and get more information about what went wrong:
try {
const data = JSON.parse(rawInput);
processData(data);
} catch (error) {
console.error('Failed to parse input:', error.message);
console.error('Stack:', error.stack);
// Show user-friendly error message
showError('Invalid data format. Please check your input.');
}
The error object has several useful properties: error.message (human-readable description), error.stack (the stack trace as a string), error.name (the error type, e.g. "TypeError"), and for custom errors, any properties you add.
Debugging Async Code
Async bugs are notoriously tricky because errors can surface far from where they originate. Here are strategies that help:
Always await Promises or handle rejections. An unhandled rejected Promise shows up as a console warning in development and can silently fail in production. Add a global handler: window.addEventListener('unhandledrejection', handler) to catch these.
Wrap async functions in try/catch:
async function fetchUser(id) {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('fetchUser failed:', error);
throw error; // Re-throw so callers can handle it
}
}
Enable async stack traces in Chrome DevTools: open Settings (F1) → Preferences → check "Enable async stack traces." This shows the full call chain across async boundaries, making it far easier to trace where async bugs originate.
Debugging Network Requests
The Network tab in DevTools is essential for debugging API calls. Every HTTP request your page makes appears here with full request and response details.
Filter by "Fetch/XHR" to see only API calls. Click any request to inspect: Headers (what was sent and received), Payload (the request body), Preview (formatted response), Response (raw response), and Timing (how long each phase took).
Common issues to look for: a 401 means authentication failed (check your token); a 403 means forbidden (check permissions); a 404 means wrong URL (check for typos); a 500 means a server error (check server logs); CORS errors appear in the console, not the Network tab (look for "Access-Control-Allow-Origin" related messages).
Pro Debugging Tips
Use the debugger statement. Placing debugger; in your code triggers a breakpoint when DevTools is open — no need to navigate to the file manually. Remove these before committing.
Inspect variables in the Watch panel. While paused, add expressions to the Watch panel to monitor their values as you step through code. This is more convenient than repeatedly hovering over variables.
Use source maps. If you're working with transpiled or bundled code, ensure source maps are configured so DevTools shows your original source files rather than minified output.
Reproduce in isolation. When a bug is hard to pin down, strip your code down to the minimum reproducible case. This process often reveals the bug itself, and it makes it easier to ask for help.
Check the obvious first. Is the variable the value you think it is? Is the function being called? Is the right version of the code running? Clear the cache, hard reload (Ctrl+Shift+R), and verify your changes are live before deep-diving into DevTools.