Skip to main content
⚡ Calmops

Modern JavaScript Build Tools: Vite, esbuild, Turbopack, and Rollup

Introduction

The JavaScript build tooling landscape has transformed dramatically. Where webpack once dominated, a new generation of tools built on native ES modules and compiled languages (Rust, Go) delivers 10-100x faster builds. This guide covers the major tools, their strengths, and when to use each.

Why Build Tools Matter

Modern JavaScript projects need build tools to:

  • Bundle many files into fewer HTTP requests
  • Transform TypeScript, JSX, and modern syntax for older browsers
  • Tree-shake unused code to reduce bundle size
  • Optimize assets (minification, compression, code splitting)
  • Enable Hot Module Replacement (HMR) during development

Vite: The Modern Standard

Vite is the most popular modern build tool. It uses native ES modules in development (no bundling needed) and Rollup for production builds.

Why Vite is Fast

  • Dev server: serves files as native ES modules — no bundling, instant startup
  • HMR: updates only the changed module, not the whole bundle
  • Production: uses Rollup with optimizations

Setup

# Create a new project
npm create vite@latest my-app -- --template react
npm create vite@latest my-app -- --template vue
npm create vite@latest my-app -- --template vanilla-ts

cd my-app
npm install
npm run dev    # dev server
npm run build  # production build
npm run preview # preview production build

vite.config.ts

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
    plugins: [react()],

    // Dev server
    server: {
        port: 3000,
        open: true,
        proxy: {
            '/api': {
                target: 'http://localhost:4000',
                changeOrigin: true,
            },
        },
    },

    // Build options
    build: {
        outDir: 'dist',
        sourcemap: true,
        minify: 'esbuild',  // or 'terser' for better compression
        rollupOptions: {
            output: {
                // Manual chunk splitting
                manualChunks: {
                    vendor: ['react', 'react-dom'],
                    utils: ['lodash', 'date-fns'],
                },
            },
        },
    },

    // Path aliases
    resolve: {
        alias: {
            '@': path.resolve(__dirname, './src'),
            '@components': path.resolve(__dirname, './src/components'),
        },
    },

    // Environment variables
    define: {
        __APP_VERSION__: JSON.stringify(process.env.npm_package_version),
    },
});

Vite Plugins

# Popular plugins
npm install @vitejs/plugin-react          # React with Fast Refresh
npm install @vitejs/plugin-vue            # Vue 3
npm install vite-plugin-pwa               # PWA support
npm install vite-tsconfig-paths           # TypeScript path aliases
npm install rollup-plugin-visualizer      # Bundle size visualization
import { visualizer } from 'rollup-plugin-visualizer';
import { VitePWA } from 'vite-plugin-pwa';

export default defineConfig({
    plugins: [
        react(),
        VitePWA({ registerType: 'autoUpdate' }),
        visualizer({ open: true }),  // opens bundle analysis after build
    ],
});

esbuild: The Speed Champion

esbuild is written in Go and is 10-100x faster than webpack. It’s used internally by Vite for dependency pre-bundling and TypeScript transpilation.

Speed Comparison

Bundle a 3MB React app:
webpack 5:  ~30 seconds
Rollup:     ~8 seconds
Parcel:     ~5 seconds
esbuild:    ~0.3 seconds ⚡ (100x faster than webpack)

Basic Usage

// build.mjs
import * as esbuild from 'esbuild';

// Simple bundle
await esbuild.build({
    entryPoints: ['src/index.tsx'],
    bundle: true,
    outfile: 'dist/bundle.js',
    minify: true,
    sourcemap: true,
    target: ['es2020', 'chrome90', 'firefox88'],
    format: 'esm',
    jsx: 'automatic',  // React 17+ automatic JSX transform
});

// Multiple entry points
await esbuild.build({
    entryPoints: ['src/app.ts', 'src/worker.ts'],
    bundle: true,
    outdir: 'dist',
    splitting: true,  // code splitting
    format: 'esm',
    chunkNames: 'chunks/[name]-[hash]',
});

Dev Server with esbuild

