How to Debug JavaScript in Browser: Chrome DevTools Guide
Effective JavaScript debugging: breakpoints, console, network, performance. Practical Chrome DevTools guide.
Introduction to Browser Debugging
Debugging JavaScript in the browser is an essential skill for every web developer. Chrome DevTools provides a powerful suite of tools that can help you identify and fix bugs quickly, optimize performance, and understand how your code executes. This comprehensive guide will walk you through everything you need to know about debugging JavaScript effectively.
Whether you're tracking down a mysterious bug, profiling performance bottlenecks, or simply trying to understand how a piece of code works, mastering DevTools will significantly improve your productivity as a developer.
Opening Chrome DevTools
Before diving into debugging techniques, let's cover the basics of accessing DevTools:
- Keyboard shortcut (Windows/Linux):
F12orCtrl + Shift + I - Keyboard shortcut (Mac):
Cmd + Option + I - Right-click menu: Right-click anywhere on the page and select "Inspect"
- Chrome menu: More tools → Developer tools
DevTools opens as a panel that can be docked to the bottom, right side, or left side of your browser window, or opened as a separate window entirely.
The Console Panel
Basic Console Methods
The Console is often the first tool developers reach for when debugging. Beyond the basic console.log(), there are many powerful methods available:
// Basic logging
console.log('Simple message');
console.log('Value:', myVariable);
// Styled console output
console.log('%cStyled text', 'color: blue; font-size: 20px');
// Warning and error levels
console.warn('This is a warning');
console.error('This is an error');
// Grouping related logs
console.group('User Data');
console.log('Name:', user.name);
console.log('Email:', user.email);
console.groupEnd();
// Table format for arrays/objects
console.table([
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 }
]);
// Timing operations
console.time('fetchData');
await fetchData();
console.timeEnd('fetchData'); // Output: fetchData: 234.5ms
// Stack trace
console.trace('Trace point');
Console Utilities
DevTools provides special utility functions only available in the Console:
// Select elements (like jQuery)
$('selector') // Returns first matching element
$$('selector') // Returns all matching elements
// Get the currently selected element in Elements panel
$0 // Current selection
$1 // Previous selection
// Monitor function calls
monitor(functionName);
// Copy any value to clipboard
copy(object);
// Get event listeners on an element
getEventListeners($0);
Setting Breakpoints
Line-of-Code Breakpoints
The most common debugging technique is setting breakpoints in your code. When execution reaches a breakpoint, it pauses, allowing you to inspect the current state:
- Open the Sources panel
- Navigate to your JavaScript file in the file tree
- Click on the line number where you want to pause
- A blue marker appears, indicating an active breakpoint
Conditional Breakpoints
Sometimes you only want to pause when certain conditions are met:
- Right-click on a line number
- Select "Add conditional breakpoint"
- Enter a JavaScript expression (e.g.,
user.id === 42) - The breakpoint only triggers when the condition is true
// This breakpoint condition would pause only for specific users
user.role === 'admin' && user.permissions.includes('delete')
Logpoints
Logpoints are like breakpoints that log messages instead of pausing execution—perfect for adding temporary logging without modifying your code:
- Right-click on a line number
- Select "Add logpoint"
- Enter the message to log (can include expressions in curly braces)
// Logpoint example
"User {user.name} clicked button at {new Date().toISOString()}"
DOM Breakpoints
Pause execution when the DOM changes:
- In the Elements panel, right-click on an element
- Select "Break on" and choose:
- Subtree modifications: When child elements change
- Attribute modifications: When attributes change
- Node removal: When the element is removed
Event Listener Breakpoints
Pause on specific types of events:
- In Sources panel, expand "Event Listener Breakpoints"
- Check the events you want to break on (e.g., Mouse → click)
- When that event fires, execution pauses
Stepping Through Code
Once paused at a breakpoint, use these controls to navigate through your code:
- Resume (F8): Continue execution until the next breakpoint
- Step Over (F10): Execute the current line and move to the next
- Step Into (F11): Enter the function being called
- Step Out (Shift + F11): Finish the current function and return to caller
- Step (F9): Step to the next line, entering async operations
The Call Stack
The Call Stack panel shows you the chain of function calls that led to the current point:
// If your code looks like this:
function processOrder(order) {
validateOrder(order); // Currently paused here
}
function validateOrder(order) {
checkInventory(order.items);
}
function checkInventory(items) {
// Breakpoint here
}
// The call stack shows:
// checkInventory (current)
// validateOrder
// processOrder
// (anonymous) or main entry point
Click on any frame in the call stack to see the code and variables at that point in the execution.
Inspecting Variables and Scope
The Scope Panel
When paused, the Scope panel shows all variables accessible at the current point:
- Local: Variables in the current function
- Closure: Variables from enclosing scopes
- Global: Global variables (window object)
Watch Expressions
Add expressions to watch their values as you step through code:
- In the Sources panel, find the "Watch" section
- Click the + button
- Enter any JavaScript expression
// Useful watch expressions
user.permissions.length
items.filter(i => i.price > 100).length
Date.now() - startTime
Hover to Inspect
While paused, hover over any variable in your code to see its current value. For objects, you can expand them to see all properties.
Network Panel Debugging
The Network panel is essential for debugging API calls and resource loading:
Filtering Requests
- XHR: Show only AJAX/fetch requests
- JS: JavaScript files
- CSS: Stylesheets
- Img: Images
- Filter box: Search by URL, method, or status
Inspecting Request Details
Click on any request to see:
- Headers: Request and response headers
- Payload: Request body (for POST/PUT requests)
- Preview: Formatted response (JSON, HTML, images)
- Response: Raw response body
- Timing: Detailed timing breakdown
For JSON API responses, you can use our JSON Formatter tool to better visualize and validate complex data structures.
Throttling Network Speed
Test how your app behaves on slow connections:
- Open the Network panel
- Find the throttling dropdown (usually shows "No throttling")
- Select a preset like "Slow 3G" or "Fast 3G"
- Or create custom throttling profiles
Performance Debugging
The Performance Panel
Profile your application to find performance bottlenecks:
- Open the Performance panel
- Click the record button (or press Ctrl/Cmd + E)
- Perform the actions you want to profile
- Stop recording
- Analyze the flame chart and timing data
Identifying Long Tasks
Look for:
- Red triangles: Long tasks blocking the main thread
- Yellow bars: JavaScript execution time
- Purple bars: Rendering/layout time
- Green bars: Painting time
Memory Profiling
Track down memory leaks:
- Open the Memory panel
- Take a heap snapshot
- Perform actions that might leak memory
- Take another snapshot
- Compare snapshots to find objects that weren't garbage collected
Debugging Async Code
Async Stack Traces
Modern DevTools can track async operations across setTimeout, Promise, and async/await:
async function fetchUserData(userId) {
const response = await fetch(`/api/users/${userId}`);
const user = await response.json(); // Breakpoint here
return user;
}
// The async call stack shows the full chain:
// fetchUserData (async)
// handleClick
// (anonymous)
Promise Debugging
Enable "Pause on caught exceptions" to debug Promise rejections:
- In Sources panel, click the pause icon with the arrow
- Choose "Pause on caught exceptions"
- Now Promise rejections will pause even if caught
Source Maps
When debugging minified or transpiled code (TypeScript, Babel, Webpack), source maps let you debug the original source:
// webpack.config.js
module.exports = {
devtool: 'source-map', // Full source maps for development
// Or 'cheap-module-source-map' for faster builds
};
With source maps enabled, DevTools shows your original TypeScript/ES6+ code instead of the compiled output.
Useful Keyboard Shortcuts
| Action | Windows/Linux | Mac |
|---|---|---|
| Open DevTools | F12 / Ctrl+Shift+I | Cmd+Option+I |
| Open Console | Ctrl+Shift+J | Cmd+Option+J |
| Search all files | Ctrl+Shift+F | Cmd+Option+F |
| Go to file | Ctrl+P | Cmd+P |
| Go to line | Ctrl+G | Cmd+G |
| Toggle breakpoint | Ctrl+B | Cmd+B |
| Resume execution | F8 | F8 |
| Step over | F10 | F10 |
| Step into | F11 | F11 |
Advanced Debugging Techniques
Live Expressions
Pin expressions that update in real-time:
- Click the eye icon in the Console toolbar
- Enter an expression
- Watch it update live without re-executing
Snippets
Save and run reusable debugging scripts:
- In Sources panel, go to Snippets tab
- Create a new snippet
- Write your debugging code
- Run with Ctrl/Cmd + Enter
// Example snippet: Log all click handlers
document.querySelectorAll('*').forEach(el => {
const listeners = getEventListeners(el);
if (listeners.click) {
console.log(el, listeners.click);
}
});
Blackboxing Scripts
Skip stepping through library code:
- Right-click on a file in Sources panel
- Select "Add script to ignore list"
- The debugger will skip over this file when stepping
Debugging Common Issues
Finding Undefined Variables
Use the Console with pause on exceptions:
// Instead of this crashing silently:
const name = user.profile.name;
// Use optional chaining and log:
console.log('user:', user);
console.log('profile:', user?.profile);
const name = user?.profile?.name ?? 'Unknown';
Tracking Event Handlers
Find what's handling an event:
// In console, inspect event listeners
getEventListeners(document.querySelector('#myButton'));
// Or use monitorEvents
monitorEvents(document.querySelector('#myButton'), 'click');
// Now click the button to see events logged
Debugging Regular Expressions
When working with complex patterns, test them first with our Regex Tester tool before implementing in your code. This saves time and helps visualize matches.
Remote Debugging
Debugging Mobile Devices
Debug mobile Chrome on Android:
- Enable USB debugging on your Android device
- Connect via USB
- In Chrome, go to
chrome://inspect - Find your device and click "Inspect"
Debugging Node.js
Debug Node.js applications with Chrome DevTools:
# Start Node with inspector
node --inspect app.js
# Or with break on first line
node --inspect-brk app.js
Then open chrome://inspect in Chrome and click "Open dedicated DevTools for Node".
Conclusion
Mastering Chrome DevTools is an investment that pays off every day as a JavaScript developer. From basic console logging to advanced performance profiling, these tools give you deep visibility into how your code executes.
Key takeaways:
- Use conditional breakpoints and logpoints to debug without modifying code
- Master the stepping controls to navigate complex code flows
- Leverage the Network panel to debug API interactions
- Profile performance to find and fix bottlenecks
- Use source maps to debug original source code
For more development tools and resources, check out our free online developer tools, including the JSON Formatter for API response debugging and the Regex Tester for pattern validation.
For the official documentation, visit the Chrome DevTools documentation and MDN's guide to browser developer tools.