Native ES modules quietly removed the reason bundlers existed in the first place — and development tooling has been lighter ever since.

Yellow lightning bolt icon over a blue-to-purple triangular shape, representing the Vite logo
The browser learned modules and the build step shrank Native ES modules let development servers skip bundling entirely, which reversed a decade of the toolchain getting heavier.Photo: Vitejs-logo · Wikimedia Commons

How bundling became load-bearing infrastructure

For most of the 2010s, the front-end build step only grew. Concatenation came first, because HTTP/1.1 made each separate file a round trip with real cost. Then bundlers added module resolution, tree-shaking, code splitting, and minification, until the toolchain between a developer's editor and a running browser was a substantial piece of infrastructure in its own right. webpack, released in 2012 and dominant by the middle of the decade, turned a text file into a dependency graph, resolved it, and emitted a single bundle that the browser could consume without knowing anything about where the code had come from. The browser's own module system was, for practical purposes, irrelevant — because browsers did not have one yet.

The specification that changed this was finalized as part of ECMAScript 2015, commonly called ES6. The import and export syntax introduced a standard module format into the language itself, after years of competing conventions — CommonJS in Node.js, AMD in RequireJS, UMD as an awkward bridge between them. TC39, the committee that maintains the ECMAScript standard, had been working through the design questions since at least 2013: how should modules be loaded, how should circular dependencies be handled, when should evaluation happen. The resulting semantics were static — import declarations cannot move inside conditions — which was unfamiliar to JavaScript developers accustomed to dynamic require(), but it made the dependency graph statically analyzable, which is what made tree-shaking tractable.

Browser vendors shipped native ES module support progressively between 2017 and 2018. Firefox, Chrome, Safari, and Edge all added it within roughly a year of each other, and MDN's compatibility data recorded the spread. What <script type="module"> gave a browser was real: it could fetch, parse, and execute a module graph itself, following import paths across the network, without any bundler having pre-processed the files. In production, with hundreds of modules and an HTTP/1.1 connection, that many round trips was still slow. But in development, on localhost, it was fast enough to be interesting.

Lifted from the piece

Chronology

  1. 2012webpack released
  2. 2015ES2015 (ES6) finalizes import/export syntax
  3. 2017–2018Major browsers ship native ES module support
  4. 2019Snowpack released; dev-time bundling begins to look optional
  5. 2020Vite released by Evan You
A conveyor of identical cardboard cartons under flat warehouse fluorescence
A text file turned into a pipeline Bundlers made the build step mandatory for ordinary front-end work, which put a toolchain between the source and what the browser receives.

The tool that noticed first

Snowpack, released in 2019, was built around the observation that the browser's native loader was perfectly adequate for development. Rather than rebundling on every change, it served files as ES modules, letting the browser handle the graph. A file change meant re-serving one file; the browser fetched only what changed. The rebuild cost went from seconds to near-zero, because there was no rebuild.

Vite, created by Evan You and first released in 2020, took the same premise and refined it. You had already built Vue.js and understood the friction of heavy toolchains; Vite used native ES modules for development while using Rollup for optimized production bundles, which meant the two environments had different characteristics but the development path was genuinely fast. Adoption was quick, partly because Vite's scope was clear and partly because the experience of starting a development server in milliseconds rather than tens of seconds was immediately legible as an improvement.

The mechanism that made both tools viable was the same: browsers had stopped being passive consumers of pre-processed bundles and had become capable of loading module graphs directly. This did not eliminate bundlers from production — the case for optimized, cache-friendly, minimized output at the network edge remained solid — but it decoupled production optimization from development experience. Those had been the same step for most of a decade, and separating them let tooling authors make different trade-offs in each direction.

Lifted from the piece

What the shift actually changed

  • Development servers: rebuild on change collapsed from seconds toward zero
  • Production builds: still bundled and optimized, but now a separate concern
  • HTTP/2 multiplexing: weakened the original argument for concatenating everything
  • Dependency graphs: static import syntax made them analyzable without execution

HTTP/2's multiplexing also changed the calculus for production. Where HTTP/1.1 imposed a hard cost on each additional file, HTTP/2 could serve many small files over a single connection without the same penalty, which made unbundled or lightly bundled production deployments more defensible than they had been when the bundler was invented. The case for concatenating everything into one file was an argument about HTTP/1.1 latency, and that argument weakened as the protocol changed. The build step did not vanish, but its justification narrowed, and the browser's own understanding of modules did most of the narrowing.

npm logo in white lowercase letters on a red rectangular background
A dependency became free to add and expensive to audit npm launched in 2010 and made installing someone else's code a single command. The cost moved from adding it to knowing what you had added.Photo: Npm-logo · Wikimedia Commons