← Back to dashboard
chrome-devtoolstoolingfresh

Chrome DevTools Developer Training & Resource Guide

What is Chrome DevTools?

The real model

Manual panels + CDP + the chrome-devtools MCP — verify in a real browser, gate on CWV.

Three faces: the panels (F12) for hands-on debugging; CDP for automation (`Network.emulateNetworkConditions`, `Emulation.setCPUThrottlingRate`); and `chrome-devtools-mcp` (Google, Apache-2.0) so Claude Code drives a real Chrome — navigate, screenshot, read console/network, run `lighthouse_audit`, capture traces. The scoreboard is Core Web Vitals at the 75th percentile: LCP ≤2.5s, INP ≤200ms (replaced FID in 2024), CLS ≤0.1. For codeAmani, make 3G + Disable-cache the default test condition and gate PRs on LCP/CLS via Lighthouse CI — a page fast on fibre but failing at 3G fails our users.

Six core surfaces

The panels you live in plus the agent surface that drives them. Tap a card for the shortcuts + gotchas.

Will it survive 3G? — Core Web Vitals playground

Pick a connection, then drag page weight, server time, and render-blocking resources. Watch TTFB / FCP / LCP shift against the official budgets and the request waterfall stretch. This is exactly the test codeAmani runs before shipping: if it’s slow on a Nairobi 3G link, it’s slow for our users.

Connection (representative)
TTFB0.85s≤ 0.8s
FCP1.90s≤ 1.8s
LCP37.9s≤ 2.5s
Request waterfallLCP 37.9s
Connect (TLS)
TTFB (server)
Render-blocking
Render-blocking
Render-blocking
Parse / render
Content transfer (LCP)

The green content transfer bar is what page weight inflates. On a slow link, a heavy bundle pushes LCP past the 2.5s budget — that's why splitting + lazy-loading matters.

Core Web Vitals (75th pct): LCP ≤ 2.5s INP ≤ 200ms CLS ≤ 0.1(INP & CLS come from interactions/layout, not bandwidth)

Thresholds are exact/official (web.dev/articles/vitals). Connection bandwidth/RTT are representative— DevTools intentionally doesn’t publish exact preset values; the load model here is illustrative. Measure real numbers in the Performance panel, Lighthouse, or the web-vitals library.

Text
 ██████╗██╗  ██╗██████╗  ██████╗ ███╗   ███╗███████╗
██╔════╝██║  ██║██╔══██╗██╔═══██╗████╗ ████║██╔════╝
██║     ███████║██████╔╝██║   ██║██╔████╔██║█████╗
██║     ██╔══██║██╔══██╗██║   ██║██║╚██╔╝██║██╔══╝
╚██████╗██║  ██║██║  ██║╚██████╔╝██║ ╚═╝ ██║███████╗
 ╚═════╝╚═╝  ╚═╝╚═╝  ╚═╝ ╚═════╝ ╚═╝     ╚═╝╚══════╝

██████╗ ███████╗██╗   ██╗████████╗ ██████╗  ██████╗ ██╗     ███████╗
██╔══██╗██╔════╝██║   ██║╚══██╔══╝██╔═══██╗██╔═══██╗██║     ██╔════╝
██║  ██║█████╗  ██║   ██║   ██║   ██║   ██║██║   ██║██║     ███████╗
██║  ██║██╔══╝  ╚██╗ ██╔╝   ██║   ██║   ██║██║   ██║██║     ╚════██║
██████╔╝███████╗ ╚████╔╝    ██║   ╚██████╔╝╚██████╔╝███████╗███████║
╚═════╝ ╚══════╝  ╚═══╝     ╚═╝    ╚═════╝  ╚═════╝ ╚══════╝╚══════╝

Chrome DevTools Developer Training & Resource Guide

Focus: Everything a developer can do with Chrome DevTools — every panel, the Chrome DevTools Protocol (CDP), the chrome-devtools-mcp server for Claude Code, Core Web Vitals budgets, and end-to-end testing with Playwright / Puppeteer / Lighthouse CI. Grounded in developer.chrome.com, web.dev, and the official MCP repo; reviewed 2026-06-07.


