JavaScript Bytes Are Not Free
JavaScript tends to be the most expensive resource a page loads. The heavier your bundles, the more time users spend waiting on network transfer, decompression, parsing and compiling before the page becomes interactive. On mobile hardware, these costs compound and directly delay how soon someone can actually use your site.
Keeping JavaScript lean requires discipline across several fronts: what you send, how you send it, and how much of it the browser has to process at startup.
The Network Cost
The most obvious cost of JavaScript is the download. More bytes over the wire means slower delivery, especially when the user's effective connection type isn't what their device reports. Coffee-shop Wi-Fi connected to a cellular hotspot can behave like 2G speeds, regardless of what your analytics tell you.
You can reduce the network transfer cost of JavaScript with these techniques:
- Code-splitting: Break your JavaScript into critical and non-critical chunks using bundlers like webpack. Lazy-load anything that isn't needed for the initial render.
- Minification: Use UglifyJS to minify ES5 code, or babel-minify or uglify-es for ES2015+.
- Compression: Use gzip at minimum. Brotli (quality level ~q11) outperforms gzip on compression ratio; CertSimple saved 17% on compressed JS bytes and LinkedIn reduced load times by 4%.
- Remove unused code: UseDevTools coverage to spot dead code. Review your bundle dependencies with tools like webpack-bundle-analyzer. Consider tree-shaking, Closure Compiler's advanced optimizations, and library-trimming plugins like lodash-babel-plugin or webpack's ContextReplacementPlugin for large libraries such as Moment.js.
- Cache aggressively: Use HTTP caching with sensible
max-agelifetimes and validation tokens like ETag. A Service Worker can layer on network resilience and expose V8's code cache. Long-term caching with hashed filenames avoids refetching unchanged resources.
Parse and Compile Time
Once downloaded, the next bottleneck is parse and compile. In Chrome DevTools, this shows as part of the yellow "Scripting" time in the Performance panel; the Bottom-Up and Call Tree tabs expose exact timings.
Byte-for-byte, JavaScript is more expensive for the browser to process than an equivalently sized image or Web Font. Images still need decoding, but on average mobile hardware, JavaScript is far more likely to delay interactivity.
Context matters here — you're optimizing for average mobile phones, not flagships. Average users often have devices with slow CPUs and GPUs, no L2/L3 cache, and limited memory. A user with a fast fiber connection may still have a weak processor. Network capability and device capability don't always line up.
The difference is significant: parsing roughly 1MB of decompressed JavaScript costs 2–5x more time on average phones versus the fastest devices. On a real-world site like CNN.com, the high-end iPhone 8 takes about 4 seconds to parse and compile the page's JS; an average Moto G4 takes roughly 13 seconds. Testing only on the phone in your pocket gives you a dangerously optimistic picture — optimize for the hardware your users actually have.
Data from HTTP Archive across the top ~500K sites shows half of mobile sites take over 14 seconds to become interactive, with up to 4 seconds spent purely on parsing and compiling JavaScript. Removing non-critical JS from your pages reduces transmission time, CPU work and memory overhead — and gets users to an interactive page sooner.
Execution Time
Parse and compile aren't the only costs. JavaScript execution runs on the main thread, and long synchronous runs delay interaction. If a script executes for more than 50ms, time-to-interactive gets pushed out by the entire duration of downloading, compiling and executing that script.
Keep scripts in small chunks so they don't lock up the main thread, and audit what work is actually being done during execution.
Other Hidden Costs
JavaScript's impact doesn't stop at load time. Memory churn and garbage collection can cause visible jank as the browser pauses execution to reclaim memory. Avoid memory leaks and frequent GC pauses. At runtime, long-running JavaScript on the main thread makes pages feel unresponsive; chunking work with requestAnimationFrame() or requestIdleCallback() keeps the page responsive and helps your Interaction to Next Paint (INP) metric.
Patterns That Help
PRPL
PRPL (Push, Render, Pre-cache, Lazy-load) is a delivery pattern built around aggressive code-splitting and caching. Analyzing mobile sites with V8's Runtime Call Stats shows parse time consuming a significant portion of load on many popular pages. Sites that adopt PRPL — like Wego — maintain minimal parse times per route and get interactive quickly. Wego achieved this through code-splitting and strict performance budgets.
Progressive Bootstrapping
A common anti-pattern is to optimize for first paint with server-side rendering, then send down a large JavaScript bundle that "hydrates" the page. This usually means a larger HTML response, which pushes out interactivity, and leaves users in an uncanny valley where half the page isn't functional until all the script finishes processing.
Progressive Bootstrapping does better. Send down a minimally functional page — just the HTML, JS and CSS needed for the current route. As more resources arrive, the app lazy-loads and unlocks additional features incrementally, keeping the amount of code loaded proportional to what's actually visible.
Budgets and Discipline
Transmission size is the critical constraint on low-end networks; parse time is what matters on CPU-bound devices. Both need to stay low. Successful teams enforce strict performance budgets around JavaScript payloads and parse/compile cost. Alex Russell's "Can You Afford It?: Real-world Web Performance Budgets" provides a practical baseline for mobile-oriented targets.
Develop against representative hardware for your user base. Keep parse and compile times down, and keep your team honest with a performance budget that surfaces JavaScript costs before they silently compound.
Beyond parsing: delivery still matters
Getting JavaScript to the client efficiently is only the first half of the equation. Even with perfect code, delivery mechanisms can undo the gains. A few practical levers are worth considering.
Compression quality and cost
Brotli compression typically outperforms gzip, but the quality setting you choose has trade-offs. Cloudflare’s experiments with Brotli show real gains, but dynamic Brotli at a higher quality can delay the initial page render. That is a hidden cost: the compression happens at request time, and slower compression means slower first bytes. If you adopt Brotli, statically compressing assets during your build is generally the safer path; it avoids the runtime cost entirely and still gives you the same compressed payload.
The budget argument
Performance budgets are often framed as a discipline problem, but Alex Russell argues they are a reality check. In Can you afford it? Real-world performance budgets, he makes the case that engineering teams need to face the math head-on: given your users’ devices and networks, a framework download may not be a choice at all.
The same logic applies to the tools you pick. Kristofer Baxter’s tweet thread on evaluating web frameworks and libraries is a useful starting point if you want to measure before you commit.
Where to go deeper
If you are tuning a real application, start with the broader set of findings rather than one-off tricks. The following resources cover the range of issues touched on here, from rendering to bundling:
- Chrome Dev Summit 2017 — Modern Loading Best Practices — video walk-through of loading patterns.
- JavaScript Start-up Performance — background on the parse/compile costs that dominate early page life.
- Solving the web performance crisis — Nolan Lawson’s analysis of where the real bottlenecks live.
- Cloudflare’s Brotli results — background on compression trade-offs.
- Performance Futures: Bundling — Sam Saccone on where bundling is headed.



