Vite doesn’t actually replace esbuild, it uses it.

Here’s a Vite build output for a simple React app, showing esbuild doing the heavy lifting for dependency pre-bundling:

{
  "dependencies": {
    "react": {
      "src": "node_modules/react/index.js",
      "file": "dist/assets/react.ea978236.js",
      "type": "module"
    },
    "react-dom": {
      "src": "node_modules/react-dom/index.js",
      "file": "dist/assets/react-dom.d9e4c729.js",
      "type": "module"
    }
  },
  "chunks": {
    "entry-client": {
      "src": "src/main.tsx",
      "file": "dist/assets/entry-client.8a9b0c1d.js",
      "imports": ["react", "react-dom"],
      "type": "module"
    }
  }
}

Vite’s magic is in its dev server and build process, which are distinct but work together. At its core, Vite leverages esbuild for two primary, high-performance tasks: dependency pre-bundling and optimized production builds.

When you start a Vite development server, the first thing it does is pre-bundle your dependencies using esbuild. This isn’t about transpiling your application code; it’s about taking your node_modules and converting them into efficient ES modules. Why? Because native ES module imports in the browser are slow when you have thousands of small files. Esbuild, being written in Go, is incredibly fast at this. It bundles your dependencies into a few large files, transforming CommonJS or UMD modules into ESM. This dramatically speeds up the initial server start and subsequent page loads in development.

For example, if you have react and react-dom installed, Vite will run esbuild to create dist/vendor/[name].[hash].js files for these dependencies. This means when your browser requests node_modules/react, Vite serves the pre-bundled dist/vendor/react.ea978236.js instead, which is a single, optimized file.

During development, your application code is served directly to the browser via native ES modules. Vite uses the browser’s native import support. When you import a file like ./src/App.tsx, Vite doesn’t bundle it. Instead, it transforms it on-demand using esbuild (or SWC, if configured) for syntax features not supported by all target browsers, and then serves it. This "no-bundle" approach for your app code is what makes Vite’s dev server feel instantaneous.

When you run vite build, Vite orchestrates a production build. Here, it again leverages esbuild, but this time for its lightning-fast minification and bundling capabilities. While Rollup is the default bundler for production builds in Vite (offering more advanced code-splitting and plugin extensibility), esbuild is used for its speed in minifying the output. Vite can be configured to use esbuild as the primary bundler for production builds if maximum speed is the absolute priority, though this sacrifices some of Rollup’s advanced features.

The key difference in when you’d use them directly versus through Vite is control and scope.

Use esbuild directly when:

  • You need an extremely fast command-line build tool for a specific task, like generating a single JavaScript file, minifying CSS, or transpiling TypeScript to JavaScript without a complex project structure.
  • You are building developer tooling or internal scripts where raw speed for simple transformations is paramount.
  • You want the absolute fastest possible minification for production assets, and you’re willing to potentially forgo some of the finer-grained control or advanced code-splitting features that bundlers like Rollup offer.

Consider this command:

esbuild src/index.ts --bundle --outfile=dist/bundle.js --minify --platform=node --format=esm

This command takes src/index.ts, bundles it with its dependencies, outputs it to dist/bundle.js, minifies it, targets Node.js, and formats it as an ES module. It’s a direct, fast transformation.

Use Vite directly (as a framework/dev server) when:

  • You are building a modern web application (frontend or full-stack) and want an incredibly fast development experience.
  • You need a complete build toolchain that handles asset management, hot module replacement (HMR), and optimized production builds with minimal configuration.
  • You want to leverage the speed of esbuild for dependency pre-bundling and on-demand transformation during development, while benefiting from a robust production build process.

Vite’s configuration file, vite.config.js, is where you control its behavior:

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

export default defineConfig({
  plugins: [react()],
  esbuild: {
    // Options to pass to esbuild
    // For example, to disable minification in dev builds:
    minify: process.env.NODE_ENV === 'production',
    // Or to target a specific browser version with esbuild's JSX transform:
    jsxFactory: 'React.createElement',
    jsxFragment: 'React.Fragment',
  },
  build: {
    // You can even choose esbuild as the production bundler
    // This is less common as Rollup offers more flexibility
    // bundler: 'esbuild',
    rollupOptions: {
      // Rollup specific options
    },
  },
});

This vite.config.js shows how you can pass options directly to esbuild for its minification or JSX transformations, or even configure Vite to use esbuild as its production bundler (though Rollup is the default and generally recommended for its plugin ecosystem and advanced features).

The one thing most people don’t realize is that Vite’s "no-bundle" development server is a bit of a misnomer for your application code. While it doesn’t perform a full static analysis and bundle everything upfront like Webpack, it does perform on-demand transformations using esbuild or SWC. So, when you import a .ts file, esbuild is invoked in memory to transpile it to .js just before sending it to the browser, and this happens for every requested file. This on-demand transpilation is the core of Vite’s speed, but it’s still a transformation step, not a true zero-work scenario.

The next step is understanding how Vite’s plugin system interacts with these underlying tools, allowing you to hook into the transformation pipeline before or after esbuild does its work.

Want structured learning?

Take the full Vite course →