Chrome DevTools Developer Training & Resource Guide

Technology: chrome-devtools · Category: tooling · Last reviewed: 2026-08-23

Source: https://tech-stack.codeamanilabs.org/guide/chrome-devtools

Insight:

Chrome DevTools is three things at once: a manual UI (the panels you open with F12), a protocol (CDP — everything the UI does, scriptable), and an agent surface (the chrome-devtools-mcp server, which lets Claude Code drive a real browser). The throughline is verify in a real browser instead of trusting the diff. Core Web Vitals are the scoreboard — LCP ≤ 2.5s, INP ≤ 200ms, CLS ≤ 0.1 at the 75th percentile — and the Network panel's throttling is how you prove a page survives a Nairobi 3G connection before you ship it.

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

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

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-08-23.


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:

flowchart TB
  DT["Chrome DevTools"] --> UI["Panels (F12)<br/>Elements · Console · Network · Performance"]
  DT --> CDP["Chrome DevTools Protocol"]
  CDP --> AUTO["Playwright · Puppeteer · Lighthouse"]
  CDP --> MCP["chrome-devtools MCP<br/>drives Claude Code"]

Official Documentation

Resource URL
Chrome DevTools docs https://developer.chrome.com/docs/devtools
Chrome DevTools Protocol (CDP) https://chromedevtools.github.io/devtools-protocol/
chrome-devtools-mcp (official) https://github.com/ChromeDevTools/chrome-devtools-mcp
Web Vitals https://web.dev/articles/vitals
Playwright https://playwright.dev/docs/intro
Puppeteer https://pptr.dev
Lighthouse CI https://github.com/GoogleChrome/lighthouse-ci

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.

Panel What it's for Reach for it when…
Elements Inspect & edit the live DOM + CSS (the Styles pane) Tweaking layout, debugging CSS specificity, checking computed styles
Console Log output + a live JS REPL against the page Reading errors, poking at window/page state, quick scripting
Sources View files, set breakpoints, step through JS, edit + save (Workspaces) Debugging logic, watching variables, blackboxing scripts
Network Every request/response, timing, headers, throttling Checking payload size, caching, CORS, slow-connection behaviour
Performance Record a trace of main-thread work; measures Core Web Vitals Diagnosing jank, long tasks, layout thrash, slow LCP
Memory Heap snapshots + allocation timelines Hunting memory leaks, detached DOM nodes
Application Storage (localStorage, IndexedDB, cookies), Service Workers, Cache, manifest Debugging PWAs, auth tokens, offline behaviour
Lighthouse One-click audit: Performance, Accessibility, Best Practices, SEO, PWA A scored baseline + prioritised fixes
Recorder Record, replay & measure a user flow; export to Puppeteer/Playwright Turning a manual flow into a repeatable test
Security Certificate + mixed-content checks Verifying a page is fully HTTPS
Rendering (drawer) Paint flashing, FPS meter, emulate prefers-color-scheme, CSS media Spotting unnecessary repaints, testing dark mode
Coverage (drawer) Unused JS/CSS per file Trimming dead code from the bundle
Issues / Changes / Animations / Sensors / WebAuthn Aggregated warnings · CSS diff · animation timeline · geolocation/orientation emulation · authenticator emulation Targeted debugging of each concern

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

Elements + Styles — DOM & CSS

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

// $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

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

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

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

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).

Metric Measures Good Needs improvement Poor
LCP — Largest Contentful Paint Loading ≤ 2.5 s 2.5 – 4.0 s > 4.0 s
INP — Interaction to Next Paint Interactivity ≤ 200 ms 200 – 500 ms > 500 ms
CLS — Cumulative Layout Shift Visual stability ≤ 0.1 0.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:

Metric Good threshold Helps explain
FCP — First Contentful Paint ≤ 1.8 s LCP
TTFB — Time to First Byte ≤ 0.8 s FCP / LCP

Measure vitals in code

npm install web-vitals
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:

# 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
// 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; 1.7.0 as of this review) wraps a Puppeteer-controlled Chrome as MCP tools, so Claude Code can drive a real browser.

Setup

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

Useful flags (append to args after chrome-devtools-mcp@latest): --headless (no visible window — for CI), --isolated (throwaway profile, auto-cleaned), --channel stable|beta|dev|canary, --executablePath <path> or --browserUrl http://127.0.0.1:9222 to attach to an already-running Chrome, and --slim for a reduced toolset when the full surface is more than a task needs.

Available MCP tools

1.7.0 exposes ~57 tools; the ~44 below load by default, grouped by what they do (names as exposed by the server):

Group Tools
Navigation / pages (6) new_page · navigate_page · list_pages · select_page · close_page · wait_for
Interaction (10) click · click_at · hover · drag · fill · fill_form · type_text · press_key · upload_file · handle_dialog
Inspect / capture (8) take_snapshot (accessibility tree) · take_screenshot · evaluate_script · list_console_messages · get_console_message · lighthouse_audit · screencast_start · screencast_stop
Network (2) list_network_requests · get_network_request
Performance (3) performance_start_trace · performance_stop_trace · performance_analyze_insight
Emulation (2) emulate (viewport · networkConditions · cpuThrottlingRate · geolocation · colorScheme · userAgent) · resize_page
Memory (13) take_heapsnapshot + the heap-snapshot analysis family (compare_heapsnapshots · get_heapsnapshot_dominators/retainers/summary/… · query_heapsnapshot_objects · close_heapsnapshot)

Four further categories are opt-in behind flags — Extensions (--categoryExtensions), PWA (--categoryPwa), plus experimental third-party and WebMCP tool bridges; screencast_* needs --experimentalScreencast=true.

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. Tool names and grouping change across releases — confirm the live list any time with /mcp in Claude Code.


Part 5 — Claude Code commands, hooks & testing workflows

Slash command: visual + Lighthouse review

.claude/commands/screenshot.md:

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:

{
  "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

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
// 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

import puppeteer from "puppeteer";

// headless: true IS the new headless mode now (Puppeteer 22+); the old
// headless: "new" string is deprecated — use `"shell"` for the legacy mode.
const browser = await puppeteer.launch({ headless: true });
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

# .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 }}" }
// 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

Goal Fastest path
Verify a UI change chrome-devtools MCP navigate_page + take_screenshot
Accessibility audit take_snapshot (a11y tree) or lighthouse_audit
Check the slow-connection budget Network panel 3G + Disable cache, or emulate in the MCP
Find a memory leak Memory panel heap snapshots over time / take_heapsnapshot
Diagnose jank Performance trace → look for long tasks (>50ms)
Reproduce an API call Network → right-click → Copy as cURL
Record a test Recorder panel → export to Playwright, or playwright codegen
Trim the bundle Coverage drawer → delete unused JS/CSS

Troubleshooting

Issue Fix
Chrome not found (MCP) Set CHROME_PATH to your Chrome/Chromium binary
Screenshot blank Wait for load: MCP wait_for, or Playwright waitForLoadState("networkidle")
CDP connection refused Launch Chrome with --remote-debugging-port=9222
Playwright browser missing npx playwright install chromium
Lighthouse times out Raise --max-wait-for-load, or reduce CPU throttling
MCP server won't connect Ensure Chrome can launch headlessly; check npx -y chrome-devtools-mcp@latest runs
Throttling preset looks different Chrome 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

Official docs: