Puppeteer CSSCoverageOptions: Configure CSS Coverage
Configure Puppeteer CSS coverage across navigation, collect and interpret the report, and understand what the results leave out.
CSSCoverageOptions controls whether Puppeteer resets CSS coverage when the page navigates. In Puppeteer 25.10.0, the option is resetOnNavigation, and its documented default is true. Set it to false when you want coverage collection not to reset on every navigation. Start collection before navigating, then call stopCSSCoverage() to receive the stylesheet entries.
This guide uses Puppeteer 25.10.0 API behavior. Check the documentation for the version installed in your project if you are using a different release.
1. What CSSCoverageOptions configures
CSSCoverageOptions is the optional options object passed to page.coverage.startCSSCoverage(options?). The Puppeteer 25.10.0 reference lists one property:
| Option | Type | Documented default | Effect |
|---|---|---|---|
resetOnNavigation |
boolean |
true |
Whether CSS coverage resets on every navigation. |
Choose true for a fresh coverage window at each navigation (the default). Choose false when navigation should not reset coverage. The option describes reset behavior; it does not promise that coverage survives every browser or page lifecycle event.
2. Runnable example: keep coverage across navigation
Install Puppeteer in a Node.js project with npm install puppeteer. The following CommonJS script starts CSS coverage, visits two pages in the same tab, stops the collector, and prints a byte-range estimate from the returned entries.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
try {
const page = await browser.newPage();
await page.coverage.startCSSCoverage({ resetOnNavigation: false });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.goto('https://example.com/about', { waitUntil: 'networkidle2' });
const cssCoverage = await page.coverage.stopCSSCoverage();
let totalBytes = 0;
let usedBytes = 0;
for (const entry of cssCoverage) {
totalBytes += entry.text.length;
for (const range of entry.ranges) {
usedBytes += range.end - range.start - 1;
}
}
const percentUsed = totalBytes === 0 ? 0 : (usedBytes / totalBytes) * 100;
console.log(`Stylesheets: ${cssCoverage.length}`);
console.log(`Estimated used CSS bytes: ${usedBytes} / ${totalBytes} (${percentUsed.toFixed(1)}%)`);
console.log(JSON.stringify(cssCoverage, null, 2));
} finally {
await browser.close();
}
})();
The two navigations share one collection window because coverage is started once and stopped after both visits. With resetOnNavigation: true, each navigation resets coverage according to the documented option behavior. If you need a separate report for each page, stop and start coverage around each page visit, or use the default reset behavior and confirm the resulting report matches your intended window.
Lifecycle and report shape
- Call
await page.coverage.startCSSCoverage(options)before the activity you want to measure. The method resolves toPromise<void>; it does not itself return the report. - Exercise the pages and interactions in scope, including relevant routes, menus, tabs, and responsive states if they affect which CSS is used.
- Call
const entries = await page.coverage.stopCSSCoverage()to end collection and receive stylesheet coverage entries. - Consume or save the entries before starting another collection window.
Each entry includes stylesheet text and covered ranges. Puppeteer’s documented example sums the text lengths and range lengths to estimate the share of reported CSS bytes used. This is an estimate based on returned coverage data, not a complete inventory of every style that may affect runtime rendering.
3. Choose a collection window deliberately
| Setting | Use when | Practical interpretation |
|---|---|---|
resetOnNavigation: true |
You want the documented default or a reset at navigation. | Coverage resets on each navigation. Structure your collection around the page or navigation boundaries you want represented. |
resetOnNavigation: false |
You want navigation not to reset coverage during the collection. | Start before the first relevant visit and stop after the last one to get the report for that collection window. |
For a route audit, define what counts as the target: one URL, a sequence of routes, or a route plus interactive states. Then run those steps consistently. A report only reflects code paths exercised during its collection window; visiting a route does not automatically exercise every component or state it can display.
4. Interpret the CSS coverage report
For every returned stylesheet entry, text is the stylesheet content and ranges identifies covered source ranges. Puppeteer’s documented byte calculation is:
const totalBytes = entry.text.length;
const usedBytes = entry.ranges.reduce(
(sum, range) => sum + range.end - range.start - 1,
0
);
Sum these values over the entries to estimate a used-byte percentage. Keep the result tied to the routes and interactions you exercised. It is useful for finding CSS that may deserve review, but should not alone drive deletion: unvisited routes, conditional states, or styles injected at runtime can be absent from the measured coverage.
Puppeteer documents a specific omission: CSS Coverage does not include dynamically injected style tags without source URLs. Such styles will not appear in the returned entries. If your application creates styles dynamically, account for this blind spot before treating the report as a complete unused-CSS audit.
The Puppeteer Coverage example also points to puppeteer-to-istanbul for output consumable by Istanbul. Use that route if your workflow needs coverage output in that tooling ecosystem.
5. Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| The method returns no coverage entries. | Collection was stopped before relevant stylesheets loaded, or the page produced no reportable stylesheet entries. | Start coverage before navigation, wait for the page and the interactions under review, then stop coverage. |
| The report only reflects the last page or route. | Coverage reset at navigation, which is the documented default. | Pass { resetOnNavigation: false } if the collection should not reset on navigation; alternatively, define separate collection windows for separate reports. |
| Coverage appears incomplete for runtime styles. | Some styles may be injected dynamically without a source URL, which CSS Coverage omits. | Do not interpret the returned entries as a complete account of runtime styles. Review dynamic style creation separately. |
| The used percentage seems unexpectedly low. | The tested route or interaction set did not exercise all CSS-dependent states. | Expand the route and interaction coverage, including conditional UI and responsive states relevant to the audit. |
startCSSCoverage does not appear to give the report. |
The start method resolves to void; the report is returned by the stop method. |
Await stopCSSCoverage() after the collection window and read its returned entries. |
6. Reliability, runtime, and cost considerations
- Collection boundaries: Start and stop explicitly so the report corresponds to a known set of visits and interactions. Ensure errors in navigation do not skip your cleanup path; close the browser in a
finallyblock as in the example. - Repeatability: Use the same routes, waits, viewport, and interactions when comparing runs. Differences in exercised states can change observed coverage.
- Runtime: Coverage collection and page navigation add work to an automation run. Keep the audited route set focused, and avoid collecting more pages or interactions than the analysis needs.
- Cost: Puppeteer itself is an open-source browser automation library; infrastructure cost depends on where and how often you run browsers. This API reference provides no benchmark or fixed execution cost.
- Interpretation risk: Coverage reports are evidence about exercised code in the returned data. The documented dynamic-style omission and unexercised paths mean a low-coverage result is not, by itself, proof that CSS is safe to remove.
7. Or skip the browser setup
If your task is to capture a page image rather than inspect CSS execution, ScreenshotNeo returns a screenshot or PDF from one API request. See the ScreenshotNeo API documentation for its request options.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Learn about ScreenshotNeo, then sign up for 1,000 free screenshots a month with no card.
8. FAQ
Does CSSCoverageOptions have a selector or URL filter?
The Puppeteer 25.10.0 reference lists only resetOnNavigation for this options type. Define the pages and interactions in your collection workflow instead.
Does resetOnNavigation: false guarantee coverage survives every page transition?
No such broader guarantee is stated by the CSS options reference. It specifies that coverage does not reset on every navigation; keep conclusions within that documented behavior.
Can I use this report to remove CSS automatically?
It is safer to use coverage as an investigation aid. The report reflects exercised paths and omits dynamically injected style tags without source URLs, so validate any removal against the application’s full route and state set.


