Vite: Why I Ditched Webpack and Never Looked Back

I have a complicated relationship with Webpack. It is powerful, configurable, and has been the backbone of JavaScript build tooling for years. It is also slow, complex, and requires a configuration file that grows like a weed. My last Webpack config was 200 lines long and I could not explain what half of it did.
Vite 2.0 came out last month, and after migrating two projects from Create React App, I am done with Webpack for new projects. The difference in developer experience is dramatic.
Why Vite Is Fast
The core insight behind Vite is simple: modern browsers support ES modules natively. During development, Vite does not bundle your code at all. Instead, it serves your source files directly as ES modules and lets the browser handle the import resolution.
When you start the Vite dev server, it does two things. First, it pre-bundles your dependencies (the stuff in node_modules) using esbuild, which is written in Go and is 10 to 100 times faster than JavaScript-based bundlers. This is a one-time step that gets cached. Second, it serves your application source files as native ES modules over HTTP.
When the browser loads your app, it requests each module individually. Vite serves them on demand. This means the dev server start time is nearly instant regardless of how large your application is, because Vite does not need to process every file before serving the first page.
Compare that to Webpack, which bundles your entire application into one or more files before the dev server can show anything. For a large app, that can take 30 seconds or more on a cold start.
Hot Module Replacement That Works
HMR in Webpack has always been finicky. Sometimes it works great, sometimes the state resets, sometimes the whole page reloads. Vite's HMR is noticeably more reliable and much faster.
Because Vite serves individual modules, when you change a file, it only needs to invalidate that single module and its direct dependents. It does not need to rebuild a bundle. The update shows up in the browser in under 50 milliseconds for most changes. On my old Webpack setup, the same change took 2 to 5 seconds.
For React projects, Vite integrates with React Fast Refresh through the @vitejs/plugin-react plugin. Component state is preserved during edits, and the update is nearly instantaneous.
The Configuration
This is my favorite part. A Vite config for a React + TypeScript project looks like this:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
port: 3000,
},
});
That is the entire config. Compare that to a Webpack config for the same setup, which would include loaders for TypeScript, JSX, CSS, images, and fonts, plus dev server configuration, HMR setup, and various optimization plugins. Vite handles all of that out of the box with sensible defaults.
If you do need customization, the config API is well-designed and documented. You can add aliases, configure the proxy, adjust build options, and add plugins. But for most projects, the default configuration works perfectly.
Migrating from Create React App
The migration was easier than I expected. The basic steps:
npm uninstall react-scripts
npm install --save-dev vite @vitejs/plugin-react
Then move index.html from the public folder to the project root (Vite uses it as the entry point) and add a script tag pointing to your main JavaScript file:
<script type="module" src="/src/index.tsx"></script>
Update your package.json scripts:
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
The biggest change was environment variables. Create React App uses REACT_APP_ prefix. Vite uses VITE_ prefix and accesses them via import.meta.env instead of process.env. A find-and-replace handled most of that.
Some CRA-specific features like react-app-rewired and craco customizations needed manual conversion, but since Vite's plugin system is straightforward, the equivalent config was usually simpler.
Production Builds
For production, Vite uses Rollup under the hood. Rollup produces highly optimized bundles with tree-shaking, code splitting, and asset optimization. The build output is comparable to Webpack's production output in terms of size and performance.
The build command is simply vite build. No complex optimization configuration needed. The defaults include minification, chunk splitting, and asset hashing for cache busting.
Framework Agnostic
While I have been talking about React, Vite is framework agnostic. It has official plugins for React, Vue, Svelte, and Preact. You can also use it for vanilla JavaScript or TypeScript projects with no framework at all. The core value proposition (fast dev server, simple config, good defaults) applies regardless of your framework choice.
The Numbers
On a medium-sized React project (about 150 components and 50 routes):
- CRA cold start: 28 seconds
- Vite cold start: 800 milliseconds
- CRA HMR update: 2 to 4 seconds
- Vite HMR update: under 50 milliseconds
- CRA production build: 45 seconds
- Vite production build: 12 seconds
The dev server improvement is the most impactful because you feel it constantly throughout the day. Waiting 30 seconds for the dev server to start might not sound like a lot, but when you are restarting it multiple times per day, it adds up. With Vite, the server is ready before you can switch to the browser tab.
If you are starting a new project, I would strongly recommend Vite over Create React App. The development experience is better in every measurable way, and the migration path from existing CRA projects is manageable. This is one of those tools where the improvement is so obvious that going back feels wrong.