Data note
25

Data note · Published Apr 13, 2025

I Would Start with Vite and Make Webpack Prove Itself

The benchmark gap was obvious, but deleting 90 lines of build configuration was the better reason to choose Vite for a modern app.

A field-guide drawing of a camping headlamp and star chart
In this article5 sections

The benchmark gap between Vite and Webpack was obvious. The better migration result was deleting build configuration I no longer had to understand or maintain.

The Speed Gap

MetricViteWebpackVite’s Advantage
Dev server startup376ms6s15.9x faster
Hot module reloadInstant1.5s~30x faster
Production build2s11s5.5x faster
Bundle size866KB934KB7.3% smaller

These measurements came from a medium-sized Vue project. A React comparison showed a similar direction:

MetricViteWebpackVite’s Advantage
Speed370ms7s18.9x faster
Changes updateInstant1.3s~26x faster
Production build4s11s2.8x faster
Bundle size740KB920KB19.6% smaller

For developers making frequent code changes, these speed differences compound across a workday.

Configuration: 110 Lines vs 18

The most obvious difference is config complexity.

Webpack Configuration Example:

// webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
  mode: "development",
  entry: "./src/index.js",
  output: {
    filename: "[name].[contenthash].js",
    path: path.resolve(__dirname, "dist"),
    clean: true,
    publicPath: "/",
  },
  devtool: "inline-source-map",
  devServer: {
    static: "./dist",
    hot: true,
  },
  optimization: {
    runtimeChunk: "single",
    splitChunks: {
      chunks: "all",
      maxInitialRequests: Infinity,
      minSize: 0,
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name(module) {
            const packageName = module.context.match(
              /[\\/]node_modules[\\/](.*?)([\\/]|$)/,
            )[1];
            return `npm.${packageName.replace("@", "")}`;
          },
        },
      },
    },
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env", "@babel/preset-react"],
          },
        },
      },
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: "asset/resource",
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: "Development",
      template: "./public/index.html",
    }),
    new MiniCssExtractPlugin({
      filename: "[name].[contenthash].css",
    }),
  ],
};

Vite Configuration Example:

// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  server: {
    port: 3000,
    open: true,
  },
  build: {
    outDir: "dist",
    cssCodeSplit: true,
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ["react", "react-dom"],
        },
      },
    },
  },
});

Webpack requires familiarity with loaders, plugins, and optimization settings. Vite ships sensible defaults and gets out of the way.

Field tip When migrating, delete your Webpack config entirely and start with a fresh vite.config.js. Trying to port Webpack options one-by-one leads to unnecessary complexity because Vite handles most of them by default.

Hot Module Replacement

In the measured Vite project, changes appeared without a noticeable wait or loss of state:

// src/Hello.jsx
import { useState } from "react";

export default function Hello() {
  const [name, setName] = useState("World");

  return (
    <div>
      <h1>Hello, {name}!</h1>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Enter a name"
      />
      <p>Try typing your name above</p>
    </div>
  );
}

Changing this component preserved state in Vite. In the Webpack comparison, the update took 1–1.5 seconds.

Why Vite Is Architecturally Faster

  • Cold Start Speed: Vite uses esbuild (written in Go) to pre-bundle dependencies — 10-100x faster than JavaScript-based alternatives. It serves source code on-demand using native ES modules. Webpack crawls and builds the entire application before serving anything.
  • Hot Module Replacement: Vite’s HMR runs over native ESM, cutting server load and latency. Webpack’s HMR carries higher overhead because it can’t fully use native ES modules.
  • Production Builds: Vite uses Rollup, which excels at tree-shaking and code splitting. Webpack’s optimization is powerful but demands more configuration.

When Webpack Still Makes Sense

Important Despite Vite’s advantages, Webpack remains a strong choice in certain scenarios:

  • IE11 support: Webpack’s ecosystem has more compatibility options for older browsers.
  • Module Federation: Webpack 5’s micro-frontend support is more mature than Vite alternatives.
  • Specialized plugins: Projects relying on Webpack-specific plugins without Vite equivalents are stuck.

Legacy browser support might look like this:

// webpack.config.js for legacy browser support
module.exports = {
  // ...other settings
  target: ["web", "es5"],
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [
              [
                "@babel/preset-env",
                {
                  targets: { ie: "11" },
                  useBuiltIns: "usage",
                  corejs: 3,
                },
              ],
            ],
          },
        },
      },
    ],
  },
};

For a new project targeting modern browsers, I start with Vite. I would reach for Webpack only when a concrete requirement, such as mature Module Federation, an irreplaceable plugin, or legacy-browser support, earns the extra configuration.


Related posts:

Build tooling for the same sites sits under static blog on Cloudflare.

One quick signal

Did this earn your time?

What was missing?

Thanks. That gives me something concrete to check.