Browser DevTools in 10 Minutes

Browser DevTools is not a bag of hidden tricks. It is the shortest route from “the page is broken” to evidence about the DOM, CSS, requests, JavaScript, and rendering. My rule: do not begin by spraying console.log through the codebase. First identify which layer lied.

🎙️ Published & recorded: ·

01Start with the layer that failed

Open DevTools with F12, Control Shift I, or Command Option I. Then classify the symptom. Wrong shape or color belongs in Elements. Missing data belongs in Network. A stopped interaction belongs in Console and Sources. A slow page belongs in Network timing or Performance. State that survives a refresh belongs in Storage. This classification takes ten seconds and prevents half an hour of random edits.

# Symptom → first panel
button is hidden              → Elements / Styles
API data is absent            → Network / Fetch-XHR
click does nothing            → Console, then Sources
works only after hard refresh → Network cache + Application storage
page freezes while scrolling  → Performance
keyboard cannot reach control → Elements / Accessibility
A firm rule

Do not add console logs first. A log changes timing, misses failures before the log, and tells you only what you guessed to print. Preserve the failing page, inspect the existing console and request record, then set a breakpoint where execution diverges. Add one targeted log only when a breakpoint cannot capture the environment.

02Elements and Styles: inspect the browser's truth

The Elements tree is the live DOM, not necessarily the HTML file you wrote. Use the element picker, select the broken node, and inspect matched rules, crossed-out declarations, inherited values, and the Computed tab. Toggle a declaration instead of deleting it. Add a temporary property in DevTools, prove the fix, then make the real change in source. Refreshing discards the experiment.

/* The declaration is crossed out because specificity wins */
.card button { color: white; }
#checkout .card button { color: black; }

/* Inspect Computed → color, then follow the winning rule. */
/* Fix the selector design; do not reach for !important by reflex. */
.checkout-button { color: white; }
When the box is there but invisible

One: in Computed, check display, visibility, opacity, dimensions, overflow, and transform. Two: inspect ancestors for clipping and stacking contexts. Three: toggle the suspicious rule. Four: copy the corrected declaration back to the stylesheet. If the DOM node itself is missing, stop debugging CSS and inspect the rendering condition or request data.

03Console: read the first useful failure

Turn on Preserve log only when navigation is part of the bug, clear the console, reproduce once, and read from the first red entry downward. Later errors are often fallout. Click the file and line to open Sources. Expand objects deliberately; a logged object may show its later mutated state, so log a copied snapshot when history matters.

Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')
    at app.js:18:9

// Step 1: click app.js:18 and inspect the selector result.
const button = document.querySelector("#save"); // null
// Step 2: verify the live DOM and script timing.
// Step 3: fix the ID, or load code after the DOM exists.
document.addEventListener("DOMContentLoaded", init);

Do not “fix” this with optional chaining if the button is required. That merely turns a loud wiring error into a dead button. Confirm whether the selector is wrong, the component did not render, or the script ran too early. Repair the actual contract.

04Network: inspect the request, not your intention

Open Network before reproducing because it records only while open. Filter to Fetch or XHR, select the request, and inspect status, final URL, method, request headers, payload, response headers, response body, initiator, and timing. Copy as cURL when you need to separate browser behavior from server behavior, but remove cookies and authorization before sharing it.

GET https://example.com/api/users 404 (Not Found)
Access to fetch at 'https://api.example.net/users' from origin
'https://example.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present.
Two failures, two fixes

For the 404, inspect the final Request URL, compare it with the deployed server route, check base paths and case, then call that exact URL directly. For CORS blocked, find the failed request or OPTIONS preflight, inspect its response headers, and configure the API server to allow the exact origin, method, and headers. Do not install a browser extension and do not add the response header in frontend JavaScript; the server owns that permission.

05Request timing and cache: find where time went

A slow request is not one number. Queueing can mean connection limits or main-thread pressure. DNS, connection, and TLS point to setup cost. Waiting, often called time to first byte, points toward server or network latency. Content download points to payload size or a slow stream. Read the Timing tab before deciding which team owns the delay.

# Example timing interpretation
Queueing       2 ms
DNS            0 ms       # reused connection or cached lookup
Initial conn   0 ms
Waiting      842 ms       # investigate server work / upstream latency
Download      18 ms       # payload transfer is not the problem

Cache-Control: public, max-age=31536000, immutable
Cache-Control: no-cache  # may store; must revalidate
Cache-Control: no-store  # must not store

Use Disable cache only while DevTools is open and only for a controlled test. First reproduce with normal cache, then compare with cache disabled, then inspect Size for memory cache, disk cache, or transferred bytes. If users see old hashed assets, check the HTML cache and service worker before blaming the asset cache.

06Storage: inspect state without clearing everything

The Application panel exposes cookies, local storage, session storage, IndexedDB, cache storage, and service workers. Inspect the exact key before clearing it. “Clear site data” destroys evidence and can make a bug disappear without teaching you why. For cookies, verify domain, path, expiration, SameSite, Secure, HttpOnly, and whether the request actually included the cookie.

