Reduce Unused JavaScript
By Christian Schreiber · Updated
When PageSpeed Insights or Lighthouse tells you to reduce unused JavaScript, it has found script files that your page downloads during load but largely never executes. The browser paid for bytes that did no work for this page view, and on a phone that cost shows up as slower loading and sluggish interactions.
From PerfDelta's measurements: Lighthouse reports this finding on 85% of 4,108 measured home pages. The median estimated saving is 1650 ms. More in the data section below.
What the "Reduce unused JavaScript" audit measures
The audit (id unused-javascript) loads the page in Chrome with code coverage switched on and records which parts of every script actually run before the load finishes. Anything that was delivered but not executed in that window counts as unused. Lighthouse then lists each affected file with its transfer size and the estimated savings, meaning the bytes you could avoid sending if the unused part were not in the file.
Coverage only reflects the page load itself. Code that runs when a visitor opens a menu or submits a form is reported as unused, because Lighthouse never clicks anything. Third-party files are included, so a single tag manager or chat widget can dominate the list even when your own bundle is lean. Typical sources are one bundle containing every route, a full library imported for one helper, outdated polyfills, and plugins that load their scripts site-wide.
Why unused JavaScript slows down LCP, TBT and INP
JavaScript is expensive because the work does not end with the download. The browser parses and compiles the code, and execution runs on the main thread, which also handles rendering and user input. Unused code still costs download and parse time and takes bandwidth from the resources your page needs.
- Total Blocking Time rises when large scripts create long tasks. TBT carries 30 % of the Lighthouse performance score, the largest weight of any metric.
- Interaction to Next Paint suffers in the field when the main thread is still busy with script work as a visitor taps or types. INP is good at 200 ms or less and poor above 500 ms.
- Largest Contentful Paint is delayed when the main content depends on JavaScript to render, or when big bundles compete with the hero image for bandwidth. LCP is good at 2.5 s or less.
How to find the files behind unused JavaScript
Chrome DevTools Coverage panel
Open DevTools, press Ctrl+Shift+P (Cmd+Shift+P on a Mac), run Show Coverage and reload the page. The panel lists every script with total and unused bytes, and clicking a file marks the unused lines. Then interact with the page. The unused share drops for code that serves those interactions, which is code you can load on demand instead of up front.
Lighthouse treemap
In a Lighthouse report, the View Treemap button opens a map of all JavaScript on the page, sized by bytes, with a view that highlights unused bytes. With source maps, it breaks bundles down into modules, so you can see when a date library or an icon set makes up much of the waste.
Bundle analyzers
A bundle analyzer shows what ends up in each chunk at build time: webpack-bundle-analyzer, rollup-plugin-visualizer for Vite, @next/bundle-analyzer or source-map-explorer. Look for duplicate libraries, large dependencies used in one place, and page-specific code in the shared chunk.
How to reduce unused JavaScript step by step
1. Split code by route and interaction
Code splitting breaks one large bundle into smaller chunks that load only when needed. A dynamic import() tells the bundler to put a module into its own chunk and fetch it at runtime:
// before: always part of the main bundle
import { openCheckout } from './checkout.js';
// after: fetched only when the visitor clicks
buyButton.addEventListener('click', async () => {
const { openCheckout } = await import('./checkout.js');
openCheckout();
});
Good candidates are modals, charts and maps. Keep code for above-the-fold content in the initial bundle, otherwise you trade unused JavaScript for a slower LCP.
2. Make tree shaking work
Tree shaking lets the bundler drop exports that nothing imports. It only works reliably with ES module syntax, not with require(). Import the functions you use from packages that ship ES modules:
// pulls in the whole CommonJS build
import _ from 'lodash';
// pulls in one function
import debounce from 'lodash-es/debounce';
Declare in package.json which files have side effects on import, so the bundler can safely remove the other unused modules:
{
"sideEffects": ["*.css"]
}
Barrel files that re-export a whole component library often defeat tree shaking, so import from the specific module path.
3. Remove or replace heavy libraries
Delete dependencies that are no longer referenced. Replace a large library that serves one task with native APIs, such as fetch instead of an HTTP client or Intl.DateTimeFormat for dates. Update your browserslist target so the build stops shipping polyfills for browsers you no longer support.
4. Defer and trim third-party scripts
You cannot change a vendor's code, but you control whether and when it loads. Remove tags nobody uses, and in your tag manager replace "All Pages" triggers with page-specific ones. Chat widgets and video embeds suit a facade, a placeholder that loads the real widget on click:
<button id="chat-open">Chat with us</button>
<script>
document.getElementById('chat-open').addEventListener('click', () => {
const s = document.createElement('script');
s.src = 'https://chat.example.com/widget.js';
document.head.appendChild(s);
}, { once: true });
</script>
Scripts that must load everywhere should at least use defer or async, as covered in eliminating render-blocking resources.
5. Reduce unused JavaScript in WordPress
In WordPress, most unused JavaScript comes from plugins that enqueue scripts on every page, such as a form plugin loading on the blog. Deactivate plugins you no longer need, then disable scripts per page or post type with an asset unloading plugin such as Asset CleanUp or the Perfmatters script manager. In a child theme, you can do it in code:
add_action( 'wp_enqueue_scripts', function () {
if ( ! is_page( 'contact' ) ) {
wp_dequeue_script( 'contact-form-7' );
}
}, 100 );
Check the plugin's script handle first, and test forms and carts after each change, because a missing script often fails silently.
6. Reduce unused JavaScript in React and Next.js
Next.js already splits JavaScript by route. Unused code usually comes from heavy components imported into shared layouts. Load those with next/dynamic:
import dynamic from 'next/dynamic';
const Map = dynamic(() => import('../components/Map'), {
ssr: false,
loading: () => <p>Loading map…</p>,
});
In plain React, React.lazy with Suspense does the same for components and routes. With the Next.js App Router, keep components as Server Components where possible, because only files marked 'use client' add to the client bundle.
Common mistakes when reducing unused JavaScript
- Lazy loading above-the-fold content. Splitting off the hero component delays LCP.
- Chasing zero. Handlers for interactions that have not happened yet are normal. Focus on the largest files.
- A blanket
"sideEffects": false. It can strip global CSS or polyfill imports and break production. - Only minifying. Minification shrinks code, but unused code stays in the bundle.
- Testing one page only. Plugins and tags differ between templates.
How to verify the fix
Re-run Lighthouse or PageSpeed Insights and compare the files and estimated savings in the audit with the previous run. Total Blocking Time should drop and LCP should not get worse. Chrome UX Report field data follows over a trailing 28-day window.
PerfDelta tests several pages on mobile and desktop, shows lab and field data, and lists Lighthouse opportunities with estimated savings and affected files, so you can see which templates still carry unused JavaScript. A bundle size budget or Lighthouse CI on pull requests keeps new dependencies from undoing the work. The audits overview links related fixes such as reducing unused CSS.
Frequently asked questions
What does "Reduce unused JavaScript" mean?
It means the page downloads JavaScript that is not executed during the page load. Lighthouse measures this with code coverage and reports, per file, how many bytes could be saved if the unused part were not sent.
Is unused JavaScript the same as dead code?
No. Dead code is never used. Unused JavaScript is code that did not run during this load, including code for later interactions, so the fix is often to load it on demand rather than delete it.
Does deferring a script fix the audit?
Not by itself. The defer attribute stops a script from blocking parsing, but the file is still downloaded during load and its unused bytes still count. Loading the script only on the pages that need it, or only after an interaction, is what reduces the reported savings.
Which metrics improve when I reduce unused JavaScript?
Mainly Total Blocking Time in the lab and Interaction to Next Paint in the field, because less script work keeps the main thread free. Largest Contentful Paint can also improve when the main content depends on JavaScript or competes with large bundles for bandwidth.
How do I reduce unused JavaScript in WordPress?
Remove plugins you no longer use, then disable plugin scripts on pages that do not need them, with Asset CleanUp, Perfmatters or wp_dequeue_script in a child theme. Test forms after every change.
How do I reduce unused JavaScript in Next.js?
Next.js splits code by route automatically. Load heavy components such as maps, charts or editors with next/dynamic, analyse the bundles with @next/bundle-analyzer, and with the App Router keep components as Server Components unless they need client-side interactivity.
Why do third-party scripts show up in the audit?
The audit covers every script the page loads. Tag managers, chat widgets and testing tools often ship large files of which little runs on a given page. Remove unneeded tags, limit the rest to relevant pages and use facades for widgets.
What PerfDelta's measurements show
Lighthouse reports this finding on 85% of 4,108 measured home pages. The median estimated saving is 1650 ms.
Data as of October 16, 2026, recalculated daily. Source: PerfDelta's own measurements (Chrome UX Report and Lighthouse); details in the website speed statistics and the methodology.
Thresholds and sources
Sources:
All sources retrieved on October 9, 2026. Thresholds and documentation change; the linked page is authoritative.
Test your site
Run a free scan to see whether this applies to your pages, on mobile and desktop, with lab and field data side by side.
Run a free testA free account saves your tests and shows how your values change from one test to the next. Daily monitoring with field data and alerts starts with the Solo plan.