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:
javascript// Select elements document.querySelector('.button') document.querySelectorAll('div.item') // Computed styles getComputedStyle(element) // Force state // Right-click element > Force State > :hover
Console Panel
Logging strategies:
javascript// 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:
javascript// 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, Response
Sources Panel
Set breakpoints and debug:
javascript// 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 execution
React DevTools
Component Inspection
bashnpm install --save-dev react-devtools
Features:
- View component hierarchy
- Inspect props and state
- Measure component performance
- Trace component updates
Profiler Component
javascriptimport { 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
javascriptimport { 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
javascript// 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
javascript// 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
javascript// 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:
javascript// Take memory snapshot // DevTools > Memory > Take Snapshot // Look for: // - Detached DOM nodes // - Event listeners // - Global variables
Heap Snapshots
javascript// Force garbage collection // (in Chrome, with --js-flags flag) // Compare snapshots // Snapshot 1 > Use app > Snapshot 2 // Compare to find leaks
Network Debugging
Fetch API
javascriptfetch('/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
javascript// 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
javascripttry { riskyOperation(); } catch (error) { console.error('Caught error:', error); // Log to error tracking service logError(error); }
Global Error Handler
javascript// 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)
javascriptclass 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
javascript// 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)
html<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
javascript// Bad: Direct mutation state.items.push(item); // Won't trigger re-render // Good: Immutable update setState({ ...state, items: [...state.items, item] });
Async Issues
javascript// 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
javascript// 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.