Concept
Chrome DevTools (and equivalents in Firefox, Safari, Edge) is divided into panels. Each panel surfaces a different view of what the browser is doing. Mastery means knowing which panel to open for which class of problem.
Elements panel
The live view of the DOM and computed styles. Changes here are not saved to source.
- Inspect element: right-click → Inspect, or
Cmd+Shift+Cto enter picker mode - Break on subtree modifications: right-click a node → Break on → Subtree modifications. Sets a DOM breakpoint, pauses JS execution when any descendant is mutated. Invaluable for finding "something is changing this DOM node and I don't know what."
- Computed tab: shows final resolved styles (post-cascade, post-inheritance). When a style isn't applying, compare Styles (declared) vs Computed (resolved).
- Accessibility panel: shows the accessibility tree as screen readers see it. Check role, name, and state for any interactive element.
:hovbutton: force element states (:hover,:focus,:active) without mouse interaction.
Console panel
The REPL, log output, and error surface.
// Useful console APIs beyond console.log:
console.table([{name: 'Alice', age: 30}, {name: 'Bob', age: 25}]); // formatted table
console.time('fetch'); fetch('/api'); console.timeEnd('fetch'); // timing
console.group('Auth'); console.log('token set'); console.groupEnd(); // collapsible groups
console.trace(); // stack trace at call site
console.dir(element); // DOM element as JS object
$0 // currently selected element in Elements panel
$$('.btn') // querySelectorAll shorthand
Live expressions: click the eye icon → type an expression → it updates in real-time. Useful for watching window.scrollY or React state without adding console.logs.
Network panel
Every HTTP request your page makes, with full timing breakdown.
Waterfall reading
The waterfall shows time on the horizontal axis, requests on vertical. Each row has colored segments:
- Gray (Queuing): browser waiting to send (connection limit, prioritisation)
- Gray (Stalled): connection establishment hasn't started
- Green (DNS Lookup): resolving hostname to IP
- Orange (Initial Connection): TCP handshake
- Purple (SSL): TLS handshake
- Green (TTFB): time from request sent to first response byte (server time + network)
- Blue (Content Download): downloading the response body
A tall green TTFB bar means your server is slow, no amount of CDN helps with that. Many gray/stalled bars means you're hitting the 6-connections-per-origin limit (HTTP/1.1) or the browser queue.
Key DevTools Network features
Filter bar Filter by type (XHR, JS, CSS, Doc), name, status, domain
Preserve log Keep requests across page navigations (for debugging redirects)
Disable cache Forces full cache bypass (simulates new user), only while DevTools is open
Throttle Simulate 3G, offline, custom bandwidth
Block requests Block specific URLs or patterns (context menu → Block request URL)
Initiator Who triggered this request (JS stack trace, CSS import)
Response headers Inspect Cache-Control, ETag, Content-Type for each response
Copy as cURL Copy a request as a curl command to replay in terminal
Replay XHR Right-click a request → Replay XHR (resend it)Columns to enable (right-click column headers):
- Protocol: verify HTTP/2 or HTTP/3
- Priority: fetch priority (Highest, High, Low)
- Cache: hit / miss / ServiceWorker
- Initiator: what triggered the request
Performance panel
The Performance panel records a timeline of everything the main thread and GPU do during a recording.
Recording a profile
- Open Performance panel
- Check "Screenshots" and "Web Vitals"
- Click Record (or press
Cmd+E) - Interact with the page
- Stop recording
Reading the flame chart
The flame chart shows time (horizontal) vs call stack depth (vertical). Each block is a function call; width = time spent.
[Task]────────────────────────────────────────────────────
[Parse HTML] [Layout] [Paint]
[Evaluate Script]─────────────────
[myFunction]──────────────────
[expensiveComputation]──────── ← tall + wide = hot pathWhat to look for:
- Long tasks (red diagonal stripe = > 50ms): these block the main thread and increase INP
- Forced reflow/layout: "Recalculate Style" or "Layout" following a JS block = layout thrashing
- Long paint records: complex CSS shadows, large canvas operations
Web Vitals markers
The Performance panel overlays LCP, FCP, and layout shift events on the timeline. Click any event to see what element triggered it and why.
CPU and network throttling
In Performance panel → Settings (⚙️ icon):
- CPU throttling: 4× or 6× slowdown simulates mobile devices
- Always profile with CPU throttling, desktop CPUs are 5, 10x faster than typical mobile
Memory panel
The Memory panel finds memory leaks and identifies high-memory usage.
Heap snapshot
Captures the complete JS heap at a point in time. Shows every object, what's retaining it, and how much memory it uses.
Leak detection workflow:
- Take snapshot 1 (baseline)
- Perform the action you suspect causes a leak (navigate, open modal, run interval)
- Force GC (trash can icon)
- Take snapshot 2
- Change "All objects" to "Objects allocated between Snapshot 1 and 2"
- Sort by retained size, any unexpected objects here are your leak
Common leak sources in the heap snapshot:
- Closures retaining large objects
- Detached DOM nodes (removed from DOM but still referenced in JS)
- Event listeners on removed elements
- Global variables accumulating arrays
Detached DOM trees
Filter the heap snapshot for "Detached", these are DOM elements that were removed from the document but are still referenced by JS. Every detached element leaks its entire subtree.
// Common cause:
const list = document.querySelector('.list');
const handler = () => list.innerHTML; // list reference kept alive
document.querySelector('.list').remove(); // removed from DOM
// handler still holds reference → list is detached, won't be GC'dAllocation instrumentation
Records object allocations over time (not just a snapshot). Shows which JS functions are allocating the most memory. Good for finding allocation hot paths in animations or frequent re-renders.
Application panel
- Local Storage / Session Storage / IndexedDB / Cookies: inspect, edit, delete
- Service Workers: check registration status, update, unregister, send push events
- Cache Storage: inspect what your service worker has cached
- Manifest: check your PWA manifest is parsed correctly
- Frames: storage scoped per-frame (useful in iframes)
Sources panel
The debugger. Set breakpoints, step through code, watch variables.
Breakpoint types:
- Line breakpoint (click gutter)
- Conditional breakpoint (right-click gutter → Add conditional)
- Logpoint (right-click → Add logpoint), console.log without modifying code
- XHR breakpoints (break when a URL matching a pattern is fetched)
- Event listener breakpoints (break on
click,keydown, etc.) - DOM breakpoints (set in Elements panel)
Useful debugger shortcuts:
F8 / Cmd+\ Resume / Pause
F10 Step over
F11 Step into
Shift+F11 Step outBlackboxing: right-click a file → Add to ignore list. Prevents the debugger from stepping into framework/library code you don't care about.
Common Mistakes
1. Profiling without CPU throttling
Desktop Chrome on a 2024 MacBook Pro is 8x faster than a mid-range Android phone. Always profile with 4x CPU throttle to get realistic numbers.
2. Using "Preserve log" accidentally
Preserve log keeps requests from previous navigations. If you forget it's on, you see hundreds of stale requests and misattribute issues to the wrong page.
3. Taking only one heap snapshot
A single snapshot just shows what's in memory. You need baseline + post-action snapshots to see what was added and is being retained (the leak).
4. Ignoring the Initiator column
"Some script is making a mysterious network request", click the Initiator column. It shows the call stack that triggered the request. You'll find the exact line.
5. Debugging minified code without sourcemaps
Enable sourcemaps (build.sourcemap: true in Vite, devtool: 'source-map' in Webpack). Without them you're debugging n(e,t){return a.call(this,e,t)}.
Best Practices
- Profile before optimising. Guess-and-check wastes time. The Performance flame chart shows you exactly where 80% of the time is going.
- Use Logpoints instead of adding
console.logto source, faster and leaves no lint warning. - Check the Protocol column on every new project, verify you're on HTTP/2. HTTP/1.1 without multiplexing is a performance cliff.
- Monitor the Memory panel before and after any feature that runs long sessions (dashboards, SPAs with no page navigations).
