DevTools: the four panels you use daily
A tour of Chromium DevTools, the panels you reach for to inspect the DOM, network requests, console state, and storage on every web app you build.
The last two lessons mapped the request pipeline, using the Network panel in passing. DevTools is far bigger than that one tab, and you’ll work inside the rest of it for the entire course.
DevTools is how you read the result of every change you make. Edit a class and reload to see the new style, click a button and watch the request go out, suspect a stale cookie and read the cookie jar. This lesson tours the four panels a web engineer reaches for daily, each answering one question you’ll ask constantly:
- Elements: what’s actually rendered right now?
- Network: what did the server send back?
- Console: what does the page think it knows?
- Application: what’s in storage right now?
The goal is the habit of reaching for the right panel without thinking: you skim a bug report and know which one to open. The layout is here; the depth comes from using each panel on real work.
Open a well-styled site in a new tab now, with DevTools docked to the side or bottom; react.dev works well. We’ll switch between panels as the lesson goes.
Setting up: Chromium and React DevTools
Section titled “Setting up: Chromium and React DevTools”The course teaches against Chromium DevTools, and any Chromium browser ships it. Firefox DevTools makes a good cross-browser sanity check: the layout differs but the concepts transfer. Safari DevTools is for the iOS-specific work the deployment unit covers later.
Before the React unit ships its first component, you need one extension: React DevTools. It adds two tabs to DevTools, Components and Profiler, that read the React tree directly rather than the DOM. You won’t use them in this lesson, so install it now and it’s ready when the React unit arrives.
-
Open the Chrome Web Store listing for React Developer Tools and click Add to Chrome. The same listing works for any Chromium-based browser.
-
Open a page that uses React, such as
react.dev, with DevTools open. Two new tabs labelled Components and Profiler appear in the DevTools tab strip. That confirms the install. -
If you’re on Firefox, install the same extension from Firefox Add-ons. Same UI, same two tabs.
Elements: the live DOM and the cascade
Section titled “Elements: the live DOM and the cascade”The Elements panel shows the live DOM . The source HTML the server originally sent is a different thing, which you read with View Source (Ctrl/Cmd+U), and on a React app the two routinely disagree. That disagreement is how React works.
The right pane shows the cascade for whatever element you’ve selected: every CSS rule that targeted it, listed in cascade order, with overridden rules struck through. How the cascade itself resolves, including specificity and why one rule wins, gets its own chapter later when Tailwind lands. Here you’re only learning to read it.
:hov button — force pseudo-states without using the mouse.
Five moves carry the panel’s daily value:
-
Inspect to find.
Cmd+Shift+Con macOS,Ctrl+Shift+Celsewhere, toggles the inspector cursor. Click any pixel on the page and the corresponding DOM node lights up in the tree. This is what you do when a user says “this thing isn’t styled right”: you start at the pixel, not at the source file. -
Read the cascade in the Styles pane. Rules are listed top-down by which one won, with overridden rules struck through. The struck-through line tells you why your rule didn’t apply, usually specificity or order.
-
Edit styles live, with no reload. Click any rule’s value to edit it in place, and the page updates as you type. If it works in DevTools, it’ll work in the code: make the change here first, confirm it does what you want, then go write it.
-
Toggle pseudo-states explicitly. Click
:hovin the Styles pane to force:hover,:focus-visible,:active, or:focus-withinon the selected element. Hovering the element directly seems easier, but moving the mouse to the panel drops the hover state. Forcing the toggle holds the state still. -
Computed tab for the final value. When the cascade gets dense and you stop caring why a value won, the Computed tab shows the single resolved value the browser used, plus which selector won it. Use it when the question is “what is the color, regardless of where it came from.”
Try it now. Open the page in front of you, inspect any button, and click :hov in the Styles pane to toggle :hover. The styles change without your mouse touching the button. Then edit the background-color value in the Styles pane directly: set it to red, or anything else. The button updates with no reload. That is the edit-and-check loop tightened from minutes to milliseconds.
Network: the request log
Section titled “Network: the request log”The Network panel logs every request the page makes (Document, Fetch/XHR, JS, CSS, Images, Fonts, WebSockets) as a row with its timing, headers, payload, and response. The four network stages used the Protocol column and the Timing breakdown, a thin slice of this panel. The rest, the toolbar checkboxes, the throttle, and the request-detail tabs, is where you’ll spend real time debugging a web app.
Seven moves carry the daily workflow:
-
Open the panel before the action that triggers the request. Network only captures while it’s open, so a request fired before you opened it is already gone. Open it first, then click.
-
Turn on Preserve log. By default a redirect or full-page navigation wipes the log; with Preserve log on, every request stays visible across navigations and reloads. Set it as a permanent default in your DevTools settings.
-
Tick Disable cache while DevTools is open. Otherwise a cached request returns in 1ms and you conclude your change worked when nothing went over the wire, while the next user on a cold cache still sees the old behavior. Keep the cache off whenever the panel is open.
-
Throttle to Slow 4G or 3G when testing loading states. When something looks fine on localhost, flip to Slow 4G, reload, and watch the skeleton states actually appear. A per-request override (right-click a row → Override request → Throttle) lets you slow one API call against an otherwise fast page.
-
Filter by type to drop the noise. Doc shows page navigations, Fetch/XHR shows your application’s API calls. Most debugging happens in Fetch/XHR, and the chip cuts the waterfall from hundreds of rows to the dozen that matter.
-
Read the right pane in order: Headers, Payload, Response, Timing. Headers gives you auth, content type, and status; Payload is what your client sent; Response is what the server sent back, with the Preview tab pretty-printing JSON; Timing shows where the time went, mapping onto the four stages covered in The four network stages. Any other order wastes clicks.
-
Right-click → Copy → Copy as fetch / Copy as cURL. This turns a captured request into a reproduction: paste the fetch into the Console, or the cURL into a terminal, and re-run the exact request the browser sent, ready to edit. The full
fetchAPI gets its own chapter later in this unit.
Open Network on the page in front of you. Tick Preserve log and Disable cache, then reload. Find the document request row at the top, right-click it, and choose Copy → Copy as fetch. Paste it into the Console (covered properly in the next section). What you pasted is the same request the browser made, as JavaScript you can edit and re-run.
Console: a REPL inside the page
Section titled “Console: a REPL inside the page”The Console is a REPL inside the running page. Anything in the global scope is reachable, anything a script logs with console.log lands here, and anything you type runs in the page’s own JavaScript context. Most people call console.log() and stop there, but the Console has a whole vocabulary beyond it, and learning that vocabulary pays off more than anything else on this tour.
console.table() output — array of objects rendered as a real columned table.
Seven moves to internalize:
-
Log levels and filtering.
console.logis the default, whileconsole.info,console.warn, andconsole.errorprint at their named levels. The dropdown at the top of the panel lets you hide everything but errors when a page floods the output. For debugging output you want to keep, useconsole.warnandconsole.errorso the important lines stay filterable amid the noise. -
console.table(arrayOfObjects). Renders a real table with one column per object key. Use it instead ofconsole.logfor any array of objects: it saves you from expanding[Object, Object, Object, ...]rows one at a time. -
console.dir(domNode). Prints the full JavaScript property tree of a DOM node, including its properties, methods, and internals, rather than the rendered HTML you get fromconsole.log(node). Reach for it when the question is “what JavaScript properties does this node carry?” rather than “what does it look like in the tree?” -
console.trace(). Prints the stack trace at the call site. Use it when the question is “who called this?” and you don’t want to set a breakpoint to find out. -
$0,$1,$2,$3,$4. References to the last five elements you selected in the Elements panel. Click a node in Elements, switch to Console, and type$0.getBoundingClientRect(). This bridge between the Elements and Console panels is easy to miss. -
copy(value). Copies any value to your clipboard.copy($0.outerHTML)grabs the live rendered markup of the inspected element.copy(JSON.stringify(state, null, 2))copies any complex value as pretty JSON, ready to paste into a bug report or test fixture. -
Live evaluation as you type. Start typing
document.queryS…and DevTools shows the result of the current expression in a faded preview before you hit Enter. This is handy for refining a selector against the live DOM before pasting it into your code.
$0 through $4, copy, and $_ for the last evaluated expression are console utilities . DevTools injects them into the Console’s global scope, so they don’t exist in your application code: call copy() in a script file and you get a ReferenceError. When something works in the Console but not in a .js file, the utilities are the usual reason.
The vocabulary, in five lines:
console.table(users); // an array of objects rendered as a real tableconsole.dir($0); // full JS property tree of the inspected elementconsole.trace(); // stack at this linecopy($0.outerHTML); // live rendered markup → clipboardcopy(JSON.stringify(state)); // snapshot any value as pretty JSONTry the bridge right now. Switch to Elements and inspect any element on the page. Switch to Console and type $0; the element prints. Type console.dir($0) to see its full JavaScript surface unfold. Type copy($0.outerHTML) and paste anywhere; the live rendered markup is on your clipboard.
Application: cookies and storage
Section titled “Application: cookies and storage”The Application panel is the storage and identity inspector. Every place the page persists data lives here, Cookies, Local Storage, Session Storage, IndexedDB, Cache Storage, and Service Workers, and every entry can be inspected, edited, or deleted in place.
For day-to-day web app work, two surfaces matter: Cookies, where the session cookie lives once auth is wired up, and Local/Session Storage, where client-state and URL-state tooling keep their working values. The rest you’ll open once or twice a project, so they’re named here just so you know where to find them.
HttpOnly, Secure, SameSite, Expires, Domain, Path.
Five moves to know:
-
Cookies, the auth surface. Expand Cookies under Storage and click the origin you care about. The right pane lists every cookie with its full attribute set, including the auth-relevant
HttpOnly,Secure, andSameSite. Edit a value in place, double-click any attribute to flip it, or delete a row with the Delete key. This is where you read the session cookie, and when auth fails, the cookie’sSameSiteandSecureflags are worth checking before the server logs. A later chapter in this unit covers what those attributes mean and why they block a request. -
Local Storage and Session Storage. The same edit-and-clear surface, keyed by origin. Local Storage persists across tabs and reloads; Session Storage is per-tab and clears when the tab closes. Values your client-state tooling stashes show up here.
-
IndexedDB. A persistent client-side database. Reach for it when offline state matters, but it’s off the daily path on this stack, since the platform’s data fetching covers most of what you’d otherwise use it for.
-
Service Workers and Cache Storage. Service workers register under their own sidebar entry, and whatever they cache shows in Cache Storage. This stack doesn’t ship one, since Server Components and the platform’s data fetching cover the use cases; they’re named here so you can find them.
-
Clear site data , the reset button. One button at the top of the Storage section wipes cookies, every kind of storage, cache, and service workers for the current origin. It’s the fix for “works in incognito but not here”: your local state has drifted from the server’s expectations, and clearing it is faster than diffing it. Reach for it freely.
Try the cookie inspector now. Open Application → Storage → Cookies on the page in front of you, click your origin, and read SameSite, Secure, and HttpOnly on any cookie. The wrong combination is why an authenticated request can quietly drop its cookie on a cross-site fetch; the Application panel is where you’ll diagnose that when it happens.
The other DevTools panels
Section titled “The other DevTools panels”Map a scenario to a panel
Section titled “Map a scenario to a panel”Each scenario maps to one panel, and some panels come up more than once.
For each production-shaped scenario, click the panel you would open first. Click an item on the left, then its match on the right. Press Check when done.
SameSite and Secure attributeslocalStorage data they shouldn’t havecopy($0.outerHTML) after inspecting in Elementsconsole.trace() at the call siteExternal resources
Section titled “External resources”The canonical reference for every DevTools panel. Skim the panel docs as you start using each one in earnest.
Monthly changelog of new DevTools features. Worth bookmarking once you're past the basics, since the panels gain new tools every release.
The cross-browser sanity check. Concepts transfer from Chromium; the layout differs.
The official guide to the Components and Profiler tabs you just installed. Bookmark for when the React unit lands.