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