Why Your JavaScript Might Be Working Against Your Users

JavaScript's footprint on the web keeps growing, and that growth comes with real costs for users on slower connections and older devices. Jeremy Wagner, author of Responsible JavaScript, joined the podcast to discuss how developers can think more critically about the code they ship — not just in terms of performance budgets, but in terms of who gets left behind.

The numbers tell a stark story. Per the HTTP Archive, the median amount of JavaScript downloaded by mobile devices has jumped nearly 58% in a year, from roughly 290 KB to almost 500 KB. At the 95th percentile — the experiences shipping the most script — that figure has ballooned from about 875 KB to 1.4 MB. Every dependency added via an npm install carries a price, and it's a price paid at load time and during runtime execution.

Performance Is About People, Not Just Metrics

Wagner's argument for responsible JavaScript isn't framed as a developer convenience — it's a user-first proposition. While web performance is often discussed in terms of business value, particularly in e-commerce, there's a human dimension that matters just as much. People relying on government assistance portals, online learning, or grocery delivery may be doing so from a budget Android phone or a rural connection. Those users are often the ones with the most at stake when a site fails to load or respond.

That's where the concept of the "long tail" of performance comes in, referencing an article by Tim Kadlec. Lab data from tools like Lighthouse shows how a page performs under controlled conditions, but field data reveals the real mix of devices and connections your audience uses. Around 28% of US users tracked by StatCounter are on iOS devices and about 21% on Android; globally, the split shifts to about 16% iOS and 41% Android. Android devices in particular represent a wide range of hardware quality, making them a central part of that long tail. Optimizing for the slowest experiences lifts the boat for everyone.

The Heat Problem No One Mentions

One consequence of heavy JavaScript that often flies under the radar is thermal throttling. Microprocessors generate heat under load, and unlike a laptop with heat sinks and fans, a phone has no active cooling. When a device gets too hot, it reduces its clock rate to cool down — which means it becomes less capable of handling the very work that overheated it in the first place. Parsing, compiling, and executing megabytes of JavaScript while other tabs and background processes run raises the odds of hitting that throttled state.

It's a negative feedback loop: more code leads to more processing, which leads to heat, which slows the device down further. The result is a user experience that degrades precisely at the moment the user needs it most.

Frameworks Have a Place — But Not As a Default

Wagner is clear that he isn't anti-framework. Frameworks serve a purpose, and they can be used to deliver a good user experience. What he questions is the lack of critical evaluation about their runtime costs. A button click that takes a second or two to respond, with third-party scripts competing for the main thread, is a sign that a framework's overhead isn't being scrutinized.

He points to the choice between React and Preact as a concrete example. Preact offers basically the same API with better runtime performance across devices. That single choice — a "thin" framework versus a heavier one — can determine whether a site works well for most users or only for some.

As for whether frameworks eventually enable you to ship less code through their abstractions, the evidence isn't encouraging. The framework itself is a fixed cost you can't optimize away. And historically, advances in processor speed and network bandwidth tend to get consumed by adding more JavaScript, not by delivering leaner experiences. Wagner doesn't see a way to automate your way out of this; it requires deliberate human decisions in the user's interest.

Websites vs. Web Apps: Know Which You're Building

The terminology we use matters. A website is typically a collection of documents, possibly with embedded islands of functionality. A web app suggests native-like behavior, driven by heavy client-side JavaScript. Both models are valid in the right context — Spotify works well as a web app, for instance — but the problem is reaching for the web app model by default.

When every project defaults to a client-side router and a heavy framework, you offload rendering work from the server to the client and risk excluding users who don't have the hardware to handle it. The two approaches often reflect different developer backgrounds: those who came up tinkering with the web versus those with formal computer science training. The difference in experience leads to different conclusions about how to build. What matters is critically evaluating what you're building and aligning the approach with what serves its users best.

Not a Binary Choice

The website-versus-web-app split isn't binary — it's a spectrum. Wagner's book comes down firmly on the website side as a good default when you're unsure. Building with minimal JavaScript and avoiding pushing work onto the client is the safer starting point. Technologies often dismissed as "boring" — like the PHP and Apache stack used on this very publication — deliver snappy experiences without deep knowledge of state management frameworks.

But there's room in the middle. The emerging "islands" architecture, where static content surrounds pockets of interactive functionality, represents a midpoint worth exploring. It's not a new idea: Flash embeds worked that way years ago, and many developers have been building this way without a name for it. As the web platform evolves, that middle ground may become the sweet spot for experiences that are both interactive and performant.

Progressive Enhancement Is Still Worth It — Sometimes

