Topics
v6.0.0
On this page · sections

Vite vs Webpack

Vite’s native ESM dev server vs webpack’s bundle-first dev graph is a standard senior loop question. Interviewers want architectural trade-offs, not “Vite is faster because Go.”

LayerViteWebpack
Dev serverNative ESM + on-demand transformIn-memory bundle of full graph
Prod buildRollup bundle + shakeChunk graph + splitChunks
StrengthFast default SPA DXFederation, custom loaders, enterprise graphs
Dep handlingesbuild optimizeDeps pre-bundleFull graph compile

Dev Server Architecture

Definition

Webpack dev builds a dependency graph into an in-memory bundle before serving — cold start and rebuild cost grow with app size.

Vite dev serves source as native ESM: the browser requests each module, Vite transforms only the requested file on the fly (TS, JSX, CSS), and uses esbuild for dep pre-bundling.

// vite.config.js (conceptual) // export default { // server: { port: 5173 }, // optimizeDeps: { include: ['react', 'react-dom'] }, // }; // Browser requests: // GET /src/main.jsx → Vite transforms JSX → ESM // GET /node_modules/.vite/deps/react.js → pre-bundled dep
Vite dev: Browser requests /src/App.jsx → transform single file → return ESM Webpack dev: Entry main.jsx → build full module graph → serve from memory bundle

optimizeDeps pre-bundles CJS/UMD deps to ESM with esbuild on first dev start — avoids hundreds of nested requests. Force re-run with --force after dep changes.

Vite transforms with esbuild/swc — not full Rollup in dev.

Interview: Vite dev cold start is roughly O(deps pre-bundle), not O(app modules).

Production Build Pipelines

Definition

Vite production runs Rollup — strong ESM tree-shaking, code splitting, and lean output. Plugins are mostly Rollup-compatible.

Webpack production is a mature chunk graph engine with Module Federation, granular loader chains, and deep customization.

// vite.config.js — production split // export default { // build: { // rollupOptions: { // output: { // manualChunks: { vendor: ['react', 'react-dom'] }, // }, // }, // minify: 'esbuild', // }, // }; // webpack.config.js // optimization: { // splitChunks: { // chunks: 'all', // cacheGroups: { vendor: { test: /node_modules/, name: 'vendors' } }, // }, // },

Compare fairly: Vite prod is Rollup — saying “Vite uses esbuild in production” is incomplete unless you mean minification only.

esbuild minify — faster builds, sometimes slightly larger output. terser — slower, finer dead-code patterns. Benchmark gzip on your app.

Interview: Vite prod is Rollup, not esbuild bundling — different code path from dev.

When to Choose Webpack

Definition

Choose webpack when you need Module Federation at scale, deeply custom loader chains (legacy templating, proprietary formats), or plugins with no Rollup equivalent.

// webpack Module Federation — host loads remote at runtime // new ModuleFederationPlugin({ // name: 'host', // remotes: { // checkout: 'checkout@https://cdn.example.com/remoteEntry.js', // }, // shared: { react: { singleton: true, requiredVersion: '^18.0.0' } }, // });
Need Federation + custom loaders + years of webpack config? → Stay webpack or migrate slice-by-slice Greenfield SPA with no Federation lock-in? → Vite default

Rspack — webpack-compatible API with Rust speed as migration path. @originjs/vite-plugin-federation exists for Vite — evaluate maturity against webpack’s battle-tested plugin.

Enterprises with years of webpack config — Federation remotes, DLL references, custom splitChunks for 50 entries — often migrate incrementally.

Interview: Module Federation depth and sunk config cost keep teams on webpack — not ideology.

HMR & Cold Start

Definition

Cold start: Vite serves index HTML immediately and compiles modules on first request. Webpack must compile the reachable graph before first useful response.

HMR: Vite invalidates an ESM boundary — often sub-100ms for a leaf component. Webpack HMR walks the module graph via module.hot.accept — mis-accepted boundaries force full reload.

'use client'; import { useState } from 'react'; export function HotDemo() { const [n, setN] = useState(0); return ( <button type="button" onClick={() => setN((v) => v + 1)}> {n} </button> ); } // Edit JSX — state preserved if export is React component

Vite HMR uses import.meta.hot. Webpack uses module.hot.accept. Both support React Fast Refresh with correct plugins.

Webpack cache: { type: 'filesystem' } cuts rebuild time. Vite pre-bundle cache lives in .vite/deps.

Full page reload triggers: HTML entry changes, env file changes, optimizeDeps rebuild, plugins that cannot HMR.

Interview: HMR update latency is O(affected subtree) — Vite’s ESM boundaries often yield smaller subtrees for leaf edits.

Migration Strategy

Definition

Migrate incrementally or vertical slice: start with a greenfield package on Vite, then port apps.

Breakage categories:

  • require() / CommonJS in source
  • process.env vs import.meta.env
  • SVG/asset imports needing ?url or plugins
  • Jest mocking import.meta
  • Custom webpack loaders without Rollup equivalents
import { defineConfig, loadEnv } from 'vite'; export default defineConfig(({ mode }) => { const env = loadEnv(mode, process.cwd(), ''); return { define: { 'process.env.NODE_ENV': JSON.stringify(mode), 'process.env.LEGACY_API': JSON.stringify(env.LEGACY_API), }, resolve: { alias: { '@': '/src' } }, }; });

Staff approach: ADR with rollback plan — keep webpack CI job for one release train. Measure build time, bundle size, and DX survey — not only startup anecdotes.

Monorepo: per-package tooling via Nx/Turborepo during transition. vite-plugin-legacy for older browser targets.

Interview: Top breakage categories are env variables and asset imports — not “just swap config.”

Senior Interview Summary

Dev architecture

Vite = unbundled ESM + on-demand transform. Webpack dev = bundled graph in memory.

Production

Vite prod = Rollup + shake. Webpack prod = chunk graph + splitChunks + Federation.

When webpack wins

Module Federation, custom loaders, sunk enterprise config, unified dev+prod graph model.

HMR

Vite import.meta.hot · webpack module.hot.accept · both need correct Fast Refresh boundaries.

Migration

Incremental slices · env + asset breaks · ADR with rollback · keep Federation on webpack until strategy settled.

Best mental model

Dev speed → request-scoped transform vs full graph compile Prod output → Rollup lean default vs webpack graph features Decision → greenfield vs Federation lock-in vs migration cost

Practical rules

Greenfield SPA → Vite default Federation at scale → webpack (or evaluate Rspack) Dual tooling in monorepo → acceptable during transition Prod validation → separate from dev parity

What to say in interviews

Draw the line dev = ESM vs bundle and prod = Rollup vs webpack graph. Name Module Federation as the main webpack differentiator. For migration, list env vars and asset imports as top breakage categories — interviewers hear “zero config” too often.

Last updated on