--- title: 11 Coverage Tool --- # Chrome DevTools: Coverage Tool ## What the Coverage Tool Is The Coverage tool measures which bytes of your CSS and JavaScript files are actually **used** versus **unused** during a page session. It gives you a byte-level breakdown across every loaded resource, letting you see exactly how much dead code you are shipping to your users. Key insight: unused code still has a real cost. The browser must download it, parse it, and (for JS) compile it — even if it never runs. A large unused JS bundle delays Time to Interactive (TTI). Unused CSS blocks rendering. The Coverage tool tells you where to look first. What it measures: - **CSS**: rules that matched at least one element during the session - **JS**: functions and statements that were executed at least once during the session - Coverage is **per-session** — a byte is only "used" if it was touched while you were recording --- ## How to Open It Three ways to reach the Coverage panel: **Option 1 — More Tools menu** ``` DevTools (F12 or Cmd+Option+I) → ⋮ (three-dot menu, top-right) → More tools → Coverage ``` **Option 2 — Command Menu (fastest)** ``` Cmd+Shift+P (Mac) / Ctrl+Shift+P (Windows/Linux) Type: Show Coverage Press Enter ``` **Option 3 — Drawer shortcut** ``` Press Escape to open the Drawer at the bottom of DevTools Click the + icon in the Drawer tab bar → Coverage ``` The Coverage panel opens as a tab in the DevTools Drawer by default. --- ## UI Layout (ASCII Diagram) ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ COVERAGE [ ✕ close ] │ ├──────┬──────────┬──────────────────────────────────────────────────────┤ │ ● │ ↺ │ Filter by URL... │ │ record reload │ │ ├──────┴──────────┴──────────────────────────────────────────────────────┤ │ Summary bar: Total 2.3 MB │ Used 1.1 MB │ Unused 1.2 MB (52%) │ ├──────────────────────────────────────────────────────────────────────────┤ │ URL │ Type │ Total │ Unused │ Usage │ │ ─────────────────────────────────────────────────────────────────────── │ │ https://example.com/main.js │ JS │ 842 kB │ 421 kB │ ▓▓▒▒▒▒▒▒ │ │ https://example.com/vendor.js │ JS │ 1.1 MB │ 670 kB │ ▓▓▒▒▒▒▒▒ │ │ https://example.com/styles.css │ CSS │ 180 kB │ 154 kB │ ▓▒▒▒▒▒▒▒ │ │ https://example.com/icons.css │ CSS │ 92 kB │ 88 kB │ ▓▒▒▒▒▒▒▒ │ │ ... │ ... │ ... │ ... │ ... │ ├──────────────────────────────────────────────────────────────────────────┤ │ [ Export as JSON ] │ └──────────────────────────────────────────────────────────────────────────┘ Legend for Usage bar: ▓ green = used bytes ▒ red = unused bytes ``` Key controls in the toolbar: - **Circle (record)** — start/stop a coverage recording session - **Reload arrow** — reload the page and immediately begin recording (best for capturing initial load) - **Filter box** — filter rows by URL substring - **Export** — download the full coverage report as JSON --- ## Running Coverage ### Step-by-step workflow **1. Open Coverage and start recording** Click the filled circle button (or use the reload button to capture from the very first byte loaded on page start). The button turns red/active when recording. Tip: use the reload button rather than the record button if you want to capture JavaScript that runs during initial parse and module evaluation — code that executes before you can click "record" is otherwise missed. **2. Interact with the page** Navigate through every feature, route, and user flow you care about: - Click every button and toggle - Open every modal and drawer - Navigate to every route - Scroll to trigger lazy-loaded sections - Submit forms - Expand accordions The more thoroughly you interact, the more accurate the "used" count becomes. Coverage is only as good as your interaction coverage. **3. Stop recording** Click the record button again (now a square stop button) to end the session. **4. Review the results table** The panel now shows every loaded CSS and JS file with byte counts and a visual usage bar. Sort by "Unused Bytes" descending to find the biggest opportunities first. --- ## Reading the Coverage Report ### Table columns | Column | Description | |---|---| | **URL** | Full URL of the resource | | **Type** | JS or CSS | | **Total Bytes** | Uncompressed size of the file as delivered | | **Unused Bytes** | Bytes not touched during the recording session | | **Usage Visualization** | Inline bar — green on the left (used), red on the right (unused) | ### Color coding - **Green** segment: bytes that were parsed/executed during the session - **Red** segment: bytes that were downloaded but never touched A file that is 90% red has roughly 90% of its bytes doing nothing during this session. ### Clicking into a file Click any row in the Coverage table. DevTools opens the file in the **Sources panel** and annotates every line: ``` 1 | (green sidebar) | import React from 'react'; 2 | (green sidebar) | import { useState } from 'react'; 3 | | 4 | (red sidebar) | export function UnusedComponent() { 5 | (red sidebar) | return
Never rendered
; 6 | (red sidebar) | } 7 | | 8 | (green sidebar) | export function ActiveComponent() { 9 | (green sidebar) | const [n, setN] = useState(0); 10 | (green sidebar) | return ; 11 | (green sidebar) | } ``` Green lines ran. Red lines never ran. This is your roadmap for dead code removal. --- ## Interpreting Results ### What counts as "used" **For JavaScript:** - Any statement that was executed at the bytecode level - A function declaration counts as "used" only when it is **called**, not just defined - Module-level code (imports, top-level assignments) counts as executed on parse - Event handlers count as used when the event fires - Conditional branches that were never taken stay red **For CSS:** - Any rule that matched at least one DOM element during the session - A selector like `.modal-open .sidebar` is only "used" when `.modal-open` is actually on the DOM - Keyframe definitions count as used only when an animation using them fires - Media query blocks count as used only when the query matched ### What it does NOT mean "Unused" does not always mean "delete it": - A route you did not navigate to will show as unused, but it is needed - Error handling code that never triggered during your session is still critical - Polyfills for browsers you did not test in will appear unused - CSS for print media will be "unused" in a normal browsing session --- ## Typical Coverage Numbers (What Is Normal vs Alarming) | Scenario | Unused JS | Unused CSS | Assessment | |---|---|---|---| | Small app, few dependencies | 10–25% | 15–30% | Healthy | | Medium SPA, no code splitting | 40–60% | 50–70% | Worth investigating | | Large app with full vendor bundle | 60–80% | 60–85% | Alarming — definitely optimize | | Bootstrap/Tailwind without purging | n/a | 85–99% | Expected but fixable | | After aggressive optimization | <20% | <20% | Excellent | **Realistic baselines for a production React app (single bundle, no splitting):** - `vendor.js` (React, lodash, etc.): commonly 50–75% unused on any given page - `main.js` (app code): commonly 30–60% unused depending on how many routes exist - Framework CSS (Bootstrap, MUI): commonly 80–95% unused If your vendor bundle is over 70% unused, that is the first place to invest optimization effort. --- ## Strategies for Unused CSS ### 1. Critical CSS — inline above-the-fold styles Extract only the CSS needed to render the visible viewport, inline it in ``, and load the rest asynchronously. ```html ``` Tools to automate critical CSS extraction: `critical`, `critters` (used by Angular CLI), `penthouse`. ### 2. PurgeCSS — remove unused rules at build time PurgeCSS scans your HTML/JS/template files and removes any CSS selectors it does not find. ```js // postcss.config.js const purgecss = require('@fullhuman/postcss-purgecss'); module.exports = { plugins: [ purgecss({ content: [ './src/**/*.html', './src/**/*.jsx', './src/**/*.tsx', './src/**/*.vue', ], defaultExtractor: content => content.match(/[\w-/:]+(? import('./AdminPanel')); // AdminPanel.jsx imports './admin.css' — that CSS ships only when AdminPanel loads ``` ### 5. CSS-in-JS Libraries like styled-components, Emotion, and Stitches only insert styles for components that are actually rendered. No dead CSS reaches the page. ```jsx import styled from 'styled-components'; // This CSS is only injected into the DOM when