Chrome DevTools Cheatsheet - Front-end Debugging Reference

Console.log poking is a poor substitute for stepping through a live page. This reference covers Elements and DOM breakpoints, console tricks, Network waterfall and request replay, Performance flame charts, and Application storage. Use it when a page works on your laptop but misbehaves in production — isolate a layout bug, find a slow request, or trace a storage leak. After reading you inspect, measure and fix frontend problems from inside the browser instead of guessing.

Dev Tools·54 commands·Last updated 2026-07-21
chromedevtoolsDebugging

Typical Use Case

For web developers and testers who need to pin down front-end problems precisely. Six panels form one investigation path: Console for logging, timing, counting and interactive element picking with $0; Elements for editing HTML, forcing pseudo-states, and breaking on DOM changes to find who changed a style; Network for filtering, throttling, replaying requests and copying a failed call as cURL; Performance for locating jank in the main thread and flame chart; Application for local storage, cookies, IndexedDB and service workers. The two highest-value habits are Ctrl+Shift+C followed by $0 to verify a change live, and turning a failed request into a cURL command you can rerun in a terminal.

Shortcuts 8

F12 or Cmd+Option+I
Open DevTools (macOS)
F12 or Ctrl+Shift+I
Open DevTools (Win/Linux)
Cmd+Option+J
Open Console (macOS)
Cmd+Option+E
Open Elements (macOS)
Cmd+Shift+C
Select-element mode
Cmd+Shift+P
Open the command menu
Cmd+P
Quick open file
Cmd+Shift+M
Toggle device emulation

Console Commands 12

console.log("message")
Log a message
console.table(array)
Print as a table
console.time("label") / console.timeEnd("label")
Timing
console.count("label")
Count
console.group("group") / console.groupEnd()
Group
console.assert(condition, "error")
Assert
console.dir(element)
Print a DOM object
copy(document.title)
Copy to clipboard
$0
Currently selected DOM element
$("selector")
Query selector (like querySelector)
$$("selector")
Query all matching elements
monitorEvents(element, "click")
Monitor events

Elements Debugging 9

Edit as HTML
Edit element HTML
Toggle Edit as HTML
Toggle edit-as-HTML mode
Force Element State (:hover, :active)
Force element state (:hover, :active)
Break on: subtree modifications
Break on: subtree modifications
Break on: attribute modifications
Break on: attribute modifications
Store as global variable
Store as global variable
Copy selector
Copy CSS selector
Copy XPath
Copy XPath
Delete node
Delete node

Network Analysis 10

Disable cache
Disable cache
Throttling: Slow 3G
Network throttling
Filter: Doc, JS, CSS, Img, XHR
Filter by type
Filter: -domain:example.com
Exclude a domain
Filter: status-code:404
Filter by status code
Filter: is:from-cache
Cache requests
Replay XHR
Replay XHR
Copy as cURL
Copy as cURL
Save as...
Save as...
Block request URL
Block request URL

Performance 7

Record
Record performance data
Screenshots
Screenshots
Memory
Memory analysis
Main thread
Main thread analysis
Flame Chart
Flame Chart
Bottom-Up / Call Tree
Bottom-Up / Call Tree
Summary
Summary

Application Storage 8

Local Storage
Local Storage
Session Storage
Session Storage
Cookies
Cookie management
IndexedDB
IndexedDB viewer
Cache Storage
Service Worker cache
Clear site data
Clear site data
Service Workers
Service Worker management
Manifest
PWA Manifest

Parameter matrix

