All guides · Performance

JavaScript Cost: Ship Less Code, Run Less Code

JavaScript is the most expensive kind of byte on a web page. An image only has to be downloaded and decoded; a script also has to be parsed, compiled and run, and all of that happens on the main thread, the same thread that responds to taps, clicks and typing. On a mid-range phone this can take several times longer than on a developer’s laptop.

Why it matters

While the main thread is busy running scripts, the page can’t react. That shows up as Total Blocking Time in the lab and as slow Interaction to Next Paint for real visitors: buttons that don’t respond, menus that open late, typing that lags.

What the checks look at

  • Unused JavaScript: code the page downloads but doesn’t run during load. Common sources are whole libraries used for one function, plugins loaded on every page, and widgets nobody uses.
  • Legacy JavaScript: polyfills and transpiled code for old browsers, sent to modern browsers that don’t need them.
  • Duplicated JavaScript: the same library included in several bundles, so it is downloaded and parsed more than once.
  • JavaScript execution time: how long scripts spend being parsed, compiled and run, per file.
  • Main-thread work: all main-thread work by category: script evaluation, style and layout, rendering, parsing HTML and CSS, garbage collection.
  • Minification: CSS and JavaScript files that still contain whitespace and comments.

How to fix

Remove before you optimise

The fastest script is the one you don’t load. Review your tags and plugins: old A/B testing snippets, a second analytics tool, a chat widget on every page, a slider library for one slider. Each removal helps every page at once.

Load scripts only where they are needed

Split bundles per route or page so the home page doesn’t carry the checkout’s code. Most bundlers (Vite, webpack, Rollup, esbuild) split code automatically at dynamic imports:

button.addEventListener('click', async () => {
  const { openEditor } = await import('./editor.js');
  openEditor();
});

Scripts that must load on the page but aren’t needed for the first screen should use defer (see render-blocking resources).

Target modern browsers

If your build uses Babel or a similar tool, set its targets to the browsers you actually support, for example with a browserslist entry in package.json:

{
  "browserslist": ["defaults and fully supports es6-module"]
}

Check what polyfills your build adds. Many, such as those for Promise, fetch or Array.prototype.includes, are unnecessary for current browsers.

Deduplicate dependencies

Bundle analyzers show which packages appear more than once. Align versions so the package manager installs one copy, and make sure separate bundles share a common chunk instead of each including the library.

Minify

Build tools minify in production mode by default; make sure production builds use it. On WordPress, optimisation plugins such as Autoptimize, WP Rocket or LiteSpeed Cache can minify theme and plugin files and defer scripts. Test after enabling them, since combining or deferring scripts can break features.

Break up long tasks

When code has to run, split long work into smaller chunks so the browser can handle input in between. web.dev’s guide on optimizing long tasks shows the patterns.

Nginx, Apache and Cloudflare

Server settings don’t reduce the amount of JavaScript that runs. They do make delivery cheaper: turn on text compression and long cache lifetimes for versioned bundles.

How PerfoMetrix checks this

We read these Lighthouse audits from the PageSpeed Insights run we already make (mobile, simulated device): unused-javascript, legacy-javascript-insight, duplicated-javascript-insight, bootup-time, mainthread-work-breakdown, and unminified-javascript with unminified-css (shown together as one minification check). If Lighthouse rates an audit 0.9 or higher, or it doesn’t apply, the check passes; otherwise it needs attention. We show Lighthouse’s estimate, such as “Est savings of 180 KiB” or “2.4 s”, and the first five files or items it lists.

These checks are shown but don’t count toward the performance score: their effect is already measured by TBT, FCP and LCP, which are scored. No extra requests are made to your site.

Learn more

Last updated October 28, 2026