Debugging JavaScript: Advanced Techniques with DevTools You Need to Know
Hey HaWkers, debugging is one of the most important skills for any developer, but many still limit themselves to console.log(). Modern browser developer tools offer powerful features that can completely transform how you find and fix bugs.
Let's explore advanced techniques that go beyond the basics?
Beyond console.log()
console.log() is useful, but the console object offers much more:
Advanced Console Methods
// Group related logs
console.group('Processing User');
console.log('Name:', user.name);
console.log('Email:', user.email);
console.log('Permissions:', user.permissions);
console.groupEnd();
// Tables for arrays and objects
const users = [
{ name: 'Ana', age: 28, role: 'Dev' },
{ name: 'Carlos', age: 32, role: 'Lead' },
{ name: 'Maria', age: 25, role: 'Dev' }
];
console.table(users);
// Measure execution time
console.time('fetchUsers');
await fetchUsers();
console.timeEnd('fetchUsers'); // fetchUsers: 234.5ms
// Execution count
function processItem(item) {
console.count('processItem called');
// logic...
}
// Stack trace without error
function deepFunction() {
console.trace('How did we get here?');
}
// Assertions for debugging
console.assert(user.id > 0, 'User ID should be positive', user);Console Styling
// Colored and styled logs
console.log(
'%c ERROR %c Authentication failed',
'background: #ff0000; color: white; padding: 2px 6px; border-radius: 3px;',
'color: #ff0000;'
);
console.log(
'%c SUCCESS %c User created',
'background: #00aa00; color: white; padding: 2px 6px; border-radius: 3px;',
'color: #00aa00;'
);
// Multiple styles
console.log(
'%cVariable:%c user.name %c=%c "John"',
'color: gray;',
'color: purple;',
'color: gray;',
'color: green;'
);
Conditional Breakpoints
Breakpoints are more powerful than simple code pauses.
Breakpoints with Conditions
In Chrome DevTools, right-click on a breakpoint and select "Edit breakpoint":
// Original code
async function processOrders(orders) {
for (const order of orders) {
await processOrder(order);
}
}
// Condition in breakpoint: order.total > 1000
// Only pauses when order.total is greater than 1000Logpoints (Breakpoints that don't pause)
Add logs without modifying code:
// In DevTools, add a logpoint with:
// "Processing order:", order.id, "total:", order.total
// Equivalent to adding console.log() in code,
// but without needing to modify and reloadDOM Breakpoints
Pause when DOM changes:
- Inspect the element in the Elements panel
- Right-click → Break on
- Choose: subtree modifications, attribute modifications, or node removal
// Useful for finding code that modifies elements unexpectedly
// DevTools will pause and show exactly which code made the modification
Debugging Asynchronous Code
Asynchronous code can be hard to debug. Here are specific techniques:
Async Stack Traces
Chrome DevTools shows the complete stack trace including asynchronous calls:
async function fetchUserData(userId) {
// Breakpoint here shows the entire async path
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
return data;
}
async function loadDashboard() {
const user = await fetchUserData(currentUserId);
await loadUserPosts(user.id);
await loadUserNotifications(user.id);
}
// In the stack trace you'll see:
// - fetchUserData (async)
// - loadDashboard (async)
// - buttonClickHandler
// - (click event)Promise Rejections
// Enable "Pause on caught exceptions" to debug rejected promises
async function riskyOperation() {
try {
const result = await mightFail();
return result;
} catch (error) {
// With "Pause on caught exceptions" active,
// the debugger stops here before catch executes
console.error('Operation failed:', error);
throw error;
}
}Event Listener Breakpoints
In the Sources panel → Event Listener Breakpoints:
// Mark specific events to pause:
// - Mouse → click
// - Keyboard → keydown
// - XHR/Fetch → Any XHR or fetch
// Useful for understanding event flow
// without needing to add breakpoints manually
Performance Profiling
Identifying performance bottlenecks is essential for responsive applications.
Performance Panel
// Start a recording in the Performance panel
// Execute the slow action
// Stop recording and analyze
// What to look for:
// - Long Tasks (tasks > 50ms)
// - Layout Thrashing (many reflows)
// - High scripting time
// - Blocking timeIdentifying Layout Thrashing
// ❌ BAD - Causes multiple reflows
function updateElements(elements) {
elements.forEach(el => {
const height = el.offsetHeight; // Forces reflow (read)
el.style.height = height + 10 + 'px'; // Forces reflow (write)
});
}
// ✅ GOOD - Groups reads and writes
function updateElementsOptimized(elements) {
// First, all reads
const heights = elements.map(el => el.offsetHeight);
// Then, all writes
elements.forEach((el, i) => {
el.style.height = heights[i] + 10 + 'px';
});
}Memory Profiling
// In the Memory panel, take snapshots to identify memory leaks
// Common memory leak pattern:
class Component {
constructor() {
// Event listener that is never removed
window.addEventListener('resize', this.handleResize);
}
handleResize = () => {
// ...
};
// ❌ Without cleanup, the listener keeps reference to component
}
// ✅ Fix:
class ComponentFixed {
constructor() {
window.addEventListener('resize', this.handleResize);
}
handleResize = () => {
// ...
};
destroy() {
window.removeEventListener('resize', this.handleResize);
}
}
Source Maps and Minified Code Debugging
Configuring Source Maps
// webpack.config.js
module.exports = {
devtool: 'source-map', // Production
// or
devtool: 'eval-source-map', // Development (faster)
};
// vite.config.js
export default {
build: {
sourcemap: true,
},
};Debugging with Source Maps
With source maps configured, you can:
- See original code in the Sources panel
- Add breakpoints in TypeScript/ES6+ code
- See variables with original names
- Stack traces point to original files
// Original TypeScript code
interface User {
id: number;
name: string;
}
async function getUser(id: number): Promise<User> {
// Breakpoint here works normally
const response = await fetch(`/api/users/${id}`);
return response.json();
}
Snippets and Overrides
Reusable Snippets
Create snippets in DevTools for repetitive tasks:
// Snippet: Clear localStorage and reload
(function clearAndReload() {
localStorage.clear();
sessionStorage.clear();
location.reload();
})();
// Snippet: Export console data
(function exportConsoleData() {
const data = /* your data */;
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'export.json';
a.click();
})();
// Snippet: Monitor all fetch calls
(function monitorFetch() {
const originalFetch = window.fetch;
window.fetch = async (...args) => {
console.group('Fetch Request');
console.log('URL:', args[0]);
console.log('Options:', args[1]);
const start = performance.now();
const response = await originalFetch(...args);
const duration = performance.now() - start;
console.log('Status:', response.status);
console.log('Duration:', duration.toFixed(2) + 'ms');
console.groupEnd();
return response;
};
console.log('Fetch monitoring enabled');
})();Local Overrides
Modify production files locally:
- In the Sources panel, create an overrides folder
- Select a file and edit
- Changes persist between reloads
- Useful for testing fixes in production without deploy
Network Debugging
Advanced Filters
// In the Network panel, use filters:
// By type
// is:running - Requests in progress
// larger-than:1M - Larger than 1MB
// status-code:500 - Only status 500
// By domain
// domain:api.example.com
// Negative expressions
// -domain:analytics.com - Exclude domain
// Multiple filters
// method:POST status-code:400Throttling and Offline
// Simulate slow connections
// Network panel → No throttling → Select:
// - Slow 3G
// - Fast 3G
// - Offline
// Or create custom profiles:
// - Download: 100kb/s
// - Upload: 50kb/s
// - Latency: 500msConclusion
Mastering debugging tools transforms your productivity as a developer. Instead of guessing where the problem is, you can go directly to the root cause.
Some recommended practices:
- Use conditional breakpoints instead of multiple
console.log() - Learn DevTools keyboard shortcuts
- Use logpoints when you don't want to modify code
- Profile regularly to identify performance issues before they become critical
The tools are available for free in all modern browsers. It's worth investing time to master them.
If you want to continue deepening your JavaScript knowledge, I recommend checking out the article Modern Browser Web APIs with JavaScript in 2025 where we explore powerful APIs that can improve your applications.