ParameterEffectExample
F12 / Cmd+Option+IOpen DevTools (macOS)F12
F12 / Ctrl+Shift+IOpen DevTools (Win/Linux)Ctrl + Shift + I
Cmd+Option+JOpen Console (macOS)Cmd + Option + J
Cmd+Option+EOpen Elements (macOS)Cmd + Option + E
Cmd+Shift+CElement selection modeCmd + Shift + C
Cmd+Shift+POpen the command menuCmd + Shift + P
Cmd+PQuickly open a file (Sources panel)Cmd + P
Cmd+Shift+MToggle device emulationCmd + Shift + M
console.log("message")Print a log messageconsole.log("message")
console.table(array)Print an array/object as a tableconsole.table(rows)
console.time("label") / console.timeEnd("label")Time an operation and print the elapsed timeconsole.time("render"); console.timeEnd("render");
console.count("label")Count how many times the same log has appearedconsole.count("render")
console.group("group") / console.groupEnd()Group log outputconsole.group("init"); console.groupEnd();
console.assert(condition, "error")Print an assertion message when the condition is falseconsole.assert(el, "error")
console.dir(element)Expand and print a DOM objectconsole.dir($0)
copy(document.title)Copy the result to the clipboardcopy(document.title)
$0Reference the currently selected DOM element$0.style.color = "red";
$("selector")Query a single element (similar to querySelector)$(".card")
$$("selector")Query all matching elements$$("li")
monitorEvents(element, "click")Monitor event triggers on the target elementmonitorEvents($0, "click")
Edit as HTMLEdit the element HTML directlyRight-click in Elements → Edit as HTML
Force Element State (:hover, :active)Force element state to debug interactive stylesRight-click node → Force Element State → :hover
Break on: subtree modificationsBreak when the DOM subtree changesRight-click node → Break on → subtree modifications
Break on: attribute modificationsBreak when an attribute changesRight-click node → Break on → attribute modifications
Store as global variableStore the node as a global variable for use in the ConsoleRight-click node → Store as global variable
Copy selector / Copy XPathCopy the selector or the XPath pathRight-click node → Copy selector
Delete nodeDelete that DOM nodeRight-click node → Delete node
Disable cacheDisable caching to ensure fresh resources every timeCheck Disable cache in the Network panel
Throttling: Slow 3GThrottle the network to simulate a slow connectionSelect Slow 3G under Throttling
Filter: Doc, JS, CSS, Img, XHRFilter requests by resource typeType XHR in Filter
Filter: -domain:example.comExclude requests by domainType -domain:example.com in Filter
Filter: status-code:404Filter by status codeType status-code:404 in Filter
Filter: is:from-cacheShow only requests that hit the cacheType is:from-cache in Filter
Replay XHRReplay the selected requestRight-click request → Replay XHR
Copy as cURLCopy as a cURL command to replay in the terminalRight-click request → Copy as cURL
Save as...Save the response body to diskRight-click request → Save as...
Block request URLBlock requests matching a URLRight-click request → Block request URL
RecordRecord performance dataPerformance panel → Record
ScreenshotsCapture frame-by-frame screenshots during recordingPerformance panel → Screenshots
MemoryMemory analysis viewPerformance panel → Memory
Main threadMain-thread tasks and timing analysisLook at the Main thread track
Flame ChartUse the flame chart to locate time-consuming functionsLook at the Flame Chart
Bottom-Up / Call TreeBottom-Up / Call Tree perspectiveLook at the Call Tree
SummaryTime distribution summaryLook at the Summary
Local Storage / Session StorageView local/session storageApplication → Local Storage
CookiesManage cookiesApplication → Cookies
IndexedDBView IndexedDB dataApplication → IndexedDB
Cache StorageView the Service Worker cacheApplication → Cache Storage
Clear site dataClear all local data for this siteApplication → Clear site data
Service WorkersService Worker registration and statusApplication → Service Workers
ManifestView the PWA ManifestApplication → Manifest

Common pitfalls

SymptomAfter changing code and refreshing, the page still behaves the old way.

CauseThe resources were served from the browser cache, so the locally changed JS/CSS was not re-fetched.

FixOpen DevTools and check Disable cache in the Network panel, or verify again after a hard refresh with Ctrl+Shift+R.

SymptomStyles that only appear on hover are not visible in Elements, and the element cannot be picked either.

CauseThe hover state only takes effect when a real pointer hovers over it; a static DOM has no hover target to act on.

FixRight-click the node and use Force Element State to force :hover; or press Cmd+Shift+C to enter element selection mode and select it directly in Elements.

SymptomA selector in the Console returns null, even though the element clearly exists on the page.

CauseThe element is rendered asynchronously by a framework or hidden in shadow DOM, or the selector syntax is wrong; it may also be a different node with the same name.

FixFirst use $0 to point at the selected element and confirm its structure, then switch to $$("selector") to get the collection and check its length instead of guessing the selector.

SymptomThe Console reports that a variable is undefined, even though it was clearly defined before.

CauseThe context is recreated after a page refresh or navigation, so variables saved earlier with Store as global variable are invalidated along with the document.

FixAfter refreshing, save it again with Store as global variable, or re-reference the selected element directly with $0 in the Console.

SymptomThe API works on the page, but the Network panel shows a bunch of 404s or abnormal statuses.

CauseFilter conditions or cache state affected your observation, and the requests may actually come from a Service Worker rather than the network.

