Accelerating Builds with ESBuild or Vite

Table of Contents
Big thanks to our contributors those make our blogs possible.

Our growing community of contributors bring their unique insights from around the world to power our blog. 

Introduction

Slow build times kill developer productivity. As projects grow—with TypeScript, JSX, CSS preprocessors, images, environment variants, and complex dependency graphs—each build, watch‑rebuild, or CI run can take minutes. esbuild and Vite tackle this head‑on:

  • esbuild: A lightning‑fast bundler/transpiler written in Go that handles TypeScript, JSX, modern syntax, bundling, code splitting, minification, and sourcemaps—all in one pass.
  • Vite: A next‑generation dev tool that serves source files via native ES modules for instant Cold Start and HMR (Hot Module Replacement), uses esbuild for dependency pre‑bundling, and delegates to Rollup for optimized production builds.

Together, they slash feedback loops, cut CI minutes, and make performance budgets easier to uphold. This guide shows when and how to adopt each tool, configuration patterns, and optimization techniques for libraries and large‑scale apps.

1. Understanding Build‑Speed Bottlenecks

BottleneckWhy It’s SlowHow esbuild/Vite Help
TranspilationBabel/tsc pipelines copy and transform ASTsesbuild’s native Go parser is 10–100× faster
Module ResolutionRecursive graph traversalesbuild’s parallel resolver; Vite’s pre‑bundle
Minification/Tree‑ShakingSlow Terser/Uglify and CJS analysisesbuild’s built‑in minifier & ESM native tree shaking
Asset ProcessingCSS preprocessors, PostCSS plugins, image transformsesbuild loader for CSS/JSON; Vite on‑demand transforms
Cold Start OverheadTool startup + plugin initializationesbuild single binary; Vite dev server spawns sub‑tasks only as needed
Type CheckingSynchronous tsc blocking the buildDecouple via --noEmit; run in parallel or CI only
I/O & Cache BustsRecomputing even unchanged bundlesesbuild incremental builds; Vite optimize cache persisted

2. Choosing Between esbuild and Vite

Scenarioesbuild StandaloneVite Development & Build
Small Library / NPM Package✅ Ideal (fast ESM/CJS)Overkill
Large SPA with HMR Needs✅ Possible (DIY)✅ Batteries‑included
Hybrid SSR + CSR (React, Vue)✅ Custom build engine✅ Vite SSR mode
Migrating from webpack✅ Use esbuild loader✅ Vite migration path
Monorepo with many packages✅ Pre‑build libs with esbuild✅ Apps powered by Vite
Deeply customized Rollup plugins needed❌ Limited ecosystem✅ Rollup plugin support in build

Rule of thumb:

  • Use esbuild when you need an ultra‑fast engine for transforms, library builds, or as a plugin in existing pipelines.
  • Use Vite when you want an all‑in‑one developer experience with instant feedback and a production‑ready Rollup build under the hood.

3. Using esbuild Directly

3.1 Minimal CLI Build

bashCopyEditesbuild src/index.ts \
  --bundle \
  --outfile=dist/app.js

3.2 Targeting & Minification

bashCopyEditesbuild src/index.ts \
  --bundle \
  --platform=browser \
  --target=es2018 \
  --minify \
  --sourcemap \
  --outfile=dist/app.min.js

3.3 Multiple Formats

bashCopyEdit# ESM build
esbuild src/index.ts --bundle --format=esm --outfile=dist/app.esm.js --metafile=dist/meta-esm.json
# IIFE build
esbuild src/index.ts --bundle --format=iife --global-name=MyLib --outfile=dist/app.iife.js

3.4 Incremental Rebuilds (JS API)

jsCopyEdit// build.js
const esbuild = require('esbuild');

esbuild.build({
  entryPoints: ['src/index.ts'],
  bundle: true,
  outdir: 'dist',
  platform: 'browser',
  sourcemap: true,
  incremental: true,
  watch: {
    onRebuild(err, res) {
      if (err) console.error('Rebuild failed:', err);
      else console.log('Rebuild succeeded');
    }
  }
}).then(() => console.log('Initial build complete'));

3.5 Code Splitting & Loaders

bashCopyEditesbuild src/index.ts \
  --bundle \
  --splitting \
  --format=esm \
  --outdir=dist
bashCopyEdit# Override loaders
esbuild src/app.tsx \
  --bundle \
  --loader:.svg=dataurl \
  --loader:.png=file \
  --outdir=dist

3.6 Define Constants for Tree Shaking

bashCopyEditesbuild src/index.ts \
  --bundle \
  --define:process.env.NODE_ENV='"production"' \
  --define:__FEATURE_FLAG__='true' \
  --outfile=dist/app.js

