Debugging is half of development. Master these tools and techniques to squash bugs faster.
Chrome DevTools
Elements Panel
Inspect and modify DOM in real-time:
// Select elements
document.querySelector('.button')
document.querySelectorAll('div.item')
// Computed styles
getComputedStyle(element)
// Force state
// Right-click element > Force State > :hoverConsole Panel
Logging strategies:
// Basic logging
console.log('Variable:', variable);
// Grouped logging
console.group('User Data');
console.log('Name:', user.name);
console.log('Email:', user.email);
console.groupEnd();
// Table for objects
console.table(users);
// Timing operations
console.time('Fetch data');
fetch('/api/data')
.then(data => {
console.timeEnd('Fetch data');
});
// Assertion
console.assert(condition, 'Error message');
// Tracing
console.trace('Function call stack');
// Counting
console.count('ButtonClick');Console filters:
-url:node_modules: Hide node_modulesfile:app.js: Show only app.js
Network Panel
Monitor all network activity:
// Filter requests
// - XHR/fetch
// - JS, CSS, Img
// - All
// Check status codes
// 200: OK
// 301: Redirect
// 404: Not found
// 500: Server error
// Inspect response
// Headers, Payload, Preview, ResponseSources Panel
Set breakpoints and debug:
// Add breakpoint
// Click line number in Sources panel
// Conditional breakpoint
// Right-click > Edit breakpoint
// condition: x > 100
// Logpoint (no pause)
// console.log(variable)
// Debugging keywords
debugger; // Pauses executionReact DevTools
Component Inspection
npm install --save-dev react-devtoolsFeatures:
- View component hierarchy
- Inspect props and state
- Measure component performance
- Trace component updates
Profiler Component
import { Profiler } from 'react';
function onRenderCallback(
id, phase, actualDuration,
baseDuration, startTime, commitTime
) {
console.log({
id,
phase, // 'mount' or 'update'
actualDuration, // Time spent rendering
});
}
<Profiler id="App" onRender={onRenderCallback}>
<App />
</Profiler>Redux DevTools
State Inspection
import { configureStore } from '@reduxjs/toolkit';
const store = configureStore({
reducer: rootReducer,
devTools: process.env.NODE_ENV !== 'production'
});Features:
- Time-travel debugging
- State inspection
- Action history
- State diffing
Performance Monitoring
Performance API
// Measure performance
performance.mark('start');
// Your code
fetchData().then(() => {
performance.mark('end');
performance.measure('fetchData', 'start', 'end');
const measure = performance.getEntriesByName('fetchData')[0];
console.log(`Took ${measure.duration}ms`);
});User Timing API
// Mark important moments
performance.mark('component-mount');
performance.mark('data-loaded');
// Measure between marks
performance.measure(
'data-loading-time',
'component-mount',
'data-loaded'
);
// Get measures
const measures = performance.getEntriesByType('measure');
console.table(measures);Performance Observer
// Observe long tasks
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Long task detected:', entry);
}
});
observer.observe({ entryTypes: ['longtask'] });Memory Debugging
Memory Leaks
Common causes:
- 1Unclosed intervals/timeouts
- 2Event listeners not removed
- 3Closures retaining references
- 4DOM references in objects
Detection:
// Take memory snapshot
// DevTools > Memory > Take Snapshot
// Look for:
// - Detached DOM nodes
// - Event listeners
// - Global variablesHeap Snapshots
// Force garbage collection
// (in Chrome, with --js-flags flag)
// Compare snapshots
// Snapshot 1 > Use app > Snapshot 2
// Compare to find leaksNetwork Debugging
Fetch API
fetch('/api/data')
.then(response => {
// Log response
console.log('Status:', response.status);
console.log('Headers:', response.headers);
return response.json();
})
.then(data => {
console.log('Data:', data);
})
.catch(error => {
console.error('Error:', error);
});Request Interception
// Service Worker for debugging
self.addEventListener('fetch', (event) => {
console.log('Fetch:', event.request.url);
event.respondWith(
fetch(event.request).then(response => {
console.log('Response:', response.status);
return response;
})
);
});Error Tracking
Try-Catch
try {
riskyOperation();
} catch (error) {
console.error('Caught error:', error);
// Log to error tracking service
logError(error);
}Global Error Handler
// Catch unhandled errors
window.addEventListener('error', (event) => {
console.error('Global error:', event.error);
logError(event.error);
});
// Catch unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled rejection:', event.reason);
event.preventDefault(); // Prevent default handling
});Error Boundaries (React)
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
console.error('Error boundary caught:', error, info);
logError(error, info);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}Source Maps
Configuration
// vite.config.js
export default defineConfig({
build: {
sourcemap: true
}
});Benefits:
- Debug minified code
- See original file names
- Set breakpoints in source
- Console logs show original location
Mobile Debugging
Remote Debugging
Android:
- 1Enable USB debugging
- 2Connect to Chrome
- 3chrome://inspect
iOS:
- 1Safari > Develop > [Device]
- 2Inspect web views
Eruda (Mobile Console)
<script src="//cdn.jsdelivr.net/npm/eruda"></script>
<script>eruda.init();</script>Debugging Tips
1. Reproduce the Bug
Consistently reproduce the issue before fixing.
2. Isolate the Problem
Comment out code, use binary search.
3. Read Error Messages
Errors often tell you exactly what's wrong.
4. Use Debugger
Step through code line by line.
5. Check Assumptions
Verify your assumptions about the code.
6. Check Recent Changes
Bugs often appear after recent modifications.
7. Take Breaks
Fresh eyes spot bugs faster.
Common Debugging Scenarios
State Not Updating
// Bad: Direct mutation
state.items.push(item); // Won't trigger re-render
// Good: Immutable update
setState({
...state,
items: [...state.items, item]
});Async Issues
// Bad: Race condition
useEffect(() => {
fetchData(userId);
}, [userId]);
// Good: Cleanup
useEffect(() => {
let cancelled = false;
fetchData(userId).then(data => {
if (!cancelled) {
setState(data);
}
});
return () => {
cancelled = true;
};
}, [userId]);Memory Leaks
// Bad: Not cleaning up
useEffect(() => {
const interval = setInterval(() => {
updateData();
}, 1000);
// Missing cleanup!
}, []);
// Good: Cleanup function
useEffect(() => {
const interval = setInterval(() => {
updateData();
}, 1000);
return () => {
clearInterval(interval);
};
}, []);Conclusion
Master debugging tools and you'll fix bugs 10x faster. Learn Chrome DevTools deeply, use React DevTools for component debugging, and always add proper error handling. Debugging is a skill—practice makes perfect.
Remember: The best debugging is prevention. Write clear code, add tests, and use TypeScript to catch errors before runtime.