esbuild & Rollup
Use esbuild for ultra-fast bundling and Rollup for library publishing: build scripts, tree-shaking, IIFE/ESM/CJS output formats, and plugin authoring.
esbuild is the fastest JavaScript bundler (10-100x faster than webpack), written in Go. Rollup excels at library packaging with superior tree-shaking. Both have their place: esbuild for apps and CLI tools needing fast builds, Rollup for npm libraries needing clean output.
Use esbuild for fast transformation and bundling of applications and Node.js tools. Use Rollup for npm libraries that need clean, tree-shakeable ESM output. Note: Vite uses esbuild for dev transforms and Rollup for production builds - you get both when using Vite.
Workflow
- Bundle with esbuild programmaticallyesbuild's Node.js API enables build scripts with TypeScript, without config file overhead.
- Transform files with esbuild (no bundling)Sometimes you just need TypeScript → JavaScript transformation without bundling, e.g., for Node.js scripts.
- Bundle libraries with RollupRollup generates cleaner ESM output than webpack/esbuild for libraries - better tree-shaking for consumers.
- Write a Rollup pluginRollup plugins handle custom file types, code generation, and build-time data fetching.
- Publish a library with tsuptsup wraps esbuild with sensible defaults for library publishing - simpler than raw Rollup for most cases.
Output Format
esbuild for app/CLI bundling, Rollup/tsup for library publishing, ESM + CJS output, type declarations with `rollup-plugin-dts` or tsup `dts: true`, external peer dependencies.
Examples
esbuild build script: `import { build } from "esbuild"; const ctx = await build({ entryPoints: ["src/index.ts"], outdir: "dist", bundle: true, format: "esm", splitting: true, minify: true, metafile: true }); const text = await analyzeMetafile(ctx.metafile); console.log(text)`tsup config: `export default defineConfig([{ entry: ["src/index.ts"], format: ["esm","cjs"], dts: true, splitting: false }, { entry: ["src/cli.ts"], format: ["cjs"], platform: "node", banner: { js: "#!/usr/bin/env node" } }])`Rollup tree-shake test: `import { onlyThisFunction } from "my-lib"; // result should include onlyThisFunction but NOT other exports from my-lib; verify with bundle-analysis`Edge Cases & Gotchas
- esbuild and decorators: esbuild supports TypeScript decorators with `--target=es2022` + `--supported:decorators=true`; for experimental decorators (TypeScript 4.x style), use `--loader=ts` with tsconfig `experimentalDecorators: true`
- Rollup and default exports: CommonJS interop with `export default` can cause issues - use named exports in libraries to avoid `module.exports.default = ...` confusion in CJS consumers
- Circular imports: esbuild handles them (like Node.js does) but they can cause subtle initialization order bugs; Rollup warns about them explicitly - treat Rollup warnings as errors
When to Break These Rules
- For complex SSR builds with dynamic requires, Vite or webpack with ssr-specific config handles edge cases better than raw esbuild.
Reference Files
esbuild API: https://esbuild.github.io/api/Rollup docs: https://rollupjs.org/guide/en/tsup: https://tsup.egoist.dev/Comparison: https://bundlephobia.com/