# Cookie exists in Storage but is absent from the request?
Set-Cookie: session=abc; Path=/; Secure; HttpOnly; SameSite=Lax

# Check in order:
1. Request URL matches Domain and Path
2. HTTPS is used when Secure is set
3. SameSite permits this request context
4. Fetch uses credentials when cross-origin
5. Third-party-cookie policy did not block it

If a stale service worker serves an old app, open Application, inspect the active worker and cache names, enable update on reload for one test, then unregister only after recording the state. The production fix is a versioned cache and a deliberate activation strategy, not instructions telling every user to empty their browser.

07Sources: pause where the wrong value appears

A breakpoint freezes the program with local variables, scope, call stack, and the triggering event intact. Set a line breakpoint just before the bad branch. Use a conditional breakpoint when only one record fails, an XHR breakpoint for requests containing a URL fragment, an event-listener breakpoint for mysterious clicks, and pause on caught exceptions when a library swallows the useful error.

// Conditional breakpoint expression:
order.id === "ord_1842" && order.total < 0

// Then inspect, without editing production code:
order
order.items
subtotal
new Error().stack

DevTools failed to load source map:
Could not load content for /assets/app.js.map: HTTP error: status code 404
Source map 404, fixed

One: inspect the bottom of the built JavaScript for its sourceMappingURL. Two: open that resolved map URL in Network and confirm the 404. Three: upload the map at that exact path or change the build output reference. Four: if maps must stay private, remove the public reference and upload maps only to your error tracker. The application may still run, but debugging minified code will be needlessly painful.

08Performance: record one interaction

Record the smallest slow interaction: start, perform one action, stop. Look for a long main-thread task, then expand it into scripting, style recalculation, layout, paint, and the call tree. Yellow means JavaScript work, purple points to style and layout, and green points to paint. The color is a lead, not a verdict; select the event and inspect its initiator.

# A common layout-thrashing pattern
for (const row of rows) {
  row.style.width = container.offsetWidth + "px"; // read/write repeated
}

# Batch the read, then the writes
const width = container.offsetWidth;
for (const row of rows) row.style.width = width + "px";

Use CPU throttling to reveal fragile interactions, but label the result as simulated. For loading, record a reload and connect large resources or long tasks to a user-visible delay. Do not optimize the prettiest flame-chart block. Fix the work that blocks the actual interaction.

09Device mode and accessibility are filters, not proof

Device mode is excellent for viewport width, responsive breakpoints, touch event emulation, orientation, and network throttling. It is not a real iPhone, Android GPU, mobile browser chrome, or on-screen keyboard. Use it to find likely failures, then test important paths on physical devices.

# Quick responsive checks
320 CSS px wide · 200% zoom · landscape · slow network

# Accessibility checks in Elements
computed role · accessible name · keyboard focus · contrast

# Bad: clickable div with no keyboard semantics
<div onclick="save()">Save</div>
# Good: native behavior and accessible name
<button type="button">Save</button>

Inspect the Accessibility pane for role and name, then navigate the page using Tab, Shift Tab, Enter, Space, and Escape. Automated issue panels catch missing labels and contrast failures, but they cannot tell whether focus order makes sense or a screen-reader announcement is useful. Native HTML beats a repaired div.

10Troubleshooting workflow and cheat sheet

Preserve the failure, reproduce it once, and follow evidence from the user-visible symptom to the responsible layer. Change one variable, retest the same steps, and write down what disproved your first theory. That is faster than collecting twenty plausible causes.

# Triage order
1. Reproduce with exact URL, account, viewport, and action
2. Console: first red error, file, line, call stack
3. Network: URL, status, payload, response, initiator, timing
4. Elements: live DOM, winning CSS, box and accessibility tree
5. Storage: exact cookie/key/worker involved
6. Sources: breakpoint before divergence; inspect call stack
7. Performance: record one slow action
8. Fix source, reload cleanly, repeat original steps

# Error shorthand
404 → verify final URL and deployed route
CORS blocked → inspect preflight; fix API response headers
Uncaught TypeError → click line; inspect value and lifecycle
source map 404 → fix map URL/deployment or remove reference
stale page → compare cache, service worker, and storage

# Useful shortcuts
F12 / Ctrl+Shift+I / Cmd+Option+I  open DevTools
Ctrl+Shift+P / Cmd+Shift+P         command menu
Ctrl+Shift+C / Cmd+Shift+C         pick an element

The browser already captured most of the crime scene. My preferred order is Network before logs, breakpoints before extra logging, and one narrow performance recording before any optimization. DevTools becomes powerful when it replaces guesses, not when you memorize every panel.

Tell me what missed

A correction is more useful than a compliment. This goes straight to the person who writes SwiftGrasp.

Was this page useful?
0/1000

Please do not include passwords, private keys, or personal information.