Progressive enhancement requires more work: you're effectively bifurcating development by delivering server-side minimum viable functionality and then layering JavaScript on top. Still, it remains a viable strategy.

The key is that not every interaction needs to be handled with synchronous navigation. A checkout page should have a server route with JavaScript sprinkled in sparingly to make things faster and more delightful, not to serve as the sole mechanism for adding items to a cart.

Core Web Vitals: Useful, Imperfect Instruments

Wagner sees the initial Core Web Vitals as a good attempt at quantifying the parts of user experience that matter. Metrics like Cumulative Layout Shift (CLS) and Largest Contentful Paint (LCP) push developers to think about experiences in new ways. Whoever has rage-tapped a button that moved knows why layout stability matters.

But the metrics aren't perfect. First Input Delay (FID) is useful but lacks context — delays can stem from focusing a form field, not just JavaScript execution. FID becomes more valuable when paired with data from the Long Tasks API or element timing. Similarly, CLS mostly measures layout shifts during load, even though shifts can happen any time. Measuring yourself against your own past performance, in the context of your specific site, remains a more practical approach than chasing perfect traffic-light scores.

He also notes that hydration can trigger layout shifts when client-side components render content the server didn't produce. It's a genuinely difficult problem, and he doesn't pretend there's a silver bullet.

Preloading High-Value Interactions

On code splitting and dynamic imports, Wagner's advice is to avoid premature optimization. If the effort to split code outweighs the benefit when performance isn't yet an issue, just load the functionality upfront.

But there's a smart middle ground for high-value interactions. Take form validation: HTML validation is functional but unstylable, so you might want a custom client-side approach. Loading that validation JavaScript upfront is wasteful, but you can preload it the moment a user focuses a form field. That gives the network time to pull it down before the dynamic import fires, hitting the cache. He cautions that you can't reliably do this on hover, since touch devices don't have fine pointers.

Respecting Data and Device Limits

Signals like the Save-Data request header have historically let sites detect a user's data-saving preference. When enabled in Chrome for Android, the header contains a single token — on — when the feature is active, and isn't sent otherwise. Servers can inspect it and decide, for instance, whether to send the scripts and assets for a carousel or render something lighter.

That signal is converging with a media query called prefers-reduced-data, in the same family as prefers-reduced-motion and prefers-color-scheme. Developers can match this client-side using matchMedia in JavaScript to conditionally skip non-essential functionality, like a video embed when text would suffice. Turning on the Save Data feature in Chrome for Android also makes the prefers-reduced-data query match, so both back-end and front-end responses are possible. Wagner has used this approach for a logging company client in rural Wisconsin, where the internet is slow, and cutting a decorative image carousel made a real difference.

Transpilation's Hidden Costs

Writing modern JavaScript and transpiling it for older browsers produces more code — it's almost unavoidable. Transforming even well-supported syntax like default parameters injects helper code to replicate the behavior. This isn't an abstract concern: working with an e-commerce client, Wagner reduced some page bundles by anywhere from 10% to as much as 30-40% by transpiling two sets of bundles — one for older browsers, one for evergreen browsers. That "differential serving" technique measurably improved user experience.

But differential serving is extra work, and whether it's justified depends on your audience. Matt Hobbs, who works on the NHS website, shared data showing a fair number of users still on Internet Explorer — not just IE11, but versions as far back as IE6. For government services, you're effectively required to serve everyone, because there's no telling where they're accessing the web from. For a regular web app aimed at a different demographic, it might make less sense.

Keeping an eye on your browser support thresholds matters. When usage of an old browser drops below a meaningful level, bumping your Browserslist configuration and shipping fewer transforms becomes worth evaluating. Wagner's book also explores pragmatic coding styles: relying on well-supported syntax like const and let — supported back to IE11 — puts less pressure on the transpiler, whereas targeting only newer browsers risks leaving a segment of users without functionality.

The Platform Is Doing More

CSS is stepping up to handle tasks that once demanded JavaScript. Content truncation, for instance, is achievable in pure CSS, yet libraries still get downloaded for it. Layout modes like CSS Grid eliminate the need for layout libraries — and abstractions reduce what you can do. Jen Simmons' experimental layout lab homepage is a case in point: achieving that design would be nearly impossible through a library, requiring CSS Grid directly, with zero JavaScript.

The web platform is advancing quickly, and leaning on native capabilities — in JavaScript itself, where APIs beat abstractions, and in HTML and CSS where they can replace scripting entirely — is one of the simplest ways to use JavaScript responsibly.

# 📰🗞️ Press

Show Notes

Links and resources mentioned in this episode:

Further Reading

Smashing Editorial