4. Integrating esbuild into Hybrid Pipelines

  • TypeScript Only Transpile
    • Run esbuild for JS output and tsc --noEmit in parallel for type checking.
  • Pre‑bundle Internal Libraries
    • Use esbuild to build shared libs into dist folders; apps consume pre‑built ESM.
  • As a webpack Loader
    • Install esbuild-loader to replace Babel in existing webpack setups, cutting transpile times dramatically.

5. Vite Architecture & Configuration

5.1 Dev vs Production Modes

  • Dev:
    • Native ESM serving—no bundle on startup.
    • On‑demand transforms (JSX, TS, CSS) only for imported modules.
    • esbuild pre‑bundles legacy deps once for speed.
    • Instant HMR—only affected modules reload.
  • Build:
    • Vite uses Rollup for output.
    • esbuild handles minification by default—fast prod builds.
    • Full code splitting, tree shaking, and plugin support.

5.2 Getting Started (React + TS)

bashCopyEditnpm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run dev

5.3 Key vite.config.js Options

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

export default defineConfig({
  plugins: [react()],
  build: {
    target: 'es2018',
    sourcemap: false,
    minify: 'esbuild',       // default fast minify
    cssCodeSplit: true,
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom']
        }
      }
    }
  },
  optimizeDeps: {
    include: ['lodash-es'],  // force pre-bundle
    exclude: ['@large/lib'], // skip if already ESM
    esbuildOptions: { target: 'es2018' }
  },
  define: {
    __APP_VERSION__: JSON.stringify(process.env.npm_package_version)
  },
  server: { hmr: { overlay: false } }
});

6. TypeScript Strategy in Vite

  • Dev: esbuild transpiles TS → JS; no type checking → blazing speed.
  • Type Safety:
    • Run tsc --noEmit --watch in parallel in local dev.
    • Gate merges in CI with tsc --noEmit.
    • Leverage editor/IDE for real‑time diagnostics.

7. CSS & Asset Handling in Vite

  • On‑Demand CSS: Preprocessors (Sass, PostCSS) compile only imported files.
  • Code Splitting: CSS extracted per JS chunk by default.
  • Asset URLs: Small assets inlined as base64 URIs; larger files emitted with hashed names.
  • Optimize: Limit large CSS @import cascades; prefer modular imports for performance.

8. CI/CD Build Acceleration

StrategyBenefit
Cache Vite optimizeDepsSkip re‑bundling vendor deps in CI
Parallel Lint/Type vs BuildFail fast on errors; build continues independently
Persistent esbuild IncrementalReuse previous build graph for incremental monorepo libs
Single Artifact, Multi‑StageBuild once, test & deploy same bundle; avoids duplicates

9. Measuring & Monitoring Build Performance

jsCopyEdit// measure.js
const esbuild = require('esbuild');
const { performance } = require('perf_hooks');

(async () => {
  const start = performance.now();
  await esbuild.build({ /* your options */ });
  console.log(`Build time: ${(performance.now() - start).toFixed(0)}ms`);
})();

Track in CI logs or dashboards. Record:

  • Cold build time
  • Incremental rebuild time
  • Dev server startup time
  • Bundle sizes (raw & gzipped)
  • OptimizeDeps duration (Vite)

10. Common Pitfalls & Fixes

IssueLikely CauseRemedy
Missing polyfillsTarget too modernLower target or add polyfills
Large vendor chunkNo splitting configuredUse --splitting (esbuild) or manualChunks (Vite)
Type errors in productionNo type check stepAdd tsc --noEmit in CI
Slow cold start in ViteMany CJS deps unoptimizedAdd to optimizeDeps.include
Unused code persistsSide‑effectful modules or dynamic importsMark "sideEffects": false or restructure imports

Conclusion

Reducing build times is a force multiplier for developer happiness and product velocity. esbuild provides a blisteringly fast engine you can slot into any pipeline—ideal for libraries, monorepos, or as a Babel replacement. Vite offers a first‑class dev experience for full applications, combining native ESM dev server speed, esbuild‑powered pre‑bundling, and Rollup’s mature production optimizations.

Action plan:

  1. Measure your current build times.
  2. Isolate the heaviest steps (transpile, bundle, minify).
  3. Swap in esbuild for transforms or spin up a Vite dev server for your app.
  4. Configure splitting, targets, and caching for your use case.
  5. Monitor gains in CI and local dev; iterate on chunking and plugin usage.

Reclaim minutes per build, hours per sprint—and focus on shipping features that delight your users. Happy coding!

Let's connect on TikTok

Join our newsletter to stay updated

Sydney Based Software Solutions Professional who is crafting exceptional systems and applications to solve a diverse range of problems for the past 10 years.

Share the Post

Related Posts