How to use this guide

DevTools has three faces — learn them in order:

  1. The panels (manual) → Part 1. What you open with F12 / Cmd+Opt+I to inspect, debug, and profile by hand.
  2. The protocol (scriptable) → CDP. Everything the UI does, exposed as a websocket API that Playwright, Puppeteer, Lighthouse, and the MCP all build on.
  3. The agent surface (Claude Code) → the MCP. chrome-devtools-mcp lets Claude drive a real browser to verify your work.

The interactive learn module above this page is a live Core Web Vitals + network-throttle playground — start there to build intuition, then use the panel reference below.


Overview

Chrome DevTools is the browser's built-in inspection, debugging, and profiling suite. Under the UI sits the Chrome DevTools Protocol (CDP) — a JSON-over-websocket API that exposes the same capabilities to automation. chrome-devtools-mcp (by Google, Apache-2.0) wraps a Puppeteer-driven Chrome as an MCP server so a coding agent can navigate, click, screenshot, read the console/network, run Lighthouse, and capture performance traces — the practical way to test in a browser before claiming done.

The three faces of DevTools, and how they connect:

Official Documentation


Part 1 — The DevTools panels

Open DevTools: F12, Cmd+Opt+I (macOS), or Ctrl+Shift+I (Win/Linux). The Command Menu (Cmd/Ctrl+Shift+P) jumps to any panel or action by name — the single most useful shortcut.

PanelWhat it's forReach for it when…
ElementsInspect & edit the live DOM + CSS (the Styles pane)Tweaking layout, debugging CSS specificity, checking computed styles
ConsoleLog output + a live JS REPL against the pageReading errors, poking at window/page state, quick scripting
SourcesView files, set breakpoints, step through JS, edit + save (Workspaces)Debugging logic, watching variables, blackboxing scripts
NetworkEvery request/response, timing, headers, throttlingChecking payload size, caching, CORS, slow-connection behaviour
PerformanceRecord a trace of main-thread work; measures Core Web VitalsDiagnosing jank, long tasks, layout thrash, slow LCP
MemoryHeap snapshots + allocation timelinesHunting memory leaks, detached DOM nodes
ApplicationStorage (localStorage, IndexedDB, cookies), Service Workers, Cache, manifestDebugging PWAs, auth tokens, offline behaviour
LighthouseOne-click audit: Performance, Accessibility, Best Practices, SEO, PWAA scored baseline + prioritised fixes
RecorderRecord, replay & measure a user flow; export to Puppeteer/PlaywrightTurning a manual flow into a repeatable test
SecurityCertificate + mixed-content checksVerifying a page is fully HTTPS
Rendering (drawer)Paint flashing, FPS meter, emulate prefers-color-scheme, CSS mediaSpotting unnecessary repaints, testing dark mode
Coverage (drawer)Unused JS/CSS per fileTrimming dead code from the bundle
Issues / Changes / Animations / Sensors / WebAuthnAggregated warnings · CSS diff · animation timeline · geolocation/orientation emulation · authenticator emulationTargeted debugging of each concern

Open drawer tools with Esc → the menu → More tools, or via the Command Menu.

Elements + Styles — DOM & CSS

Text
Inspect    Cmd/Ctrl+Shift+C, then click an element
Edit DOM   double-click a node / press F2
Force state :hov → toggle :hover/:focus/:active to debug states
Color      click a swatch in Styles for the eyedropper + contrast ratio

The contrast ratio readout in the color picker is your fastest accessibility check — it flags AA/AAA pass/fail inline.

Console — REPL + logging

JavaScript
// $0 is the currently-selected Elements node; $$ is querySelectorAll
$0.getBoundingClientRect();
$$('img:not([alt])');                 // find images missing alt text
monitorEvents($0, 'click');           // log events on an element
copy(JSON.stringify(window.__DATA__)); // copy a value to clipboard
console.table(performance.getEntriesByType('resource')); // request table

