Tags: web-dev concept

JavaScript Execution Cost

Date: 2026-08-16


Bytes are the cheap part. A JavaScript file has to be downloaded, parsed, compiled and executed — and on a mid-range phone the last three cost far more than the download, which is why “it’s only 200KB gzipped” understates the problem badly.


What it is

JavaScript execution cost is the total main-thread time a script consumes: parse, compile, execute, and the work it schedules afterwards. It’s distinct from transfer size, and it scales differently.

DOWNLOAD          proportional to compressed size, parallelisable,
                  helped by a fast connection

PARSE + COMPILE   proportional to UNCOMPRESSED size, on the main thread,
                  entirely determined by CPU

EXECUTE           proportional to what the code does, on the main thread

  ↑ a fast connection does nothing for the bottom two

Compression is the trap. A 200KB gzipped bundle is roughly 800KB of source to parse. Gzip helps the network and does nothing for the CPU.

Why the device matters more than the connection

A development machine parses and executes several times faster than the mid-range Android phone a large share of retail traffic arrives on. The same bundle that costs 150ms on your laptop can cost most of a second in someone’s hand.

That gap is why:

  • CPU throttling in DevTools is not optional. 4× or 6× approximates a real mid-range device; without it this cost is invisible in testing
  • Field data disagrees with your experience, consistently and in one direction — Field vs Lab Data
  • INP is the metric that catches it, because execution competes with every interaction on the same thread — Interaction to Next Paint, Long Tasks and Blocking

Where the time goes

Common contributors on a retail page:

  • Framework hydration. Attaching interactivity to server-rendered markup, usually all at once on load — Hydration
  • Third-party tags. Often the largest single block, and the least owned — Third-Party Scripts
  • Polyfills shipped to browsers that don’t need them
  • Duplicate dependencies — three copies of a date library because three packages each bundled their own
  • Large JSON parsed synchronously — JSON.parse on a big product feed is uninterruptible
  • Unused code. Most bundles ship substantially more than any single page uses — Code Splitting

Reducing it

In order of effect:

  1. Ship less. Code Splitting by route, and tree shaking to drop unused exports. The only change that reduces all three costs at once
  2. Audit dependencies. A date-formatting library at 70KB replaced with Intl.DateTimeFormat is free. Check for duplicates in the bundle analysis
  3. Defer what isn’t needed for first render. Load on interaction, on idle, or on intersection
  4. Break up long tasks with real yields — await Promise.resolve() does not yield, see Tasks and Microtasks
  5. Move computation off-thread to a worker where it doesn’t touch the DOM
  6. Reconsider the rendering strategy. Islands or server components ship interactivity only where it’s needed rather than hydrating the whole page — Rendering Strategies, Islands Architecture

Measuring it

  • DevTools → Performance, CPU throttled to 6×. The Summary pie separates scripting from rendering and painting
  • Bottom-Up view grouped by URL attributes main-thread time to specific files — the fastest way to find which vendor is expensive
  • Coverage panel shows what proportion of each bundle actually ran. 60–80% unused on a given page is normal and is the argument for splitting
  • Total Blocking Time in Lighthouse as a single lab number
  • Field INP with attribution for what real users experience — Real User Monitoring

The framing that lands

Stakeholders don’t act on kilobytes. They act on:

“This tag costs 240ms of main thread on a mid-range Android. During that time the page cannot respond to taps.”

Bytes are abstract; a frozen page during checkout isn’t. Pair it with the Interaction to Next Paint figure from field data and the conversation changes from “can we compress it” to “do we need it”.