JavaScript's Bloat Problem Is Worse Than You Think
Why your web app downloads millions of lines of unnecessary JavaScript—and who's responsible for the mess we're in.
Written by AI. Bob Reynolds
March 30, 2026

Photo: Theo - t3․gg / YouTube
A package called shebang-regex gets downloaded 133 million times per week. It contains two lines of code: a regular expression and an export statement. Another package, cli-boxes, is just a JSON file defining box-drawing characters. It pulls 40 million downloads weekly. Then there's slash, which replaces backslashes with forward slashes in file paths—one line of actual logic, 96 million weekly downloads.
Welcome to the JavaScript dependency crisis, where the typical web application downloads forests of code that shouldn't exist.
James Garbutt, a maintainer deeply embedded in the Node.js ecosystem, recently published an analysis identifying what he calls the three pillars of JavaScript bloat. His post landed in a community already exhausted by bundle sizes but perhaps not fully aware of why the problem persists. The video walkthrough of his findings, presented by developer Theo Browne, adds visceral frustration to Garbutt's technical breakdown.
The numbers are worse than most developers realize.
The Legacy Tax
The first pillar of bloat comes from supporting ancient JavaScript engines. Somewhere in the world, applications still run on ES3—the JavaScript specification from 1999. These are Internet Explorer 6 environments, early Node versions, systems that predate Array.forEach and Object.keys.
There's actually a company, HeroDevs, that specializes in this work. They maintain forks of Node.js 0.8. They backport security fixes to abandoned codebases. When a package maintainer wants to support these environments, they can't use any modern JavaScript features. Everything needs polyfills. Everything needs fallbacks.
"For those unfortunate souls who are still running old engines, they need to reimplement everything themselves or be provided with polyfills," Garbutt writes. Fair enough. The problem is that these accommodations aren't isolated to the people who need them.
Consider the package is-string. It checks if a value is a string. Simple enough. But is-string depends on has-tostringtag, which depends on has-symbols. It also depends on call-bind, which depends on get-intrinsic, which has its own sprawling dependency tree. All of this to check if something is a string, because someone somewhere might be running JavaScript older than most college students.
There's more. Some packages protect against "global namespace mutation"—the theoretical scenario where someone redefines Array.map and breaks everything. Node.js itself guards against this with "primordials," keeping pristine copies of built-in objects. Some maintainers believe user-space packages should do the same. Hence math-intrinsics, a package that does nothing but re-export JavaScript's built-in math functions so they can't be tampered with.
Then there are "realms"—the technical term for execution contexts like iframes. If you create an array in an iframe, it's not the same Array constructor as the parent page. So instanceof Array fails. Packages that need to work across realms can't use normal type checking. They need special detection code.
All three of these concerns—legacy engines, namespace protection, cross-realm compatibility—affect a tiny minority of developers. "The tiny group of people who actually need this stuff should be the ones seeking out special packages for it," Garbutt argues. "Instead, it is reversed and we all pay the cost."
When Good Intentions Backfire
The flashpoint came when a maintainer took over the axe-core accessibility testing package. The new steward focused on backward compatibility, adding 60 dependencies to support ancient Node versions. One of those dependencies, deep-equal, added 50 sub-dependencies by itself. The package went from nearly zero dependencies to a sprawling tree.
SvelteKit users saw their dependency count nearly double overnight. People thought it was a supply chain attack. It wasn't—just someone optimizing for a use case that represented perhaps one percent of users while imposing costs on the other ninety-nine.
"It's kind of crazy that a minor version bump of something that you're already using can suddenly double the number of dependencies that you have in your codebase," Browne notes in the video. The Svelte team was, understandably, furious.
Atomic Absurdity
The second pillar of bloat comes from what Garbutt calls "atomic architecture"—breaking code into the smallest possible reusable pieces. The Unix philosophy taken to a ridiculous extreme.
The package arrify converts a value to an array. If it's already an array, return it. If not, wrap it. That's the entire package. One line of meaningful code. Thirty-two million weekly downloads.
The package path-key checks if the platform is Windows and returns the appropriate environment variable name for PATH. Four kilobytes of JavaScript. One hundred fifty-eight million weekly downloads.
This isn't malice. It's a philosophical position about code reuse that made sense in theory but created a dependency catastrophe in practice. When you need to include a JSON file of box-drawing characters as an npm package instead of just... including the JSON file, something has gone fundamentally wrong.
"By splitting code up to this atomic level, the theory is that we can then create higher level packages simply by joining the dots," Garbutt writes. "I get this. I am a big fan of the Unix philosophy myself. But Unix philosophy should not be at a line of code level."
Who Benefits?
The frustration in Browne's video is palpable, but it's worth asking: why does this persist? The answer involves competing incentives that never quite align.
Package maintainers want broad compatibility. HeroDevs employees need to support ancient systems—that's literally their job. Developers like reusable code. npm makes it trivially easy to publish and depend on packages. None of these are bad things individually.
But the ecosystem lacks a forcing function to push legacy concerns out of the default path. There's no cost to adding dependencies. Bundle size shows up in metrics, but it's unclear which packages are the problem. Developers inherit dependency trees they never chose.
"These layers of niche compatibility somehow made their way into the hot path of everyday packages," Garbutt observes. The result is that everyone downloads support for Internet Explorer 6 whether they need it or not.
Garbutt's E18E initiative aims to address this through community cleanup—identifying redundant packages, removing outdated dependencies, modernizing where possible. It's unglamorous work. It's also necessary if JavaScript is going to dig itself out.
The bloat didn't appear overnight. It accumulated package by package, dependency by dependency, well-intentioned decision by well-intentioned decision. There's no villain here—just an ecosystem that optimized for the wrong things and is now paying the price in megabytes.
Bob Reynolds is Senior Technology Correspondent for Buzzrag.
Watch the Original Video
Saving the web from Javascript bloat
Theo - t3․gg
33m 24sAbout This Source
Theo - t3․gg
Theo - t3.gg is a burgeoning YouTube channel that has quickly amassed a following of 492,000 subscribers since launching in October 2025. Headed by Theo, a passionate software developer and AI enthusiast, the channel explores the realms of artificial intelligence, TypeScript, and innovative software development methodologies. Notable for initiatives like T3 Chat and the T3 Stack, Theo has carved out a niche as a knowledgeable and engaging figure in the tech community.
Read full source profileMore Like This
Vite Plus Goes Open Source—With Sharp Edges Still Showing
Vite Plus launched as open source alpha, promising unified tooling. Early testing reveals impressive speed alongside design choices that may frustrate power users.
Anthropic DMCA'd a Developer for Changing One Word
A developer received his first DMCA strike for modifying a single line in Anthropic's public repository. The story reveals how copyright law works on GitHub.
Cursor's Composer 2 Drama: What Really Powers the Model
Cursor's impressive new Composer 2 model turns out to be built on Moonshot AI's Kimi—raising questions about disclosure, licensing, and transparency.
How Node.js Cut Memory Usage in Half With One Change
A year-long collaboration between Cloudflare and the V8 team enabled pointer compression in Node.js, halving memory usage with minimal performance cost.