Sources — the debugger

Text
Breakpoint        click a line number
Conditional bp    right-click a line → "Add conditional breakpoint"
Logpoint          inject a console.log without editing source
DOM breakpoint    Elements → right-click → "Break on" → subtree/attribute change
Step              F10 over · F11 into · Shift+F11 out · F8 resume

Network — payload & throttling

Text
Throttling presets (Chrome 127+):  Offline · 3G · Slow 4G · Fast 4G · Custom
  (formerly "Slow 3G"/"Fast 3G"; DevTools no longer prints exact kbps —
   the presets are tuned to match real-world conditions.)
Disable cache     check the box (while DevTools is open) to test cold loads
Filter            `larger-than:500k`, `-domain:*.google.com`, `mixed-content:all`
Copy as cURL      right-click a request → reproduce it from the terminal

codeAmani habit: test every page under 3G (the old "Slow 3G") with Disable cache on. If LCP blows past 2.5s, the bundle is too heavy for the median Kenyan connection — lazy-load and split before shipping.

Performance — traces & Core Web Vitals

Text
Record       click ● (or Cmd/Ctrl+E) → interact → stop
Reload trace click ⟳ to capture a full page load
Read it      Main track = JS/layout/paint; red-cornered bars = long tasks (>50ms)
CWV          LCP/CLS/INP markers overlay the timeline

Application — storage & PWAs

Text
Service Workers   update-on-reload, bypass-for-network, push test
Storage           localStorage / sessionStorage / IndexedDB / Cookies (edit inline)
Clear storage     one button to reset to a first-visit state
Manifest          install + icon/colour validation for PWAs

Part 2 — Core Web Vitals & performance budgets

The scoreboard for real-user performance. Thresholds are measured at the 75th percentile of page loads, segmented by mobile/desktop (source: web.dev/vitals).

MetricMeasuresGoodNeeds improvementPoor
LCP — Largest Contentful PaintLoading≤ 2.5 s2.5 – 4.0 s> 4.0 s
INP — Interaction to Next PaintInteractivity≤ 200 ms200 – 500 ms> 500 ms
CLS — Cumulative Layout ShiftVisual stability≤ 0.10.1 – 0.25> 0.25

INP replaced FID as a Core Web Vital in 2024 — it measures the latency of all interactions, not just the first.

Supporting diagnostics:

MetricGood thresholdHelps explain
FCP — First Contentful Paint≤ 1.8 sLCP
TTFB — Time to First Byte≤ 0.8 sFCP / LCP

Measure vitals in code

Bash
npm install web-vitals
TypeScript
import { onLCP, onINP, onCLS } from "web-vitals";

// Field data — report from real users to your analytics endpoint
onLCP((m) => navigator.sendBeacon("/vitals", JSON.stringify(m)));
onINP((m) => navigator.sendBeacon("/vitals", JSON.stringify(m)));
onCLS((m) => navigator.sendBeacon("/vitals", JSON.stringify(m)));

Lab vs field: DevTools / Lighthouse give you lab data (one synthetic run). web-vitals + analytics give you field data (real users, the 75th-percentile that actually counts). Optimise in the lab; verify in the field.


Part 3 — Chrome DevTools Protocol (CDP)

Everything the UI does is a CDP command. Playwright, Puppeteer, Lighthouse, and the MCP all speak it. Start Chrome with a debugging port and you can drive it from anything:

Bash
# Launch Chrome with the protocol exposed
google-chrome --remote-debugging-port=9222 --headless=new

# List targets (tabs) — each has a websocket debugger URL
curl http://localhost:9222/json
TypeScript
// Raw CDP via Puppeteer's session — e.g. emulate a slow network
const client = await page.createCDPSession();
await client.send("Network.emulateNetworkConditions", {
  offline: false, downloadThroughput: (1.6 * 1024 * 1024) / 8,
  uploadThroughput: (750 * 1024) / 8, latency: 150,
});
await client.send("Emulation.setCPUThrottlingRate", { rate: 4 }); // 4× slowdown

