Skip to content
Issue docs

Unused JavaScript

Standardunused_javascriptIssue 181

What is this issue?

Unused JavaScript is code a page downloads but never runs while it loads -- the rest of a library imported for one function, components for other pages bundled into this one, or a third-party tag the page does not need.

This check reports pages where Lighthouse finds 20 KiB or more of unused JavaScript.

Why it matters

  • JavaScript is the most expensive byte on the web. It is downloaded, then parsed and compiled on the main thread -- which on a mid-range phone is often slower than the download.
  • It feeds the responsiveness metrics. Parse and compile time shows up as Total Blocking Time in the lab and Interaction to Next Paint for real users.
  • It hides the code that matters. A page that ships three times the code it runs is harder to keep fast as it changes.

Fixing it removes a standard-severity finding from the site's health score.

How to fix it

  1. Split your bundles by route so each page loads only its own components (dynamic import(), or your framework's route-based code splitting).

  2. Import only what you use -- import debounce from 'lodash/debounce', not the whole library -- and let your bundler tree-shake the rest.

  3. Load features on demand. Modals, carousels, chat and video players can be loaded when they are first used.

  4. Remove what is not needed at all -- abandoned experiments, duplicate libraries, tags nobody reads.

  5. Use the Coverage panel in Chrome DevTools to see which lines of each script ran.

Examples

1. A whole library for one function

Fails -- 68 KiB unused:

import _ from 'lodash';
const onResize = _.debounce(update, 200);

Passes:

import debounce from 'lodash/debounce';
const onResize = debounce(update, 200);

2. Every component on every page

Fails -- 240 KiB unused: the home page loads the checkout, account and search components in one bundle.

Passes -- route-based splitting; the home page loads 40 KiB.

How PixyScan detects this

  1. Chooses which pages to measure. After the crawl, PixyScan measures the homepage plus the pages your own site links to most, up to a per-scan budget (ten pages by default, at most fifty). Measuring every page would take hours and exhaust the PageSpeed Insights quota of the API key it runs under.

  2. Asks the PageSpeed Insights API about each of them, on mobile. This needs the Hobby plan or above and a PageSpeed Insights API key on the site or its workspace.

  3. Reads the Lighthouse audit unused-javascript, which uses Chrome's code coverage during the lab load to list each script with at least 20 KiB that never ran.

  4. Raises the issue when the total unused bytes across every listed script reach 20 KiB -- Lighthouse's own per-script floor, so in practice when Lighthouse lists any script.

  5. Raises nothing on a page it did not measure. A page outside the sample, and a page whose measurement failed, are both reported as Not measured. Neither is treated as passing, and neither can fail this check.

What we store

Storage Level

Page Level


Database Table / Prisma Model

PagePerformance (page_performance), with the finding itself on audit_issues.details.


Fields Used

Field Type Description
strategy String mobile or desktop. Every number in the row belongs to one form factor.
fetchedAt DateTime When PageSpeed answered. "Measured three weeks ago" is not "not measured".
errorReason String? Why a sampled page still has no numbers. Null when the call succeeded.
audits Json? A compact summary of the Lighthouse audits the opportunity checks read: per audit its source (the Lighthouse audit id it was read from), score, numericValue, displayValue, itemCount, totals over every row (wastedBytes, wastedMs, blockingTime, mainThreadTime) and the five worst rows. Keyed by the classic Lighthouse audit id. Null when the response carried no audits.

On the finding (audit_issues.details): message, plus audits (unused-javascript), wastedBytes (unused bytes over every script), thresholdBytes (20480), fileCount and items (the five scripts with the most unused bytes, each url, totalBytes, wastedBytes).


Detection Dependencies

  • PageSpeed Insights API v5 (lighthouseResult.audits), mobile strategy
  • The PageSpeed plan feature (Hobby and above) and a PageSpeed Insights API key
  • The crawl's internal link graph, which decides which pages are sampled

Note

A page with no row was never measured. The pass samples the homepage plus the most-linked pages within a per-scan budget (default 10, at most 50), so most pages of a site have no page_performance row at all. That is the normal state, it is drawn as Not measured, and this check cannot be raised against it.

A row whose metric columns are all null with an errorReason set is a different state again: the page WAS sampled and the measurement failed. It is still never reported as passing.

Further reading