Puppeteer CSS Coverage Options Explained
Puppeteer CSS coverage has one documented option: resetOnNavigation. Learn its default, how to collect and interpret reports, and where coverage has blind spots.
Puppeteer’s v25.12.0 API reference documents one CSS coverage option: resetOnNavigation. It defaults to true, which resets coverage on each navigation. Set it to false to disable that automatic reset. This setting describes reset behavior; it does not guarantee that coverage will persist across navigations.
Start collection with page.coverage.startCSSCoverage(), exercise the pages and states you want to measure, then call page.coverage.stopCSSCoverage() to get stylesheet reports. Keep in mind that dynamically injected style tags without source URLs are excluded.
1. The CSS coverage option
| Option | Default | Effect | When to use it |
|---|---|---|---|
resetOnNavigation |
true |
Resets CSS coverage on each navigation when enabled. | Keep the default for page-specific results. Set to false to disable automatic resets when collecting a flow that includes navigation. |
This is the only CSS coverage option documented in the referenced Puppeteer API. JavaScript coverage has separate options; includeRawScriptCoverage, reportAnonymousScripts, and useBlockCoverage are not CSS coverage settings.
If you disable the reset, treat the resulting report as coverage observed during your collection run, not as a guarantee that every stylesheet or every navigation was retained. Verify the behavior needed by your workflow against the Puppeteer version you use.
2. Collect CSS coverage
The basic sequence is to start coverage before navigating or exercising the page, visit the relevant routes and states, and then stop coverage:
const coverage = await page.coverage.startCSSCoverage();
// Exercise the route, interactions, and states you want to measure.
const cssCoverage = await page.coverage.stopCSSCoverage();
The return value from startCSSCoverage() is not the report. The reports come from stopCSSCoverage(), which resolves to an array of stylesheet coverage entries.
Runnable Node.js example
This example uses the default reset behavior and prints an observed used-byte percentage for the captured stylesheet reports. Install Puppeteer in your project first, then save this as coverage.js and run it with Node.js.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.coverage.startCSSCoverage({ resetOnNavigation: true });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
// Add clicks, route changes, or other interactions here if they are
// part of the states you want represented in the report.
const reports = await page.coverage.stopCSSCoverage();
const totalBytes = reports.reduce((sum, report) => sum + report.text.length, 0);
const usedBytes = reports.reduce((sum, report) => {
return sum + report.ranges.reduce((rangeSum, range) => {
return rangeSum + range.end - range.start - 1;
}, 0);
}, 0);
const percent = totalBytes === 0 ? 0 : (usedBytes / totalBytes) * 100;
console.log(`Stylesheets: ${reports.length}`);
console.log(`Observed used bytes: ${usedBytes} / ${totalBytes} (${percent.toFixed(1)}%)`);
for (const report of reports) {
console.log({ url: report.url, ranges: report.ranges });
}
} finally {
await browser.close();
}
})().catch(error => {
console.error(error);
process.exitCode = 1;
});
The byte calculation follows the official example’s range arithmetic. It is an observed-use metric for this run, not proof that unobserved CSS is safe to delete: another route, interaction, viewport, or application state may use it.
Collect through a navigation flow
When a flow includes navigation, you can disable automatic resets:
await page.coverage.startCSSCoverage({ resetOnNavigation: false });
await page.goto('https://example.com');
// Exercise the first page.
await page.goto('https://example.com/another-route');
// Exercise the next page.
const reports = await page.coverage.stopCSSCoverage();
This configures the reset behavior, but the option reference does not promise that the resulting report is a complete union across every navigation. If that guarantee matters, collect each route in a separately controlled run and compare the reports, or validate the precise behavior in your Puppeteer version.
3. Interpret the report carefully
Each entry returned by stopCSSCoverage() represents a stylesheet and includes source text and ranges reported as used. The official example totals used bytes by summing range.end - range.start - 1 for each range, then compares that value with the stylesheet text length.
- Coverage describes what the run exercised. It does not establish that CSS absent from the report is unused throughout the application.
- Exercise meaningful states. Menus, dialogs, validation errors, authenticated views, responsive layouts, and hover or focus states may need explicit interaction or separate runs.
- Know the injection limitation. Puppeteer’s official documentation states: “CSS Coverage doesn’t include dynamically injected style tags without sourceURLs.” If your app injects styles, account for this omission before using coverage to remove CSS.
- Use source text and ranges together. A percentage alone can hide which files or code regions were observed. Preserve the URL and ranges when producing a reviewable report.
4. Practical collection choices
Page-specific measurements
Leave resetOnNavigation at its default, true, when you want the documented reset on each navigation. Start collection before exercising the target page and stop it when that page’s measurement is complete.
Measurements across a flow
Use resetOnNavigation: false when you want to disable the automatic reset during a sequence containing navigation. Since the API reference only defines the reset behavior, test a representative flow and inspect the resulting reports rather than assuming persistence semantics beyond that.
Large applications
For many routes, prefer a repeatable route-and-state matrix over one long, opaque browser session. Record the route, viewport, interaction state, and whether coverage was reset for each run. This makes gaps and changes easier to review.
5. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| Coverage is empty or has no useful ranges. | Collection stopped before the page was exercised, or the run did not reach the styles and states of interest. | Start collection before navigation or interactions, wait for the relevant page state, exercise it, and stop afterward. |
| Earlier page activity is missing after navigation. | resetOnNavigation is enabled; that is the default. |
For a flow, set resetOnNavigation: false and validate the resulting reports. For page-specific results, collect each page deliberately. |
| Injected component styles do not appear. | Dynamic style tags without source URLs are omitted by CSS coverage. | Do not treat the report as a complete inventory of runtime CSS. Test the component states and account for the documented limitation in your review. |
| The unused percentage seems too high. | The run may not have exercised all routes, interaction states, or viewport-specific styles. | Expand the route and state matrix, including responsive and conditional UI, then compare coverage across runs. |
| Code using JavaScript coverage options has no effect on CSS results. | Those options belong to JavaScript coverage, not CSS coverage. | Configure CSS with its documented resetOnNavigation option only. |
6. Performance, reliability, and cost
CSS coverage adds collection and report-processing work to a browser run. The cited Puppeteer documentation does not provide a benchmark or a fixed overhead figure, so measure your own representative pages if runtime matters. Keep runs scoped to the routes and states needed, and avoid repeatedly collecting a large application when a targeted route set answers the question.
For reliable comparisons, hold the route, viewport, application data, and interactions steady between runs. A coverage result is a record of what that run observed; it can change when code, content, or exercised states change. There is no Puppeteer service charge described by this API workflow; your costs depend on where and how you run the browser.
7. Or skip the browser setup
If your goal is to capture a page image rather than inspect CSS usage, ScreenshotNeo provides a website screenshot API. It does not replace Puppeteer CSS coverage: it returns screenshots or PDFs, not stylesheet coverage reports. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
With Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
With Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
8. FAQ
Does CSS coverage have an option to include anonymous scripts?
No. reportAnonymousScripts is a JavaScript coverage setting, not a documented CSS coverage option.
Can I use CSS coverage to prove a rule is safe to delete?
No single run proves that. Coverage shows observed use for the collected routes and states, and dynamically injected style tags without source URLs are excluded.
What is the current documented option?
The referenced Puppeteer v25.12.0 API documents resetOnNavigation?: boolean, with a default of true.