import * as esbuild from 'esbuild';

const ctx = await esbuild.context({
    entryPoints: ['src/index.tsx'],
    bundle: true,
    outfile: 'dist/bundle.js',
    sourcemap: true,
});

// Watch mode
await ctx.watch();

// Dev server
const { host, port } = await ctx.serve({
    servedir: 'public',
    port: 3000,
});

console.log(`Dev server: http://${host}:${port}`);

When to Use esbuild

  • Building libraries (fast, minimal output)
  • CI/CD pipelines where build speed matters
  • Simple applications without complex plugin needs
  • As a TypeScript transpiler (faster than tsc)

esbuild Limitations

  • No CSS modules support (use PostCSS separately)
  • Limited plugin ecosystem vs webpack/Rollup
  • No HMR built-in (use with a separate dev server)

Turbopack: Next.js’s Bundler

Turbopack is Vercel’s Rust-based bundler, designed as the successor to webpack for Next.js.

Key Features

  • Written in Rust — extremely fast
  • Incremental compilation (only rebuilds what changed)
  • Optimized for Next.js App Router
  • Lazy compilation (only compiles what’s requested)

Usage with Next.js

# Create Next.js app with Turbopack
npx create-next-app@latest --turbo

# Or enable in existing project
# next.config.js
module.exports = {
    experimental: {
        turbo: {
            rules: {
                '*.svg': {
                    loaders: ['@svgr/webpack'],
                    as: '*.js',
                },
            },
        },
    },
};
# Dev with Turbopack
next dev --turbo

# Build (still uses webpack for production as of 2026)
next build

Turbopack vs webpack (Next.js)

Metric webpack Turbopack
Cold start ~10s ~1.5s
HMR update ~500ms ~50ms
Large app startup ~30s ~3s

Rollup: The Library Bundler

Rollup excels at bundling JavaScript libraries. It produces clean, tree-shaken output and supports multiple output formats.

When to Use Rollup

  • Building npm libraries
  • When you need multiple output formats (ESM, CJS, UMD)
  • When bundle size is critical

rollup.config.js

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import terser from '@rollup/plugin-terser';
import dts from 'rollup-plugin-dts';

export default [
    // Main bundle
    {
        input: 'src/index.ts',
        output: [
            {
                file: 'dist/index.cjs',
                format: 'cjs',
                sourcemap: true,
            },
            {
                file: 'dist/index.mjs',
                format: 'esm',
                sourcemap: true,
            },
        ],
        plugins: [
            resolve(),
            commonjs(),
            typescript(),
            terser(),
        ],
        external: ['react', 'react-dom'],  // don't bundle peer deps
    },
    // Type declarations
    {
        input: 'src/index.ts',
        output: { file: 'dist/index.d.ts', format: 'esm' },
        plugins: [dts()],
    },
];

Webpack: Still Relevant

webpack remains the most feature-complete bundler with the largest plugin ecosystem. Use it when you need:

  • Complex code splitting strategies
  • Specific loaders not available elsewhere
  • Module Federation (micro-frontends)
  • Legacy project compatibility
// webpack.config.js
module.exports = {
    entry: './src/index.tsx',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].[contenthash].js',
    },
    module: {
        rules: [
            { test: /\.tsx?$/, use: 'ts-loader' },
            { test: /\.css$/, use: ['style-loader', 'css-loader'] },
        ],
    },
    optimization: {
        splitChunks: { chunks: 'all' },
    },
};

Choosing the Right Tool

Scenario Recommended Tool
New React/Vue/Svelte app Vite
Next.js app Next.js built-in (Turbopack in dev)
npm library Rollup or tsup
Need maximum build speed esbuild
Complex enterprise app webpack (or Vite with plugins)
Simple TypeScript transpilation esbuild
Remix app Vite

Bundle Analysis

Always analyze your bundle size:

# Vite
npm install rollup-plugin-visualizer
# Add to vite.config.ts, run build, opens browser

# webpack
npm install webpack-bundle-analyzer

# esbuild
npm install esbuild-bundle-analyzer

Resources

Comments