FixUse status-code:404 to filter and locate all failed requests, check Disable cache and replay; then go to Application → Service Workers to confirm whether a cache layer is intercepting them.

SymptomA large portion of the Performance recording is marked unknown, or the recording is very short.

CauseOther tabs were also active during the recording, or the recording window did not cover the operation that actually janks.

FixBefore recording, keep only the current tab visible, start recording from the step that triggers the problem, stop immediately when done, and locate the time-consuming function on the main thread in the Flame Chart.

SymptomAfter an element is removed by a script, the Styles panel still shows the styles it once had, leading you to think they are working.

CauseThe panel keeps the last state of the deleted node, but that node is no longer in the document, so the styles do not apply to any real element.

FixIn Elements, confirm whether the node still exists (if needed, Delete node and see whether it gets reinserted); for a removed node, its styles no longer represent the live behavior.

SymptomAfter editing Local Storage or Cookies, the page behavior does not change, as if nothing was saved.

CauseThe storage panel shows a snapshot of the current document state; writing must be triggered through the page code or manual editing, and some sites also validate same-origin and server-side state.

FixManually change the value, press Enter to write it back and refresh the page to confirm; if it still fails, run a write and then a read in the Console to tell whether the write failed or the logic simply does not read that key.

Troubleshooting

  1. 1Quickly find out "who really keeps changing the styles"

    Cmd+Shift+C to select the element → right-click → Break on → attribute modifications → re-trigger

    The breakpoint pauses when a script modifies that element's attributes, exposing the call stack of the change source directly. If the style comes from a CSS override, check the Styles panel for duplicate declarations with strikethrough or source links and disable them one by one to verify.

  2. 2Verify style hypotheses directly in the Console

    $0.style.color = "red"; $$(".card").length; copy($0.outerHTML)

    $0 points at the currently selected element, so you can change styles and verify right away; $$ returns a collection where you can see the count; copy puts outerHTML on the clipboard for easy pasting into a search box or notes for comparison.

  3. 3A minimal loop for performance timing

    console.time("render"); /* suspicious operation */ console.timeEnd("render");

    time/timeEnd prints the millisecond duration between matching labels. If the result is large, use console.count to count how many times that block ran—repeated triggering is often the real source of the performance problem.

  4. 4Find failed requests and reproduce them in the terminal

    Type status-code:404 in Filter → right-click request → Copy as cURL → run in the terminal

    The filter narrows down all failed requests first; the full command copied by Copy as cURL includes the method, headers, and credentials, so you can replay it directly in the terminal or turn it into a regression script—more reliable than assembling parameters by hand.

  5. 5Reproduce loading problems on a slow network

    Check Disable cache → select Slow 3G under Throttling → refresh the page

    Throttling combined with disabling the cache reliably reproduces "fine locally, timing out in production" issues. By watching which requests stay pending in the waterfall, you can tell whether the slowness is on the critical first-screen path or in some lazily loaded API.

  6. 6Record and analyze a real jank

    Performance → Record → trigger the action → Stop → inspect Main thread / Flame Chart / Call Tree

    Do not switch tabs while recording to avoid noise. In the Flame Chart, find the widest block to identify the time-consuming function; the Call Tree confirms the call path from top to bottom; the Summary gives the overall time distribution. Reading the three views together quickly pinpoints the bottleneck.

  7. 7Troubleshoot leftover local state and cache

    Application → Local Storage / Cookies / Cache Storage / Service Workers → Clear site data → refresh

    First check each of the four storage locations for stale data, then clear all site data at once and refresh. If the behavior returns to normal after clearing, the problem lies in local state or the Service Worker cache rather than the code itself.

Command Examples

Grab a direct reference to the selected element

Cmd+Shift+C $0 $$("table tbody tr") console.table($$("table tbody tr").map(r => r.cells[0].textContent))

$0 is whichever node is currently highlighted in the Elements panel — use it as your entry point in the console. $$ returns an array, and pairing it with console.table flattens table data at once instead of expanding nodes one by one.

Copy as cURL to reproduce a request

Network panel → right-click request → Copy as cURL

When a request fails intermittently, turn it into a command line: replay it outside the page, and drop one header at a time to find the culprit. Strip Cookie and Authorization before pasting anywhere.

Filter by status code to isolate failures

Filter: status-code:404 Filter: -domain:example.com

A slow page is usually the sum of dozens of requests. Filtering by status code shows what is genuinely failing, and excluding third-party domains strips analytics and font noise so you see only your own traffic.

