Chrome DevTools
Elements, Network, Sources, Performance, Lighthouse.
Open any web page. Press Cmd+Option+I (Mac) or F12 (Windows/Linux). That panel that just appeared is the single most powerful tool in your career. Most developers use about 5% of it. This lesson is the guided tour to push that to 50%.
Elements: the live DOM
The Elements panel shows the current DOM, not the original HTML. If JavaScript added a <div> after page load, it shows up here. You can edit anything live and the page updates instantly.
- Right-click any element → Edit as HTML to rewrite a chunk of the page on the fly.
- Styles pane shows the cascade. Toggle any property with its checkbox. Click
+to add a new rule. - Computed pane shows the final resolved value of every CSS property, with which rule won.
- :hov in the styles header lets you force pseudo-states like
:hoverand:focus-visibleso you can inspect them.
Console: your REPL inside the page
The Console runs JS in the context of the current page. You have access to global variables, the DOM, and a bunch of dev-only utilities.
// $0 is whatever is currently selected in the Elements panel
$0 // <div class="hero">...</div>
$0.style.outline = "2px solid red"
// $ and $$ are shorthand for querySelector/querySelectorAll
$("nav a") // first <a> inside <nav>
$$(".btn") // NodeList of all .btn elements
// inspect the value clearly
console.log({ user, count }) // labels included
console.dir($0) // expandable object view
// tables: a game-changer for arrays of objects
console.table([
{ id: 1, name: "Ada", role: "admin" },
{ id: 2, name: "Bob", role: "user" }
]);
// ┌─────────┬────┬───────┬─────────┐
// │ (index) │ id │ name │ role │
// │ 0 │ 1 │ "Ada" │ "admin" │
// │ 1 │ 2 │ "Bob" │ "user" │
// └─────────┴────┴───────┴─────────┘Other console verbs you should know:
console.warn("Deprecated API used") // yellow
console.error("Validation failed") // red, with stack trace
console.group("Loading user"); // collapse-friendly section
console.log("fetching ...");
console.log("got response");
console.groupEnd();
console.time("query"); // measure elapsed time
await db.query("SELECT ...");
console.timeEnd("query"); // query: 124.5ms
console.count("render"); // increment a named counter
// render: 1, render: 2, render: 3 ...
console.assert(arr.length > 0, "Array should not be empty");Network: what the browser is actually fetching
Every request the page makes shows up here: HTML, CSS, JS, fonts, API calls, web sockets. Each row gives you method, status, size, timing.
- Filter row - narrow by
Fetch/XHR,JS,CSS,Img, etc. Most days you just wantFetch/XHRto watch API calls. - Preserve log - keeps requests across page reloads. Essential when debugging auth redirects.
- Disable cache (with DevTools open) - forces the browser to refetch everything, useful for testing cold loads.
- Throttling- pretend you're on a slow 3G connection. Your homepage probably needs this test.
- Click a request - Headers, Payload, Preview, Response, Timing tabs. The waterfall in Timing shows DNS, TLS, waiting, downloading.
Sources: breakpoints, the real debugger
Yes, you can console.log. But a breakpoint lets you pause time, inspect every variable, and step through one line at a time.
- Line breakpoint - click the line number in any source file. Code stops there before executing.
- Conditional breakpoint - right-click the line number, set a condition like
userId === 42. Only pauses when the condition is true. - Logpoint - same right-click menu. Logs an expression to the console without pausing. The unbeatable replacement for adding
console.logand re-deploying. - DOM breakpoint - right-click an element in Elements → Break on → subtree modifications. Pauses when JS mutates that part of the DOM.
- XHR/Fetch breakpoint - pause every time a URL containing a substring is requested. Magic for finding where a mystery request comes from.
When paused, the right panel shows: Scope (all variables in scope), Call Stack (how you got here), Watch (expressions you want to re-evaluate at each step), and Breakpoints.
function reducer(state, action) {
// set a logpoint here:
// "reducer called with", action.type, action.payload
// → console messages appear with no code change required
switch (action.type) {
case "increment":
return { count: state.count + 1 };
// ...
}
}debugger;statement in your code is the same as setting a breakpoint there. It only triggers when DevTools is open, so it's a fine way to pause from a build-step file you can't easily click in.Application: storage, cookies, service workers
Where the browser keeps your state.
- Local Storage / Session Storage - see and edit every key. Right-click → Clear to nuke the lot.
- Cookies - including
HttpOnlyones. Watch what your auth flow is setting. - IndexedDB - the heavyweight client-side store. You can browse object stores like a tiny database.
- Service Workers- "Update on reload" and "Bypass for network" checkboxes have saved many an hour of debugging a stale cached app.
- Cache Storage - what your service worker has cached.
Lighthouse: an instant audit
Lighthouse runs the page through automated tests and gives you scores for Performance, Accessibility, Best Practices, and SEO. Each finding is actionable, with documentation links.
# you can also run it from the CLI
npx lighthouse https://example.com --viewDon't obsess over a perfect 100 on every metric. Use it to find the easy wins: missing alt text, unused JavaScript, oversized images, no-cache headers on assets.
Performance: flame charts in 60 seconds
When something feels slow, the Performance panel records what the browser actually did. Hit record, interact with the page, stop. You get:
- Frames timeline at the top, showing dropped frames in red.
- Main thread flame chart - call stacks over time. Wide blocks at the top are functions that took long. Tall stacks deep below are nested call chains.
- Bottom-up / Call Tree tabs - aggregate the same data by time spent.
The standard playbook: record 5 seconds of slow interaction, find the widest block in the flame chart, click it, see what file and line is the hot spot. 9 times out of 10 the culprit is obvious.
?profile bundle for React) for results that match reality.Hidden gems
Cmd+Shift+Popens the "command palette" in DevTools. Type "screenshot" for a full-page screenshot command. Type "disable JavaScript" to see if your site works at all without JS.- Coverage tab (in More Tools) shows you which CSS and JS your page actually used during load. Reveals dead code that ships but never runs.
- Rendering tabhas "Paint flashing" (highlights re-paints) and a layout shift visualizer. Animation debugging gold.
- copy(value) in the Console copies any value to your clipboard, even objects (as JSON).
Quick quiz
You're staring at an API bug and want to log every render of a function without changing the code. What's the right DevTools tool?
Recap
- Elements: live DOM and CSS. Edit anything, force pseudo-states.
- Console:
$0,$$,console.table,group,time,count. - Network: filter, preserve log, throttle, copy as fetch.
- Sources: conditional breakpoints, logpoints, DOM breakpoints, the
debugger;keyword. - Application: storage, cookies, service workers.
- Lighthouse for audits. Performance for flame charts on real production builds.