- Replace .eslintrc.cjs with eslint.config.mjs (ESLint 9 flat config)
using direct eslint-plugin-solid + @typescript-eslint/parser approach
- Add @typescript-eslint/parser to root devDependencies
- Add main/module/types top-level fields to packages/core/package.json
- Add resolve.conditions to packages/core/vite.config.ts
- Create packages/core/tsconfig.test.json for test type-checking
- Remove empty paths:{} from packages/core/tsconfig.json
361 lines
11 KiB
Markdown
361 lines
11 KiB
Markdown
[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 |