
Remove Unused JavaScript: How to Find and Fix It
TL;DR — This audit fires when your bundles contain code the page downloads but never executes on load. Dead JavaScript still gets fetched, parsed, and compiled, so it steals network bandwidth and boot-up CPU before a single line of it ever runs — if it runs at all.
What this check flags
Lighthouse measures byte-level coverage of every script and reports how much of each file went unused during page load. A 400 KB bundle where only 120 KB executes has 280 KB of pure overhead: bytes the browser fetched, parsed into an AST, and handed to the compiler for nothing. This is a shipping problem, not a runtime one — the fix is to stop sending the code, not to make it run faster.
A real example, and the fix
You import a whole utility library to use one function:
import _ from 'lodash'; // pulls the entire library into the bundle
const unique = _.uniq(ids);Even with tree-shaking configured, a default CommonJS import of lodash often drags in the full module because the bundler can't prove the rest is side-effect-free. Import the single function from its own path so only that code lands in the bundle:
import uniq from 'lodash-es/uniq'; // one function, tree-shakeable
const unique = uniq(ids);For code tied to a screen the user may never open, don't import it statically at all — load it on demand:
button.addEventListener('click', async () => {
const { openEditor } = await import('./editor.js'); // fetched only on click
openEditor();
});The three ways dead code gets in
Unused JavaScript almost always traces back to one of these root causes. Naming which one you have points straight at the fix.
| Root cause | What it looks like | The fix | Tool |
|---|---|---|---|
| Whole-library imports | import _ from 'lodash' for one helper | Import single functions; use ES-module builds | Tree-shaking / bundle analyzer |
| Everything in one bundle | Route B's code shipped on Route A | Route-based code splitting | Dynamic import() |
| Polyfills for modern browsers | Legacy shims served to Chrome/Safari | Serve modern JS with module/nomodule | Differential serving |
| Abandoned features | Old A/B tests, dead components | Delete the source; verify with coverage | DevTools Coverage tab |
How to detect it
- Lighthouse / PageSpeed Insights: open "Reduce unused JavaScript" under Diagnostics. It lists each script with the wasted bytes and estimated savings, ranked so you fix the biggest offender first.
- DevTools Coverage tab: open DevTools, run "Show Coverage" from the command menu, reload, and read the unused percentage per file. Then exercise real interactions — the number should drop as code you actually use finally executes; whatever stays red on a full journey is a deletion candidate.
- Bundle analyzer: run
webpack-bundle-analyzeror the Vite/Rollup visualizer to see which modules bloat each chunk, so you catch a fat dependency at build time instead of on a live page.
How to fix it
- Turn on tree-shaking properly. Use ES-module imports, mark packages side-effect-free where safe, and build for production so the bundler can drop unreachable exports. Start with the tree shaking primer if the concept is new.
- Code-split by route and interaction. Load a view's code only when the user visits it. The code splitting pattern with dynamic
import()is the workhorse here. - Import narrowly. Pull single functions from their own paths instead of whole libraries, and prefer smaller dependencies for one-off needs.
- Kill dead features. Delete abandoned components and stale experiment code at the source, then re-run Coverage to confirm the bytes are gone.
- Defer third parties. Tag managers and widgets are often the biggest chunk of unused JS on load — audit each and load non-essential ones after first paint.
How this differs from Reduce JavaScript execution time
These two are cause and effect, not duplicates. Unused JS is measured in bytes and coverage — how much code you shipped that never ran. Reduce JavaScript execution time is measured in CPU seconds — how long the code that does run takes on the main thread. Deleting unused code helps both audits, because bytes you never send are never parsed or compiled. But you can pass this audit and still fail execution time if the code you do ship runs expensively. Also note the sibling audit Minify JavaScript shrinks the code you keep; removing unused code deletes the code you don't. Do both.
FAQ
Is unused JavaScript the same as unminified JavaScript?
No. Minification compresses the code you keep by stripping whitespace and shortening names. Removing unused code deletes code you never run. They stack: purge the dead code, then minify what survives.
Why does tree-shaking sometimes fail to remove code?
Tree-shaking relies on static analysis of ES modules. CommonJS imports, dynamic property access, and packages not marked side-effect-free defeat it, so the bundler keeps the code to be safe. Use ES-module builds and a proper sideEffects field to unblock it.
Will code splitting increase the number of requests?
Yes, slightly — but over HTTP/2 that trade is almost always a win, because you defer bytes the user may never need instead of blocking the first load with all of them.
How much unused JS is acceptable?
There is no hard pass line like other audits, but treat anything above roughly 40 KB of wasted script as worth chasing. On mobile, every 100 KB of unnecessary JavaScript is real parse-and-compile time on a slow CPU.
Can I remove unused code from third-party scripts?
Usually not directly — you don't control their bundles. What you can control is whether they load at all and when. Audit each vendor, drop the ones you don't need, and defer the rest past first paint.
Claude Vincent is a technical SEO consultant focused on crawlability, rendering, and AI-search visibility. He writes the field guides and case studies at SEO ProCheck, with a bias toward the durable, unglamorous work that decides whether search engines and AI answer engines can actually read and cite a site.
About SEO ProCheck
Technical SEO consulting and GEO strategy with 20 years of enterprise experience. Case studies, resources, and tools for search and AI visibility.
Work With Me
Technical SEO audits, GEO strategy, site migrations, and international SEO. Hourly consulting for teams who need hands-on support, not just reports.







