# JavaScript Debugging: From Console to Advanced DevTools
Master Chrome DevTools, breakpoint debugging, performance profiling and troubleshooting methods
Debugging is an essential developer skill. This article explores various JavaScript debugging techniques and best practices.
Console API
Basic Methods
// Basic output
console.log('Regular log');
console.info('Info');
console.warn('Warning');
console.error('Error');
// Formatted output
console.log('User %s is %d years old', 'Alice', 25);
console.log('Object: %o', { name: 'Alice', age: 25 });
console.log('Styled: %cRed text', 'color: red; font-size: 20px');
// Conditional logging
console.assert(1 === 2, 'This will show because condition is false');
// Clear console
console.clear();
Advanced Methods
// Grouping
console.group('User Info');
console.log('Name: Alice');
console.log('Age: 25');
console.groupEnd();
// Collapsed grouping
console.groupCollapsed('Details');
console.log('Email: alice@example.com');
console.log('Address: ...');
console.groupEnd();
// Table display
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
];
console.table(users);
console.table(users, ['name']); // Show only name column
// Counter
function handleClick() {
console.count('Click count');
}
console.countReset('Click count');
// Timer
console.time('Data loading');
await fetchData();
console.timeEnd('Data loading'); // Data loading: 1234ms
console.time('Operation');
console.timeLog('Operation', 'Intermediate state');
console.timeEnd('Operation');
Debugging Techniques
// Stack trace
function a() { b(); }
function b() { c(); }
function c() {
console.trace('Call stack');
}
a();
// Directory structure
console.dir(document.body, { depth: 2 });
// Performance monitoring
console.profile('Performance analysis');
// Execute code
console.profileEnd('Performance analysis');
// Memory snapshot
console.memory;
Chrome DevTools
Breakpoint Types
Breakpoint Types:
┌─────────────────────────────────────────────────────┐
│ │
│ Line Breakpoint │
│ └── Click line number, most common │
│ │
│ Conditional Breakpoint │
│ └── Right-click line number > Add condition │
│ │
│ Logpoint │
│ └── Don't pause, just log output │
│ │
│ DOM Breakpoint │
│ └── Triggers on node modification │
│ │
│ XHR/Fetch Breakpoint │
│ └── Triggers on network requests │
│ │
│ Event Listener Breakpoint │
│ └── Pause on specific events │
│ │
└─────────────────────────────────────────────────────┘
Code Debugging
// debugger statement
function processData(data) {
debugger; // Code pauses here
return data.map(transform);
}
// Conditional debugger
function handleRequest(req) {
if (req.userId === 'problem-user') {
debugger;
}
// Processing logic
}
Watch Expressions
// Add in DevTools Sources panel Watch section
// Common expressions:
this
this.state
Array.from(document.querySelectorAll('.item'))
localStorage.getItem('token')
performance.now()
Debugging Shortcuts
| Shortcut | Function |
|---|---|
| F8 | Resume execution |
| F10 | Step over |
| F11 | Step into |
| Shift+F11 | Step out |
| Ctrl+\ | Pause/Resume |
Network Debugging
Request Analysis
// Using DevTools Network panel
// Filters:
// - XHR: Show only XHR requests
// - Fetch: Show only Fetch requests
// - is:running: In-progress requests
// - larger-than:100k: Larger than 100KB
// - domain:api.example.com: Specific domain
// Copy request as cURL
// Right-click request > Copy > Copy as cURL
// Replay request
// Right-click request > Replay XHR
Mock Requests
// Override response using DevTools
// Network > Right-click request > Override content
// Mock using Service Worker
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/api/users')) {
event.respondWith(
new Response(JSON.stringify({ mock: true }), {
headers: { 'Content-Type': 'application/json' },
})
);
}
});
Performance Debugging
Performance Panel
// Using Performance API
const startTime = performance.now();
await expensiveOperation();
const duration = performance.now() - startTime;
console.log(`Duration: ${duration}ms`);
// Marks and measures
performance.mark('start');
await operation1();
performance.mark('after-op1');
await operation2();
performance.mark('end');
performance.measure('operation1', 'start', 'after-op1');
performance.measure('operation2', 'after-op1', 'end');
performance.measure('total', 'start', 'end');
// Get measurements
const measures = performance.getEntriesByType('measure');
console.table(measures);
// Cleanup
performance.clearMarks();
performance.clearMeasures();
Memory Debugging
// Detecting memory leaks
// 1. DevTools > Memory > Take heap snapshot
// 2. Perform operations
// 3. Take another snapshot
// 4. Compare snapshots
// Common memory leaks
// 1. Uncleared event listeners
function badComponent() {
window.addEventListener('resize', this.handleResize);
// Forgot to remove on unmount
}
// 2. Closure references
function createLeak() {
const largeData = new Array(1000000);
return function() {
console.log(largeData.length); // Closure holds reference
};
}
// 3. Uncleared timers
const intervalId = setInterval(() => {
// operation
}, 1000);
// Forgot to call clearInterval(intervalId)
React Debugging
React DevTools
// Using displayName
const MyComponent = memo(function MyComponent() {
return <div>Hello</div>;
});
MyComponent.displayName = 'MyComponent';
// Using useDebugValue
function useCustomHook(value: string) {
useDebugValue(value ? `Value: ${value}` : 'Empty');
// hook logic
}
// Profiler component
import { Profiler } from 'react';
function onRenderCallback(
id: string,
phase: 'mount' | 'update',
actualDuration: number
) {
console.log(`${id} ${phase}: ${actualDuration}ms`);
}
function App() {
return (
<Profiler id="App" onRender={onRenderCallback}>
<MainContent />
</Profiler>
);
}
State Debugging
// Development environment debugging helpers
if (process.env.NODE_ENV === 'development') {
// Expose to global for debugging
(window as any).__DEBUG__ = {
getState: () => store.getState(),
dispatch: store.dispatch,
};
}
// useWhyDidYouUpdate hook
function useWhyDidYouUpdate(name: string, props: Record<string, any>) {
const previousProps = useRef<Record<string, any>>({});
useEffect(() => {
if (previousProps.current) {
const allKeys = Object.keys({ ...previousProps.current, ...props });
const changes: Record<string, { from: any; to: any }> = {};
allKeys.forEach((key) => {
if (previousProps.current[key] !== props[key]) {
changes[key] = {
from: previousProps.current[key],
to: props[key],
};
}
});
if (Object.keys(changes).length) {
console.log('[why-did-you-update]', name, changes);
}
}
previousProps.current = props;
});
}
Node.js Debugging
Starting Debug
# Using --inspect
node --inspect app.js
# Pause at first line
node --inspect-brk app.js
# Specify port
node --inspect=9229 app.js
# Chrome DevTools debugging
# Open chrome://inspect
VS Code Debugging
// .vscode/launch.json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug Program",
"program": "${workspaceFolder}/src/index.ts",
"preLaunchTask": "tsc: build",
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
},
{
"type": "node",
"request": "attach",
"name": "Attach to Process",
"port": 9229
},
{
"type": "node",
"request": "launch",
"name": "Debug Tests",
"program": "${workspaceFolder}/node_modules/vitest/vitest.mjs",
"args": ["run", "--reporter=verbose"],
"console": "integratedTerminal"
}
]
}
Debugging Tips Summary
Debugging Best Practices:
┌─────────────────────────────────────────────────────┐
│ │
│ Locating Issues │
│ ├── Use binary search to narrow scope │
│ ├── Check network requests and responses │
│ ├── Look at console errors │
│ └── Use breakpoints for step debugging │
│ │
│ Tool Selection │
│ ├── Console for quick logging │
│ ├── Debugger for complex logic │
│ ├── Network for API issues │
│ └── Performance for perf issues │
│ │
│ Prevention │
│ ├── Use TypeScript │
│ ├── Write unit tests │
│ ├── Use ESLint │
│ └── Code review │
│ │
└─────────────────────────────────────────────────────┘
| Issue Type | Debugging Method |
|---|---|
| Logic errors | Breakpoints + Watch |
| Performance issues | Performance panel |
| Memory leaks | Memory panel |
| Network issues | Network panel |
Debugging isn’t finding bugs, it’s understanding how code runs.