[Skip to main content](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026#main-content) ## [TL;DR](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#tldr) **tsup** is the most popular TypeScript library bundler — zero-config, generates ESM + CJS + `.d.ts` types automatically, used by thousands of npm packages. **tsdown** is the next-generation successor to tsup — built on Rolldown (Vite's Rust-based bundler), significantly faster, same DX but better performance. **unbuild** is from the UnJS ecosystem — supports multiple build presets, stub mode for development (no build step), and is used by Nuxt, Nitro, and UnJS libraries internally. In 2026: tsup is the safe choice with the largest community, tsdown is the emerging performance leader, unbuild if you're in the Nuxt/UnJS ecosystem. ## [Key Takeaways](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#key-takeaways) - **tsup**: ~6M weekly downloads — esbuild-based, zero-config, ESM + CJS + types in one command - **tsdown**: ~500K weekly downloads — Rolldown-based (Rust), 3-5x faster than tsup, tsup-compatible API - **unbuild**: ~3M weekly downloads — UnJS, stub mode, multiple presets, Rollup-based - All three generate dual ESM/CJS output — required for modern npm packages - All three generate TypeScript declaration files (`.d.ts`) automatically - tsdown is rapidly gaining adoption in 2026 as the fast tsup replacement * * * ## [Why Library Bundling Matters](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#why-library-bundling-matters) ``` Problem: publishing TypeScript to npm requires: 1. Compile TypeScript → JavaScript 2. Generate .d.ts type declarations 3. Create both ESM (import) and CJS (require) versions 4. Tree-shaking to minimize bundle size 5. Correct package.json "exports" map Without a bundler: tsc --outDir dist → CJS only, no bundling + manually maintain package.json exports + separately configure dts generation + no tree-shaking With tsup/tsdown/unbuild: One command → dist/index.mjs + dist/index.cjs + dist/index.d.ts ``` * * * ## [tsup](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#tsup) [tsup](https://tsup.egoist.dev/) — zero-config library bundler: ### [Setup](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#setup) ```bash npm install -D tsup typescript # Add to package.json scripts: { "scripts": { "build": "tsup", "dev": "tsup --watch" } } ``` ### [tsup.config.ts](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#tsupconfigts) ```typescript import { defineConfig } from "tsup" export default defineConfig({ entry: ["src/index.ts"], // Entry point(s) format: ["esm", "cjs"], // Output ESM + CommonJS dts: true, // Generate .d.ts files splitting: false, // Code splitting (for multiple entry points) sourcemap: true, // Generate source maps clean: true, // Clean dist/ before build minify: false, // Don't minify libraries (let consumers decide) external: ["react", "vue"], // Don't bundle peer dependencies treeshake: true, // Remove unused code target: "es2020", // Output target outDir: "dist", }) ``` ### [Multiple entry points](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#multiple-entry-points) ```typescript import { defineConfig } from "tsup" export default defineConfig({ // Multiple entry points (for sub-path exports): entry: { index: "src/index.ts", server: "src/server.ts", client: "src/client.ts", }, format: ["esm", "cjs"], dts: true, splitting: true, // Share code between entry points }) // Generates: // dist/index.mjs + dist/index.js + dist/index.d.ts // dist/server.mjs + dist/server.js + dist/server.d.ts // dist/client.mjs + dist/client.js + dist/client.d.ts ``` ### [package.json for dual ESM/CJS](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#packagejson-for-dual-esmcjs) ```json { "name": "my-library", "version": "1.0.0", "main": "./dist/index.js", // CJS entry (legacy) "module": "./dist/index.mjs", // ESM entry (bundlers) "types": "./dist/index.d.ts", // TypeScript types "exports": { ".": { "import": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" }, "require": { "types": "./dist/index.d.ts", "default": "./dist/index.js" } }, "./server": { "import": "./dist/server.mjs", "require": "./dist/server.js" } }, "files": ["dist"], "scripts": { "build": "tsup", "prepublishOnly": "npm run build" }, "devDependencies": { "tsup": "^8.0.0", "typescript": "^5.0.0" } } ``` ### [Watch mode for development](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#watch-mode-for-development) ```bash # Rebuild on file changes: tsup --watch # Or in parallel with your dev server: # package.json: { "scripts": { "dev": "concurrently \"tsup --watch\" \"node dist/index.js\"" } } ``` * * * ## [tsdown](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#tsdown) [tsdown](https://tsdown.egoist.dev/) — the Rolldown-based tsup successor: ### [Why tsdown is faster](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#why-tsdown-is-faster) ``` tsup uses: esbuild (Go) → fast, but JS orchestration overhead tsdown uses: Rolldown (Rust) → faster bundler + faster orchestration Build time comparison (real-world library with 50 files): tsup: ~2.5s tsdown: ~0.6s (varies by project size and machine) ``` ### [Setup (nearly identical to tsup)](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#setup-nearly-identical-to-tsup) ```typescript // tsdown.config.ts import { defineConfig } from "tsdown" export default defineConfig({ entry: ["src/index.ts"], format: ["esm", "cjs"], dts: true, clean: true, sourcemap: true, external: ["react"], }) ``` ```bash # Commands are the same as tsup: npx tsdown # Build npx tsdown --watch # Watch mode ``` ### [tsup → tsdown migration](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#tsup--tsdown-migration) ```bash # Install: npm uninstall tsup npm install -D tsdown # Rename config file: mv tsup.config.ts tsdown.config.ts # Update import: # - import { defineConfig } from "tsup" # + import { defineConfig } from "tsdown" # Update package.json scripts: # - "build": "tsup" # + "build": "tsdown" ``` * * * ## [unbuild](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#unbuild) [unbuild](https://github.com/unjs/unbuild) — UnJS library bundler: ### [Setup](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#setup-1) ```typescript // build.config.ts import { defineBuildConfig } from "unbuild" export default defineBuildConfig({ entries: ["src/index"], rollup: { emitCJS: true, // Also emit CommonJS }, declaration: true, // Generate .d.ts clean: true, }) ``` ```bash # Build: npx unbuild # Stub mode (development): npx unbuild --stub ``` ### [Stub mode (unique to unbuild)](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#stub-mode-unique-to-unbuild) ```typescript // "Stub mode" — generates proxy files that require/import the source directly // No build step needed during development! // dist/index.mjs (stub): // export * from "../src/index.ts" // dist/index.js (stub): // module.exports = require("../src/index.ts") // via jiti // Benefits: // - No watch mode needed — file changes are picked up immediately // - Faster feedback loop when developing a library locally // - Link the package to a consumer with npm link — changes are live // Production build: // npx unbuild ← produces real bundles (no stub) ``` ### [Multiple presets](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#multiple-presets) ```typescript import { defineBuildConfig } from "unbuild" export default defineBuildConfig([\ // Main package:\ {\ entries: ["src/index"],\ declaration: true,\ rollup: { emitCJS: true },\ },\ // CLI entry (no types needed):\ {\ entries: [{ input: "src/cli", name: "cli" }],\ declaration: false,\ rollup: {\ emitCJS: false,\ inlineDependencies: true, // Bundle everything into the CLI binary\ },\ },\ ]) ``` ### [Used by the UnJS ecosystem](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#used-by-the-unjs-ecosystem) ``` unbuild is used by: - nuxt → @nuxt/... packages - nitro → the Nuxt server engine - h3 → the HTTP framework - ofetch → the fetch wrapper - Most @unjs/* packages If you're contributing to or building in this ecosystem, unbuild is the natural choice. ``` * * * ## [Feature Comparison](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#feature-comparison) | Feature | tsup | tsdown | unbuild | | --- | --- | --- | --- | | Build engine | esbuild (Go) | Rolldown (Rust) | Rollup (JS) | | Build speed | Fast | ⚡ Fastest | Moderate | | ESM + CJS | ✅ | ✅ | ✅ | | .d.ts generation | ✅ | ✅ | ✅ | | Stub mode (no build) | ❌ | ❌ | ✅ | | Code splitting | ✅ | ✅ | ✅ | | treeshake | ✅ | ✅ | ✅ | | Plugin ecosystem | esbuild plugins | Rolldown plugins | Rollup plugins | | TypeScript config | tsup.config.ts | tsdown.config.ts | build.config.ts | | Community size | ⭐ Large | Growing fast | Medium | | Weekly downloads | ~6M | ~500K | ~3M | * * * ## [When to Use Each](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#when-to-use-each) **Choose tsup if:** - The safe, battle-tested choice — most tutorials and examples use it - Large community, most Stack Overflow answers, most plugins - Works for 95% of library use cases out of the box **Choose tsdown if:** - Build speed is a priority (large libraries, frequent CI builds) - You're migrating from tsup — API is nearly identical - On the cutting edge of tooling in 2026 **Choose unbuild if:** - Working in the Nuxt, Nitro, or UnJS ecosystem - Want stub mode for instant development without watch rebuilds - Need Rollup-specific plugins not available in esbuild/Rolldown **Also consider:** - **Vite Library Mode** — for libraries that need Vite plugins (CSS modules, etc.) - **pkgroll** — minimal bundler for packages with simple needs - **microbundle** — smaller alternative, but less actively maintained in 2026 * * * ## [Methodology](https://www.pkgpulse.com/blog/tsup-vs-tsdown-vs-unbuild-typescript-library-bundling-2026\#methodology) Download data from npm registry (weekly average, February 2026). Feature comparison based on tsup v8.x, tsdown v0.x, and unbuild v2.x. _[Compare build tooling and bundler packages on PkgPulse →](https://www.pkgpulse.com/)_ ## Comments ### The 2026 JavaScript Stack Cheatsheet One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly. Get the Free Cheatsheet