Domains you'll use most: Page, Network, Runtime, Performance, Emulation, DOM, Accessibility. Full reference: chromedevtools.github.io/devtools-protocol.


Part 4 — chrome-devtools MCP for Claude Code

The official chrome-devtools-mcp server (Google LLC, Apache-2.0) wraps a Puppeteer-controlled Chrome as MCP tools, so Claude Code can drive a real browser.

Setup

Bash
# Add via the Claude Code CLI
claude mcp add chrome-devtools -- npx -y chrome-devtools-mcp@latest
JSON
// .mcp.json
{
  "mcpServers": {
    "chrome-devtools": {
      "command": "npx",
      "args": ["-y", "chrome-devtools-mcp@latest"],
      "env": {
        "CHROME_PATH": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
      }
    }
  }
}

Available MCP tools

Grouped by what they do (names as exposed by the server):

GroupTools
Navigation / pagesnew_page · navigate_page · list_pages · select_page · close_page · wait_for
Interactionclick · hover · drag · fill · fill_form · type_text · press_key · upload_file · handle_dialog
Inspect / capturetake_snapshot (accessibility tree) · take_screenshot · evaluate_script
Consolelist_console_messages · get_console_message
Networklist_network_requests · get_network_request
Performanceperformance_start_trace · performance_stop_trace · performance_analyze_insight · lighthouse_audit
Emulation / memoryemulate (device/network/CPU) · resize_page · take_heapsnapshot

take_snapshot returns the accessibility tree — both your a11y check and the most reliable way for the agent to locate elements (by role/name) instead of brittle CSS selectors.


Part 5 — Claude Code commands, hooks & testing workflows

Slash command: visual + Lighthouse review

.claude/commands/screenshot.md:

Markdown
Open $ARGUMENTS in a browser and review it.

1. `new_page` then `navigate_page` to $ARGUMENTS
2. `resize_page` to 1280×900, then `take_screenshot`
3. `emulate` a "Slow 4G" network and reload; `take_screenshot` again
4. `lighthouse_audit` for performance + accessibility scores
5. `list_console_messages` for errors
Report: layout/contrast issues, the two screenshots' differences, Lighthouse
scores vs the budgets (LCP ≤ 2.5s, CLS ≤ 0.1), and console errors with fixes.

Usage: /project:screenshot http://localhost:3000/dashboard

Hook: nudge a browser check after a UI change

.claude/settings.json:

JSON
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command",
            "command": "if echo \"$CLAUDE_TOOL_INPUT\" | grep -qE '\\.(tsx|jsx|css)$'; then echo 'UI file changed — verify with the chrome-devtools MCP before claiming done'; fi" }
        ]
      }
    ]
  }
}

Playwright — E2E tests

Bash
npm init playwright@latest
npx playwright test --ui        # interactive runner
npx playwright codegen <url>    # record a test by clicking
npx playwright show-trace trace.zip
TypeScript
// tests/auth.spec.ts
import { test, expect } from "@playwright/test";

test("user can sign in", async ({ page }) => {
  await page.goto("/login");
  await page.getByTestId("email").fill("test@example.com");
  await page.getByTestId("password").fill("secret123");
  await page.getByTestId("submit").click();
  await expect(page).toHaveURL("/dashboard");
  await expect(page.getByRole("heading", { level: 1 })).toContainText("Welcome");
});

test("renders within a 3G budget", async ({ page }) => {
  const client = await page.context().newCDPSession(page);
  await client.send("Network.emulateNetworkConditions", {
    offline: false, latency: 300,
    downloadThroughput: (400 * 1024) / 8, uploadThroughput: (400 * 1024) / 8,
  });
  const t0 = Date.now();
  await page.goto("/", { waitUntil: "load" });
  expect(Date.now() - t0).toBeLessThan(8000); // hard ceiling on a slow link
});

