sfw/fix
Reduce unused JavaScript high

Reduce unused JavaScript

The page downloads, parses, and compiles JavaScript it never executes, burning main-thread time and delaying interactivity.

What you see

Reduce unused JavaScript
Reduce unused JavaScript and defer loading scripts until they are required to decrease bytes consumed by network activity.
Est savings of 1,240 KiB

What’s actually happening

The page looks done but won't respond — taps and scrolls lag for a second or two while the CPU chews through scripts. Lighthouse lists specific .js files with a chunk of each greyed out as unused. The Coverage tab in Chrome DevTools (Cmd+Shift+P, "Show Coverage") shows the same files 60-80% red. Worst on mid-range phones, where parse and compile cost several times what it does on your laptop.

Common causes

  • Whole libraries imported for one function (all of lodash or moment for a single call)
  • A bundler with no tree-shaking, or CommonJS imports that defeat it, so dead code ships anyway
  • Third-party tags loaded on every page — chat widgets, A/B tools, analytics, ad scripts — most of which the current page doesn't need
  • No code splitting: one giant bundle for the whole app loads on the landing page
  • Polyfills and legacy transpiler output served to modern browsers that don't need them

How to fix it

  1. Read the Coverage tab and go after the biggest red bars firstOpen DevTools Coverage, reload, sort by unused bytes. The top one or two files usually account for most of the waste. That tells you whether the problem is your own bundle or a third-party script before you change anything.
  2. Code-split so each route loads only its own codeUse dynamic import() for routes and heavy components (React.lazy, Vue async components, route-level splitting in Next/Nuxt). The landing page stops paying for the dashboard's chart library. This is usually the single biggest win.
  3. Import only what you use and let tree-shaking workSwap import _ from 'lodash' for import debounce from 'lodash/debounce'. Drop moment for date-fns or native Intl. Make sure your build targets ES modules so the bundler can actually drop dead code — CommonJS often can't be shaken.
  4. Defer and gate third-party scriptsAdd defer or async to tags that don't need to block. Load chat, marketing, and A/B scripts on interaction or after the page is idle (requestIdleCallback) rather than on every initial load. Audit whether each tag still earns its place.
  5. Stop shipping legacy code to modern browsersUse a modern build target (esbuild/SWC, or the module/nomodule pattern) so up-to-date browsers skip the polyfills and down-leveled syntax. That alone can shave a large slice off the bundle.

Stop it recurring

Put a bundle-size budget in CI (e.g. bundlesize or Lighthouse CI) so a new dependency that balloons the bundle fails the build.

Related errors