Vite, esbuild, and Leaving Webpack Behind
· Jerwin Arnado
Archive note: this is a backdated post, written years later while rebuilding this site. It’s dated to the moment it covers, but the hindsight is real.
Laravel made it official this summer: new Laravel apps ship with Vite instead of Laravel Mix. After years of Mix (and the webpack config it lovingly hid from us), the framework’s frontend story now runs on the tool I flagged as a side-note in the Vue 3 post — “keep an eye on this one” has graduated to “this is the default.”
Having migrated work projects, the verdict up front: the upgrade is real, the migration is mild, and I don’t miss webpack even slightly.
Why Vite is actually faster (not just “faster”)
The trick is architectural, not incremental. Webpack’s dev model: bundle everything up front, serve the bundle, rebuild affected chunks on change. As apps grow, startup and rebuild time grow with them — the all-too-familiar “save, alt-tab, wait, refresh” loop.
Vite’s dev model inverts it:
- No bundle in development. The browser loads your modules as native ES modules — the thing browsers have supported for years. The dev server transforms files on demand, as the browser requests them. Startup time stops being a function of app size; a 50-module app and a 5,000-module app both start in milliseconds.
- esbuild for the grunt work. Dependencies get pre-bundled once by esbuild — a bundler written in Go that’s 10–100x faster than JS-based tooling at this job. Your
node_moduleschurn becomes a one-time cost, cached until dependencies change. - HMR that stays instant. Hot module replacement swaps the changed module over native ESM, so update speed is independent of project size. Vue and React component state survives edits. Once you’ve felt sub-100ms feedback, the old loop reads as broken.
- Production still bundles — via Rollup — because hundreds of HTTP requests for modules is the wrong shape to ship. Dev and prod use different strategies, each optimal for its job. That asymmetry is the design.
Laravel specifics
The integration is pleasantly thin: a vite.config.js with the official laravel-vite-plugin, the @vite Blade directive replacing mix() helpers, and environment-aware magic (dev server URLs in development, hashed build manifests in production). Migration from Mix on the projects I’ve touched amounted to:
- Swap
laravel-mixforvite+ plugin; translatewebpack.mix.js(usually 10 lines of real intent buried in boilerplate) intovite.config.js. mix()→@vitein layouts;require()→importwhere legacy code lingered.- Move env vars from
MIX_*toVITE_*prefixes. - The one real gotcha: anything relying on webpack-specific behavior —
require.context, magic comments — needs its Vite equivalent (import.meta.globcovers most of it, and honestly reads better).
Half a day per project, most of it testing. Mix served faithfully — it made webpack humane, which was the hard job of its era. But the era is over.
The bigger pattern
Tooling speed is a compounding interest account. Per save, webpack-vs-Vite is seconds. Per hundreds of saves a day, every day, across a team — it’s the difference between staying in flow and context-switching to your phone while the bundler thinks. The trend it represents pleases me even more: dev tooling rewritten in fast compiled languages (esbuild in Go, the new wave in Rust) doing the heavy lifting, while configuration shrinks toward zero. The frontend build, after a decade of being a hobby unto itself, is becoming what it always should have been: invisible.