How to Migrate from Webpack to Vite
How do I migrate from Webpack to Vite?
TL;DR
- Bottom line: Replace
webpack.config.jswithvite.config.js, swap Webpack loaders for Vite plugins or built-in features, convertprocess.envtoimport.meta.env, move yourindex.htmlto the project root, and convert any CommonJSrequire()calls to ESMimportstatements. - Key tool/command:
npm install vite @vitejs/plugin-react --save-dev && npx vite - Watch out for: CommonJS
require()calls and dynamicrequire.context()patterns that Vite cannot handle natively -- these must be converted toimport.meta.glob()or ESM imports. - Works with: Vite 6.x/7.x, React 18+/19, Vue 2/3, Svelte, Node.js 20.19+ (Vite 7) or 18+ (Vite 6), any project currently using Webpack 4 or 5.
Constraints
- Vite 7 and Vite 8 require Node.js 20.19+ or 22.12+ — Node 18 reached EOL and is no longer supported. If the project must stay on Node 18, pin Vite 6.x. [src8, src9]
- All source code must use ESM (
import/export) — CommonJSrequire()is not supported in Vite's module graph. Convert before migrating. [src3, src6] - Environment variables exposed to client code must use the
VITE_prefix — never shimprocess.envglobally as it leaks all server env vars to the browser bundle. [src2, src4] index.htmlmust be at the project root (not inpublic/orsrc/) — Vite uses it as the entry point instead ofhtml-webpack-plugin. [src4, src5]- Vite 8 (released 2026-03-12) replaces esbuild+Rollup with a single Rust bundler (Rolldown) and uses Oxc for JS transforms.
build.rollupOptionsbecomesbuild.rolldownOptions,optimizeDeps.esbuildOptionsbecomesoptimizeDeps.rolldownOptions, and theesbuildconfig option becomesoxc. A compat layer auto-converts during the transition but will be removed. [src9] - Webpack Module Federation has no first-party Vite equivalent — use
@module-federation/vite(preferred in 2026, shares the@module-federation/runtime) or@originjs/vite-plugin-federation. Both still have dev-server rough edges. [src7, src10]
Quick Reference
| Webpack Config/Feature | Vite Equivalent | Example |
|---|---|---|
entry (string) | build.rolldownOptions.input (Vite 8) / build.rollupOptions.input (Vite ≤7) | input: { main: 'src/main.js' } |
output.path | build.outDir | build: { outDir: 'dist' } |
output.publicPath | base | base: '/my-app/' |
output.filename | build.rolldownOptions.output.entryFileNames (Vite 8) | entryFileNames: 'assets/[name]-[hash].js' |
output.chunkFilename | build.rolldownOptions.output.chunkFileNames (Vite 8) | chunkFileNames: 'assets/[name]-[hash].js' |
devServer.port | server.port | server: { port: 3000 } |
devServer.proxy | server.proxy | server: { proxy: { '/api': 'http://localhost:8080' } } |
devServer.https | server.https | server: { https: true } (or @vitejs/plugin-basic-ssl) |
resolve.alias | resolve.alias | resolve: { alias: { '@': '/src' } } |
resolve.extensions | resolve.extensions | resolve: { extensions: ['.js', '.ts', '.jsx'] } |
DefinePlugin | define | define: { __APP_VERSION__: JSON.stringify('1.0') } |
process.env.NODE_ENV | import.meta.env.MODE | Automatic in Vite (development/production) |
process.env.REACT_APP_* | import.meta.env.VITE_* | Prefix env vars with VITE_ in .env files |
copy-webpack-plugin | publicDir (default: public/) | Static files in public/ are copied to dist/ automatically |
css-loader + style-loader | Built-in CSS support | import './style.css' works out of the box |
sass-loader | Built-in (install sass) | npm install sass -D — no loader config needed |
postcss-loader | Built-in (auto-detects postcss.config.js) | Just keep your PostCSS config file |
file-loader / url-loader | Built-in asset handling | import logo from './logo.png' returns URL |
html-webpack-plugin | index.html at project root | Move index.html to root, add <script type="module" src="/src/main.js"> |
MiniCssExtractPlugin | Built-in CSS code splitting | Automatic in production builds |
HotModuleReplacementPlugin | Built-in HMR | Automatic with framework plugins |
require.context() | import.meta.glob() | const modules = import.meta.glob('./modules/*.js') |
require() (dynamic) | new URL('./path', import.meta.url).href | For asset URLs in ESM context |
babel-loader | Built-in Oxc (Vite 8) / esbuild (Vite ≤7) | Oxc handles JSX/TSX transformation in Vite 8; @vitejs/plugin-react v6 drops Babel entirely |
ts-loader | Built-in Oxc (Vite 8) / esbuild (Vite ≤7) | No loader needed; native TS transpilation |
webpack-bundle-analyzer | rollup-plugin-visualizer | import { visualizer } from 'rollup-plugin-visualizer' |
ProvidePlugin | vite-plugin-inject or manual imports | Replace global shimming with explicit imports |
splitVendorChunkPlugin (deprecated Vite 7) | build.rolldownOptions.output.manualChunks (Vite 8) | Define manual chunk splitting (object form removed in Vite 8 — use function form) |
tsconfig.compilerOptions.paths | Built-in tsconfig paths (Vite 8) | Vite 8 reads tsconfig paths natively; pre-Vite 8 needs vite-tsconfig-paths plugin |
ModuleFederationPlugin | @module-federation/vite | federation({ name: 'host', remotes: { app1: '...' } }) — preferred over @originjs/vite-plugin-federation in 2026 |
Decision Tree
START
├── Is the project using CommonJS (require/module.exports) extensively?
│ ├── YES → First convert to ESM imports/exports, then proceed with migration
│ └── NO ↓
├── Is the project using Vue CLI (vue-cli-service)?
│ ├── YES → Use `npx @originjs/webpack-to-vite` automated tool, then fix remaining issues
│ └── NO ↓
├── Is the project using Create React App (react-scripts)?
│ ├── YES → See CRA to Vite migration (simpler path with fewer config decisions)
│ └── NO ↓
├── Does the project rely on Webpack-specific APIs (require.context, module.hot)?
│ ├── YES → Replace require.context with import.meta.glob, remove module.hot (Vite HMR is automatic)
│ └── NO ↓
├── Does the project use Webpack Module Federation?
│ ├── YES → Evaluate vite-plugin-federation or keep Webpack for federated entry points
│ └── NO ↓
├── Does the project have complex Webpack plugins with no Vite equivalent?
│ ├── YES → Check for Rollup-compatible plugin or write a custom Vite plugin (Rollup plugin API)
│ └── NO ↓
├── Is this a React project?
│ ├── YES → Install @vitejs/plugin-react, create vite.config.js, update scripts
│ └── NO ↓
├── Is this a Vue project?
│ ├── YES → Install @vitejs/plugin-vue, create vite.config.js, update scripts
│ └── NO ↓
└── DEFAULT → Install vite, create minimal vite.config.js, move index.html to root, update scripts
Step-by-Step Guide
1. Audit your Webpack configuration
Catalog all loaders, plugins, and custom configuration in your webpack.config.js. Identify which ones have built-in Vite equivalents (most CSS/asset loaders), which need Vite plugins, and which require manual conversion. [src3, src7]
# List all Webpack dependencies in your project
npm ls | grep -i webpack
npm ls | grep -i loader
npm ls | grep -i plugin
# Count configuration complexity
cat webpack.config.js | wc -l
Verify: You have a complete list of every loader and plugin that needs a Vite replacement or removal.
2. Install Vite and framework plugin
Install Vite and the official plugin for your framework. Keep Webpack installed during migration so you can compare builds. [src1, src5]
# For React projects
npm install vite @vitejs/plugin-react --save-dev
# For Vue 3 projects
npm install vite @vitejs/plugin-vue --save-dev
# For Vue 2 projects
npm install vite vite-plugin-vue2 --save-dev
# For Svelte projects
npm install vite @sveltejs/vite-plugin-svelte --save-dev
Verify: npx vite --version prints the installed Vite version without errors.
3. Create vite.config.js from your webpack.config.js
Translate your Webpack configuration to the Vite equivalent using the Quick Reference table above. [src1, src3, src4]
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
},
},
},
build: {
outDir: 'dist',
sourcemap: true,
},
});
Verify: npx vite build --config vite.config.js does not throw configuration errors.
4. Move index.html to project root and update it
Vite uses index.html at the project root as the entry point (unlike Webpack's html-webpack-plugin). Add a <script type="module"> tag pointing to your entry file. [src4, src5]
<!-- Move from public/index.html to project root -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>My App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
Verify: The file is at the project root (same level as package.json), and the src attribute points to your actual entry file.
5. Convert environment variables
Rename all REACT_APP_* or custom process.env.* variables to VITE_* prefix. Replace all process.env.VARIABLE references with import.meta.env.VITE_VARIABLE. [src2, src4]
// BEFORE (Webpack)
const apiUrl = process.env.REACT_APP_API_URL;
const isProd = process.env.NODE_ENV === 'production';
// AFTER (Vite)
const apiUrl = import.meta.env.VITE_API_URL;
const isProd = import.meta.env.PROD; // Built-in boolean
Verify: grep -rn "process\.env\." src/ returns zero results.
6. Convert CommonJS require() to ESM imports
Vite requires ESM syntax. Replace all require() calls with import statements and module.exports with export. [src3, src6]
// BEFORE (CommonJS)
const React = require('react');
const logo = require('./logo.png');
const modules = require.context('./modules', true, /\.js$/);
// AFTER (ESM)
import React from 'react';
import logo from './logo.png';
const modules = import.meta.glob('./modules/**/*.js');
Verify: grep -rn "require(" src/ returns zero results.
7. Rename .jsx/.tsx files if needed
Vite is stricter than Webpack about file extensions. Any file that contains JSX syntax must have a .jsx or .tsx extension -- .js files with JSX will fail. [src2, src7]
# Find .js files containing JSX syntax
grep -rln "<[A-Z][a-zA-Z]*" --include='*.js' src/ | head -20
# Rename them
git mv src/components/App.js src/components/App.jsx
Verify: npx vite build does not throw JSX-related parse errors.
8. Update package.json scripts and remove Webpack
Replace Webpack scripts with Vite commands and uninstall all Webpack dependencies. [src5, src7]
# Remove Webpack and all related packages
npm uninstall webpack webpack-cli webpack-dev-server \
html-webpack-plugin mini-css-extract-plugin css-loader \
style-loader file-loader url-loader babel-loader ts-loader \
sass-loader postcss-loader webpack-bundle-analyzer \
copy-webpack-plugin
# Remove config files
rm webpack.config.js webpack.dev.js webpack.prod.js
Verify: npm run dev starts the Vite dev server; npm run build produces output in dist/. Application loads without console errors.
Code Examples
JavaScript/React: Complete vite.config.js replacing a typical webpack.config.js
Full script: javascript-react-complete-vite-config-js-replacing.js (67 lines)
// Input: A React project with Webpack using aliases, proxy, env vars, SCSS, and SVG
// Output: Equivalent Vite configuration
import { defineConfig, loadEnv } from 'vite';
import react from '@vitejs/plugin-react';
import svgr from 'vite-plugin-svgr';
import path from 'path';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
return {
plugins: [
react(), // Replaces babel-loader + @babel/preset-react
svgr(), // Replaces @svgr/webpack
],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
},
},
server: {
port: 3000,
open: true,
proxy: {
'/api': {
target: env.VITE_API_URL || 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/styles/variables" as *;`,
},
},
modules: {
localsConvention: 'camelCaseOnly',
},
},
build: {
outDir: 'dist',
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
router: ['react-router-dom'],
},
},
},
},
};
});
TypeScript: Migrating Webpack require.context to Vite import.meta.glob
Full script: typescript-migrating-webpack-require-context-to-vi.ts (37 lines)
// Input: Webpack project using require.context for dynamic module loading
// Output: Equivalent Vite pattern using import.meta.glob
// BEFORE: Webpack require.context (auto-import all route modules)
// const routeModules = require.context('./routes', true, /\.tsx$/);
// AFTER: Vite import.meta.glob (eager loading equivalent)
const routeModules = import.meta.glob<{ default: React.ComponentType }>(
'./routes/**/*.tsx',
{ eager: true }
);
const routes = Object.entries(routeModules).map(([path, module]) => ({
path: path
.replace('./routes', '')
.replace('.tsx', '')
.replace('/index', '/'),
component: module.default,
}));
// AFTER: Vite import.meta.glob (lazy loading for code splitting)
const lazyRouteModules = import.meta.glob<{ default: React.ComponentType }>(
'./routes/**/*.tsx'
);
const lazyRoutes = Object.entries(lazyRouteModules).map(([path, loader]) => ({
path: path.replace('./routes', '').replace('.tsx', '').replace('/index', '/'),
lazy: async () => {
const module = await loader();
return { Component: module.default };
},
}));
Node.js: Migration script to automate environment variable renaming
Full script: node-js-migration-script-to-automate-environment-v.js (52 lines)
// Input: A project with REACT_APP_* env vars in .env files and process.env.* in source
// Output: Converted VITE_* env vars and import.meta.env.* references
import { readFileSync, writeFileSync, readdirSync, statSync } from 'fs';
import { join, extname } from 'path';
// Step 1: Rename env vars in .env files
function migrateEnvFiles(rootDir) {
const envFiles = ['.env', '.env.local', '.env.development', '.env.production'];
for (const file of envFiles) {
const filePath = join(rootDir, file);
try {
let content = readFileSync(filePath, 'utf-8');
content = content.replace(/^REACT_APP_/gm, 'VITE_');
writeFileSync(filePath, content);
console.log(`Migrated: ${file}`);
} catch { /* file doesn't exist, skip */ }
}
}
// Step 2: Replace process.env references in source files
function migrateSourceFiles(dir) {
for (const entry of readdirSync(dir)) {
const fullPath = join(dir, entry);
if (entry === 'node_modules' || entry === 'dist') continue;
if (statSync(fullPath).isDirectory()) {
migrateSourceFiles(fullPath);
continue;
}
if (!['.js', '.jsx', '.ts', '.tsx'].includes(extname(fullPath))) continue;
let content = readFileSync(fullPath, 'utf-8');
let modified = false;
content = content.replace(/process\.env\.REACT_APP_(\w+)/g, (_, name) => {
modified = true;
return `import.meta.env.VITE_${name}`;
});
content = content.replace(
/process\.env\.NODE_ENV\s*===?\s*['"]production['"]/g,
() => { modified = true; return 'import.meta.env.PROD'; }
);
if (modified) {
writeFileSync(fullPath, content);
console.log(`Updated: ${fullPath}`);
}
}
}
migrateEnvFiles('.');
migrateSourceFiles('./src');
Anti-Patterns
Wrong: Keeping process.env references and shimming with define
// ❌ BAD — Shimming process.env globally to avoid refactoring
// vite.config.js
export default defineConfig({
define: {
'process.env': process.env, // Leaks ALL env vars to client bundle!
},
});
Correct: Use Vite's import.meta.env with VITE_ prefix
// ✅ GOOD — Only VITE_-prefixed vars are exposed to client code
// .env
// VITE_API_URL=https://api.example.com
// SECRET_KEY=abc123 ← NOT exposed to client (no VITE_ prefix)
// src/config.js
const apiUrl = import.meta.env.VITE_API_URL;
const mode = import.meta.env.MODE;
Wrong: Copying webpack.config.js structure into vite.config.js
// ❌ BAD — Manually configuring things Vite handles automatically
export default defineConfig({
plugins: [
react(),
cssPlugin(), // Vite has built-in CSS support
assetPlugin(), // Vite has built-in asset handling
htmlPlugin(), // Vite uses index.html at root
hotReloadPlugin(), // Vite has built-in HMR
],
});
Correct: Minimal Vite config — let defaults work
// ✅ GOOD — Vite needs very little configuration for most projects
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
// CSS, assets, HMR, HTML all work out of the box
});
Wrong: Using require() in source code with Vite
// ❌ BAD — CommonJS require doesn't work in Vite's ESM-first architecture
const logo = require('./assets/logo.png');
const config = require('./config.json');
const modules = require.context('./plugins', true, /\.js$/);
Correct: Use ESM imports and import.meta.glob
// ✅ GOOD — ESM imports for static assets and modules
import logo from './assets/logo.png';
import config from './config.json';
const modules = import.meta.glob('./plugins/**/*.js', { eager: true });
Wrong: Defining global as empty object to fix Node.js polyfill errors
// ❌ BAD — Breaks libraries that actually use global
export default defineConfig({
define: {
global: {}, // Causes "global is not an object" errors at runtime
},
});
Correct: Properly polyfill global for browser context
// ✅ GOOD — Define global as globalThis for browser compatibility
export default defineConfig({
define: {
global: 'globalThis', // globalThis works in both Node and browser
},
});
Wrong: Using the deprecated Sass legacy API with Vite 7
// ❌ BAD — Sass legacy API was removed in Vite 7
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
api: 'legacy', // This option no longer exists in Vite 7
},
},
},
});
Correct: Use the modern Sass API (Vite 7 default)
// ✅ GOOD — Modern Sass API is the only option in Vite 7
export default defineConfig({
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/styles/variables" as *;`,
// No 'api' option needed — modern API is the default
},
},
},
});
Common Pitfalls
- Circular import dependencies in dev mode: Vite's native ESM dev server exposes circular dependencies that Webpack's bundling hides. Symptoms:
undefinedimports, runtime errors only in dev. Fix: Runnpx madge src/index.tsx --circularto find cycles, then extract shared code into separate modules. [src6] - Missing file extensions in imports: Vite requires explicit
.vueextensions and may require other extensions depending on configuration. Fix: Add extensions to all imports or configureresolve.extensions, but prefer explicit extensions. [src3] global is not definederrors: Libraries that reference Node.jsglobalobject fail in Vite's browser ESM context. Fix: Adddefine: { global: 'globalThis' }tovite.config.js. [src4]- CSS Modules require
.module.cssnaming: Webpack'scss-loaderwithmodules: trueworks on any.cssfile, but Vite requires the.module.csssuffix convention. Fix: Renamestyle.csstostyle.module.css. [src3] - Broken default exports from CommonJS dependencies: Some npm packages use
module.exports = Xwhich Vite handles differently. Symptoms:X is not a function. Fix: Useconst Lib = (X as any).default || Xas a fallback. [src6] process.envnot defined at runtime: Vite does not injectprocess.envby default unlike Webpack. Fix: Replace allprocess.env.*withimport.meta.env.*usingVITE_prefix. [src2]- Production build works differently than dev: Vite uses esbuild in dev but Rollup in production. Fix: Always test with
vite build && vite previewbefore deploying. [src4] - Top-level
thisreferences in legacy code: Code that usesthisat module top level fails in ESM wherethisisundefined. Fix: ReplacethiswithglobalThisor configureesbuild: { define: { this: 'window' } }. [src4] - JSX in .js files fails: Unlike Webpack with Babel, Vite's esbuild only processes JSX in
.jsx/.tsxfiles by default. Fix: Rename files containing JSX to.jsxor.tsxextensions. [src2, src7] optimizeDeps.entriestreated as globs in Vite 7: All values are treated as glob patterns rather than literal file paths. Fix: Escape special characters if you have literal paths. [src8]
Diagnostic Commands
# Check for remaining Webpack references in the project
grep -rn "webpack\|require(\|module\.exports" --include='*.js' --include='*.ts' --include='*.jsx' --include='*.tsx' src/ | grep -v node_modules
# Find remaining process.env references
grep -rn "process\.env\." --include='*.js' --include='*.ts' --include='*.jsx' --include='*.tsx' src/
# Find .js files containing JSX that need renaming
grep -rln "<[A-Z][a-zA-Z]*" --include='*.js' src/
# Detect circular dependencies
npx madge src/main.tsx --circular --extensions ts,tsx,js,jsx
# Verify Vite dev server starts
npx vite --debug
# Verify production build completes
npx vite build 2>&1 | tail -20
# Compare bundle sizes (install rollup-plugin-visualizer)
npx vite build && open dist/stats.html
# Check that all env vars are prefixed correctly
grep -rn "^[A-Z]" .env* | grep -v "^.*:VITE_\|^.*:#\|^.*:NODE_ENV"
# List all dependencies that might need Vite-specific plugins
npm ls --depth=0 | grep -i "webpack\|loader\|babel"
# Verify Node.js version meets Vite 7 requirements
node -v # Must be 20.19+ or 22.12+
Version History & Compatibility
| Version | Status | Key Features | Migration Notes |
|---|---|---|---|
| Vite 8.x (2026-03-12) | Current | Rolldown bundler (unified Rust toolchain, 10-30x faster builds, dev/prod parity), Oxc replaces esbuild for JS transforms, integrated Devtools, built-in tsconfig paths, WebAssembly SSR, emitDecoratorMetadata support | build.rollupOptions → build.rolldownOptions; optimizeDeps.esbuildOptions → optimizeDeps.rolldownOptions; esbuild option → oxc; default target Chrome 111/Firefox 114/Safari 16.4; transformWithEsbuild → transformWithOxc; +15 MB install size; recommended 2-step: migrate to rolldown-vite on Vite 7 first |
| Vite 7.x (2025-09) | Maintenance | Node 20.19+ required, baseline-widely-available target, buildApp hook, Sass legacy API removed | Drop Node 18; remove splitVendorChunkPlugin; use order/handler in hooks |
| Vite 6.x (2024) | LTS | Environment API (experimental), Rolldown preview | Vite 5 configs work with minor updates; server.fs.allow tightened |
| Vite 5.x (2023) | Maintenance | Rollup 4, Node 18+ required | Drop Node 14/16 support; update Rollup plugins to v4-compatible |
| Vite 4.x (2022) | EOL | SWC support via plugin | First version with stable React SWC plugin |
| Vite 3.x (2022) | EOL | ESM-only config, import.meta.glob changes | glob returns lazy imports by default (add { eager: true } for sync) |
| Vite 2.x (2021) | EOL | First stable release, Rollup-based | Original migration target from Webpack; most guides reference this |
When to Use / When Not to Use
| Use When | Don't Use When | Use Instead |
|---|---|---|
| Starting a new project or major refactor | Webpack config is simple and build times are acceptable | Keep Webpack |
| Dev server HMR is painfully slow (>2s) | Project relies on Webpack Module Federation | Webpack 5 with Module Federation |
| Want to reduce build configuration complexity | Team has deep Webpack plugin expertise | Maintain existing Webpack setup |
| Using React, Vue, Svelte, or vanilla JS/TS | Building a complex micro-frontend architecture | Webpack 5 + Module Federation or Rspack |
| Need native ESM and modern browser targeting | Must support IE11 or very old browsers | Webpack 5 with Babel |
| Project size is small to medium (<500 modules) | Very large monorepo with 10K+ modules | Turbopack or Rspack |
| Want faster builds without changing bundler API | Need Webpack API compatibility with better perf | Rspack (drop-in Webpack replacement) |
Important Caveats
- On Vite 7 and earlier, Vite uses esbuild for development (fast, but less compatible) and Rollup for production (slower, but more configurable). Code that works in dev may fail in production -- always verify with
vite build && vite preview. Vite 8 closes this gap by using Rolldown for both. [src9] - Vite does not polyfill Node.js built-in modules (
path,fs,crypto, etc.). If your frontend code imports these, you needvite-plugin-node-polyfillsor must refactor. - Vite's dev server serves native ES modules to the browser. The first page load can be slow on large projects (1000+ modules). Use
optimizeDeps.includeto pre-bundle heavy dependencies. - The
@vitejs/plugin-react-swcplugin offers faster builds but may crash on syntax errors rather than displaying error overlays. Test stability before committing to SWC. Note:@vitejs/plugin-reactv6 (paired with Vite 8) now uses Oxc and drops Babel entirely. [src9] - Rolldown-based (Vite 8) and Rollup-based (Vite ≤7) production builds produce different chunk structures than Webpack. If your deployment relies on specific output file names or chunk patterns, verify the output structure after migration.
- Vite 8 (released 2026-03-12) ships Rolldown as the unified bundler. Real-world reports: Linear cut production builds from 46s to 6s (87% reduction); HMR is ~24× faster than Webpack. Migration from Vite 7: the compat layer auto-converts
build.rollupOptions,optimizeDeps.esbuildOptions, and theesbuildconfig option, but plan to rename them before the layer is removed. Recommended path for large apps: switch to therolldown-vitepackage on Vite 7 first to isolate Rolldown-specific issues, then upgrade to Vite 8. [src9, src10] - Vite 7 changed the default browser target from
'modules'(Chrome 87, Firefox 78, Safari 14) to'baseline-widely-available'(Chrome 107, Firefox 104, Safari 16). Vite 8 raises it further to Chrome 111 / Firefox 114 / Safari 16.4. If you need older browser support, setbuild.targetexplicitly. [src8, src9] - Oxc does not yet lower native TypeScript/legacy decorators. If your project relies on
experimentalDecoratorsor stage-3 decorator semantics, you must add a Babel or SWC plugin until Oxc catches up. [src9] - For large codebases, plan an incremental migration (2-6 weeks): run Vite for dev while keeping Webpack for production builds temporarily, then flip prod once all blockers (CommonJS, require.context, Module Federation, SVG, custom loaders) are cleared. [src10]