Time a block of code to find the synchronous block

console.time("parse"); // code under test console.timeEnd("parse");

Console timing is lighter than the Performance panel and suits bisecting: wrap chunks of a long routine and time each one — the offender is whichever blows past the others. Use the Performance panel only when you need flame charts and call trees.

Output

parse: 312.7ms

Catch third-party DOM mutations with a subtree breakpoint

Break on: subtree modifications

When a node vanishes for no visible reason, the Elements panel will not tell you who removed it. A subtree breakpoint pauses the moment a script mutates the structure, and the call stack names the script.

Common Pitfalls

  • Leaving "Disable cache" checked makes you debug stale assets and chase a bug that no longer exists in your code.
  • "Copy as cURL" embeds Cookie, Authorization, and CSRF tokens — pasting that into an issue, a chat, or a public repo hands over a live session.
  • A throttling preset can outlive the DevTools window in some versions, so every later test runs slow for reasons unrelated to the code.
  • "Edit as HTML" changes are temporary and vanish on reload. Never treat them as a fix — the real change belongs in the source file.
  • "Clear site data" wipes local and session storage, cookies, and caches. Running it mid-investigation destroys the login state and the localStorage fixtures you were reproducing against.
  • Force Element State (:hover/:active) is a visual simulation that fires no real JS events, so it cannot tell you whether click logic actually works.

Tips

  • $0 in Console is the currently selected DOM element, directly operable.
  • Right-click in the Network panel and Copy as cURL to replay a request in the terminal.
  • When recording in the Performance panel, avoid other tabs open, which hurts accuracy.

FAQ

What are $0, $(...) and $$(...) in the Console, and how do they pair with Elements?

Once you select a node in the Elements panel, $0 refers to that element and you can operate on it directly: $0.style.color = "red" or $0.remove(). $("selector") is querySelector and returns one match; $$("selector") is querySelectorAll and returns a NodeList you can then .forEach over. To get a complicated selected element out of the panel, use Store as global variable to keep it as a global, or console.dir(element) to print the DOM object's properties — console.log alone only gives you an "[object HTMLDivElement]" summary.

How do I use Console timing to find out which part of a piece of code is slow?

Pair console.time("label") with console.timeEnd("label") using the same label; repeating a start warns and a missing end prints nothing. Wrap each suspicious stage — console.time("parse") around parsing, console.time("render") around rendering — and compare the two numbers in the Console, then subdivide whichever is larger. Alongside it, console.count("label") counts invocations and exposes redundant calls inside a loop, console.group / console.groupEnd collapse noisy output, and console.assert(condition, "error") checks an invariant.

How do I filter the Network panel down to the requests that actually misbehave?

The filter box combines conditions: narrow by type — Doc, JS, CSS, Img, XHR — to drop static-asset noise; -domain:example.com excludes a domain; status-code:404 pins every 404; is:from-cache surfaces cache hits. Pair it with Disable cache and reload to check whether caching is masking the problem, Throttling: Slow 3G to reproduce weak-network behaviour, and Block request URL to deliberately break a request and verify your fallback. Once you have the request, right-click Copy as cURL and replay it in the terminal.

How do I set things up to reproduce a bug that only shows on mobile?

Cmd + Shift + M toggles device emulation; once a device is selected you can edit the HTML directly in Elements, and Force Element State pins :hover / :active so interactive styles become inspectable. Cmd+Shift+C puts you in the element picker for clicking straight on the real page. When the bug involves storage, move to the Application panel to inspect Local Storage, Session Storage, Cookies, IndexedDB and Cache Storage, and use Clear site data to wipe state before reproducing so leftovers do not confuse you; Service Workers and Manifest panels cover PWA issues.

How do I read the flame chart produced by the Performance panel?

Hit Record to capture (and avoid touching other tabs while recording, or you pollute the data), then use Screenshots to grab the frame where the jank is visible. Bottom-Up sorts by Self Time, telling you directly which functions burn the most CPU; Call Tree expands the stack so you can find who called the slow code. Summary shows how time is distributed across phases, and the Memory panel alongside it reveals whether the JS heap keeps growing during the capture. The workflow is: locate the longest slice in Summary, then identify the culprit in Bottom-Up.

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.

Maintained by LaoHand

Publicly updated on Jul 21, 2026, continuously proofread against official docs.

Contact Us

Wrong command or description? Send us corrections, business inquiries or product feedback by email.

Contact Us