Why migrate from Webpack to Vite?
The pitch: Vite uses the browser's native ES modules during
development (no bundling) and esbuild for prebundling
dependencies. Cold dev-server start drops from "minutes" to
"sub-second"; HMR updates land in <100ms regardless of project
size. The production build switches to rollup, which produces
output competitive with webpack.
For a typical React app moving from webpack 5, the wins are:
- Dev start: 30s -> 0.5s. The biggest single ergonomics win in modern frontend tooling.
- HMR: 2-5s -> <100ms. No more "save and lose your edit cursor while waiting".
- Config surface: 200 lines -> 20 lines. Vite's defaults cover what most apps need; bring-your-own-loader is rare.
- Production bundle: roughly the same. Both use tree-shaking, code-splitting, and minification. Don't migrate for production bundle size -- migrate for dev-loop speed.
The trade: less mature plugin ecosystem. webpack has a plugin for everything; Vite has plugins for the common 90% and sometimes a webpack-specific tool (Module Federation, custom loaders) won't have a direct Vite equivalent.
Before you start
- Pin your current webpack config to git. You'll reference it during the move and want to be able to roll back.
- Inventory your loaders + plugins. List every entry in
module.rulesandplugins. Each needs a Vite equivalent or removal decision. - Check Node version. Vite 5+ requires Node 18+.
Step-by-step migration
1. Install Vite + your framework plugin
npm install --save-dev vite
# Pick one based on your framework:
npm install --save-dev @vitejs/plugin-react # React
npm install --save-dev @vitejs/plugin-vue # Vue
npm install --save-dev @sveltejs/vite-plugin-svelte
2. Create vite.config.js
Minimal React example:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'node:path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
server: {
port: 3000,
},
});
Port your webpack resolve.alias here. Port your webpack
devServer.proxy to Vite's server.proxy.
3. Move index.html to project root
webpack's HtmlWebpackPlugin renders the index template; Vite
just serves an index.html from the project root that imports
your entry script directly:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<title>My App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Move whatever was in your webpack template's <head> here.
4. Update package.json scripts
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
Drop webpack-dev-server, webpack-cli, and the loader/plugin
devDependencies in the same commit -- otherwise they sit around
adding to install time.
5. Replace webpack-specific imports
These webpack idioms don't work in Vite + need replacements:
| webpack | Vite |
|---|---|
require.context('./svg', false, /\.svg$/) |
import.meta.glob('./svg/*.svg') |
require('./img.png') |
new URL('./img.png', import.meta.url) |
process.env.MY_VAR |
import.meta.env.MY_VAR (must be VITE_-prefixed) |
dynamic-import-string-template |
plain import() with literal paths |
Search-and-replace require.context and process.env first --
they're the most common sources of "works in dev, breaks in
build" issues post-migration.
6. CSS / preprocessor handling
Vite supports .css, .scss, .sass, .less, .styl out of
the box -- just npm install the preprocessor and import.
CSS Modules work via .module.css filename convention.
If you used MiniCssExtractPlugin, it's gone -- Vite does this
in production by default.
7. Run dev + production builds
npm run dev # should start in <1s
npm run build # produces dist/
npm run preview # serves dist/ for spot-check
Walk through the app. Most issues surface as one of:
- Missing alias (add to
resolve.alias). - Missing env var (rename
MY_VAR->VITE_MY_VAR+ update references). require()in a third-party CommonJS dep (Vite'soptimizeDeps.includehelps).
What doesn't migrate automatically
- Module Federation. Webpack's headline feature for
micro-frontends. Vite has
originjs/vite-plugin-federationbut it's not 1:1. - Custom webpack loaders. Audit
module.rulesfor anything beyond the common loaders -- Vite plugins differ in shape. - Service worker config (Workbox webpack plugin). Use
vite-plugin-pwainstead. - MDX with custom remark/rehype plugins.
@mdx-js/rollupis the equivalent.
Common pitfalls
- Env vars stop appearing. Vite requires
VITE_prefix on env vars (the prefix is configurable viaenvPrefix). Aprocess.env.API_URLreference will silently renderundefinedin the browser bundle until renamed. - CommonJS dependencies break in dev. Some third-party libs
ship CJS only. Add them to
optimizeDeps.includeso Vite pre-bundles them as ESM. - Tree-shaking changes break a side-effecty import. Vite's
rollup is more aggressive than webpack's terser. If a
side-effect-only import (
import 'side-effect-css';) gets shaken out, mark its package'ssideEffects: trueinpackage.json. - CSS import order. webpack and rollup order CSS imports
differently. If your design depends on cascade order, audit the
built
dist/assets/index.cssfor unexpected reordering.
How BeaverCheck measures the difference
We've audited 901 sites built with webpack and 24 built with Vite. Average Lighthouse performance: webpack-built sites 38 / 100, Vite-built sites 49 / 100, a difference of +11 points.
Production-bundle quality is similar between the two; the gap that BeaverCheck measures mostly reflects what stack each site is ON, not the bundler itself. Run a free audit to see your current bundle's effect on Core Web Vitals.
Further reading
- Vite migration from webpack
- Awesome Vite -- plugin equivalents
- Compare both: webpack vs Vite