Puppeteer — scripted browser

TypeScript
import puppeteer from "puppeteer";

const browser = await puppeteer.launch({ headless: "new" });
const page = await browser.newPage();
await page.emulateNetworkConditions(puppeteer.networkConditions["Slow 3G"]);
await page.goto("https://example.com", { waitUntil: "networkidle0" });
await page.screenshot({ path: "shot.png", fullPage: true });
await browser.close();

Lighthouse CI — budgets in CI

YAML
# .github/workflows/lighthouse.yml
name: Lighthouse
on: [pull_request]
jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '22' }
      - run: npm ci && npm run build
      - run: npm install -g @lhci/cli && lhci autorun
        env: { LHCI_GITHUB_APP_TOKEN: "${{ secrets.GITHUB_TOKEN }}" }
JavaScript
// lighthouserc.js — fail the PR if budgets regress
module.exports = {
  ci: {
    collect: { startServerCommand: "npm run preview", url: ["http://localhost:4173/"], numberOfRuns: 3 },
    assert: { assertions: {
      "categories:performance": ["warn", { minScore: 0.8 }],
      "categories:accessibility": ["error", { minScore: 0.9 }],
      "largest-contentful-paint": ["error", { maxNumericValue: 2500 }],
      "cumulative-layout-shift": ["error", { maxNumericValue: 0.1 }],
    } },
    upload: { target: "temporary-public-storage" },
  },
};

Common use cases

GoalFastest path
Verify a UI changechrome-devtools MCP navigate_page + take_screenshot
Accessibility audittake_snapshot (a11y tree) or lighthouse_audit
Check the slow-connection budgetNetwork panel 3G + Disable cache, or emulate in the MCP
Find a memory leakMemory panel heap snapshots over time / take_heapsnapshot
Diagnose jankPerformance trace → look for long tasks (>50ms)
Reproduce an API callNetwork → right-click → Copy as cURL
Record a testRecorder panel → export to Playwright, or playwright codegen
Trim the bundleCoverage drawer → delete unused JS/CSS

Troubleshooting

IssueFix
Chrome not found (MCP)Set CHROME_PATH to your Chrome/Chromium binary
Screenshot blankWait for load: MCP wait_for, or Playwright waitForLoadState("networkidle")
CDP connection refusedLaunch Chrome with --remote-debugging-port=9222
Playwright browser missingnpx playwright install chromium
Lighthouse times outRaise --max-wait-for-load, or reduce CPU throttling
MCP server won't connectEnsure Chrome can launch headlessly; check npx -y chrome-devtools-mcp@latest runs
Throttling preset looks differentChrome 127 renamed them: "Slow 3G"→"3G", "Fast 3G"→"Slow 4G", + "Fast 4G"
INP shows "—"INP needs a real interaction; click/scroll, then re-measure

codeAmani notes

  • Test on the network our users have. The African-market mandate is 2G/3G-first. Make 3G + Disable cache the default test condition, and gate PRs on LCP ≤ 2.5s and CLS ≤ 0.1 via Lighthouse CI. A page that's fast on office fibre but fails at 3G fails our users.
  • Verify, don't assume. Use the chrome-devtools MCP to actually load a page after a UI change — screenshot it, check the console, run a Lighthouse pass — before claiming done. This is the browser half of the "evidence before assertions" rule.
  • Mobile-first emulation. Device Mode + emulate lets you check the median Android viewport and a slow CPU (Emulation.setCPUThrottlingRate) — closer to a real low-end handset than your laptop.
  • M-Pesa flows. Drive the STK-Push UI end-to-end in a real browser (Recorder → Playwright), and use the Network panel to confirm the callback round-trip and that no secrets (Daraja keys, tokens) leak into client requests or the console.
  • Secrets stay server-side. Never paste credentials into the Console on a production page, and scrub evaluate_script snippets of any secret before saving them to a command or repo. DevTools output can end up in screenshots and logs.