dts-bundle-generator vs rollup-plugin-dts vs tsup dts: TypeScript Declaration Bundling (2026)
TL;DR
dts-bundle-generator is a standalone CLI tool that generates a single .d.ts bundle from your TypeScript library — no Rollup required, fast, handles re-exports and barrel files. rollup-plugin-dts is a Rollup plugin that bundles .d.ts files — integrates into your existing Rollup/Vite build pipeline, processes tsc output into a single declaration file. tsup dts is tsup's built-in declaration generation — tsup --dts generates declarations alongside your JavaScript bundle automatically. In 2026: tsup --dts for the simplest workflow, dts-bundle-generator for standalone CLI usage, rollup-plugin-dts for complex Rollup pipelines.
Key Takeaways
- dts-bundle-generator: ~500K weekly downloads — standalone CLI, no Rollup dependency
- rollup-plugin-dts: ~1M weekly downloads — Rollup plugin, processes tsc output
- tsup --dts: built into tsup (~5M weekly downloads) — zero config, esbuild + tsc
- The problem:
tscgenerates one.d.tsper source file — consumers want ONE file - A single
.d.tsbundle: smaller package, cleaner API surface, faster IDE autocomplete - All three produce the same result — a single declaration file for your package entry
The Problem
tsc output (individual files):
dist/
index.d.ts → re-exports from multiple files
utils/helpers.d.ts → internal types leaked
types/index.d.ts → intermediate types
core/parser.d.ts → implementation detail types
What npm consumers want:
dist/
index.d.ts → ONE file with all public types
index.js → bundled JavaScript
Benefits of bundled .d.ts:
✅ Smaller npm package (fewer files)
✅ Faster IDE autocomplete (one file to parse)
✅ Cleaner public API (internal types not exposed)
✅ No deep import accidents (users can't import internal types)
tsup --dts (Simplest)
tsup — built-in dts generation:
Zero config
# Generate JS bundle + .d.ts in one command:
tsup src/index.ts --dts --format esm,cjs
# Output:
# dist/index.js (ESM)
# dist/index.cjs (CJS)
# dist/index.d.ts (declarations)
# dist/index.d.cts (CJS declarations)
tsup.config.ts
import { defineConfig } from "tsup"
export default defineConfig({
entry: ["src/index.ts"],
format: ["esm", "cjs"],
dts: true, // Generate .d.ts
clean: true,
sourcemap: true,
splitting: false,
minify: false, // Don't minify library code
})
package.json exports
{
"name": "my-library",
"version": "1.0.0",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"files": ["dist"],
"scripts": {
"build": "tsup"
}
}
DTS-only mode
// tsup.config.ts — separate dts build for complex cases:
export default defineConfig([
{
entry: ["src/index.ts"],
format: ["esm", "cjs"],
// JS only:
},
{
entry: ["src/index.ts"],
dts: { only: true }, // DTS only — no JS output
// Use when dts generation needs different config
},
])
How tsup --dts works
1. tsup uses esbuild to bundle JavaScript (fast)
2. tsup runs tsc to generate individual .d.ts files
3. tsup uses rollup-plugin-dts internally to bundle into one .d.ts
4. Both outputs land in dist/
Note: tsup --dts is slower than JS-only builds because tsc runs
For faster dev builds: tsup (no --dts) in dev, tsup --dts for release
dts-bundle-generator
dts-bundle-generator — standalone CLI:
CLI usage
npx dts-bundle-generator -o dist/index.d.ts src/index.ts
# Options:
npx dts-bundle-generator \
--project tsconfig.json \
--out-file dist/index.d.ts \
--no-check \
src/index.ts
Programmatic API
import { generateDtsBundle } from "dts-bundle-generator"
const result = generateDtsBundle([
{
filePath: "./src/index.ts",
output: {
noBanner: true,
exportReferencedTypes: false, // Don't export internal types
},
},
])
// result[0] is the bundled .d.ts content as a string
Build script
{
"scripts": {
"build:js": "esbuild src/index.ts --bundle --outfile=dist/index.js --format=esm",
"build:dts": "dts-bundle-generator -o dist/index.d.ts src/index.ts",
"build": "npm run build:js && npm run build:dts"
}
}
Advantages
dts-bundle-generator vs tsc + rollup-plugin-dts:
✅ Single step — no intermediate .d.ts files
✅ No Rollup dependency
✅ Faster for simple libraries
✅ Standalone CLI — works with any bundler
Limitations:
❌ Slower than rollup-plugin-dts for complex projects
❌ Less ecosystem integration (not a Rollup/Vite plugin)
❌ Some edge cases with complex re-exports
rollup-plugin-dts
rollup-plugin-dts — Rollup integration:
Setup
npm install -D rollup-plugin-dts typescript
rollup.config.ts
import { defineConfig } from "rollup"
import dts from "rollup-plugin-dts"
export default defineConfig([
// JavaScript bundle:
{
input: "src/index.ts",
output: [
{ file: "dist/index.js", format: "esm" },
{ file: "dist/index.cjs", format: "cjs" },
],
plugins: [/* esbuild, typescript, etc. */],
},
// Declaration bundle:
{
input: "dist/types/index.d.ts", // tsc output
output: { file: "dist/index.d.ts", format: "es" },
plugins: [dts()],
},
])
Two-step workflow
{
"scripts": {
"prebuild": "tsc --emitDeclarationOnly --outDir dist/types",
"build": "rollup -c",
"postbuild": "rm -rf dist/types"
}
}
Step 1: tsc → generates dist/types/*.d.ts (individual files)
Step 2: rollup + rollup-plugin-dts → bundles into dist/index.d.ts
Step 3: cleanup → remove intermediate dist/types/
With Vite
// vite.config.ts — using rollup-plugin-dts in Vite library mode:
import { defineConfig } from "vite"
import dts from "rollup-plugin-dts"
export default defineConfig({
build: {
lib: {
entry: "src/index.ts",
formats: ["es", "cjs"],
},
rollupOptions: {
plugins: [dts()],
},
},
})
Feature Comparison
| Feature | tsup --dts | dts-bundle-generator | rollup-plugin-dts |
|---|---|---|---|
| Bundler | tsup (esbuild) | Standalone | Rollup/Vite |
| JS + DTS in one step | ✅ | ❌ (DTS only) | ❌ (DTS only) |
| Zero config | ✅ | CLI args | Rollup config |
| Requires tsc first | No (runs internally) | No | Yes |
| Complex re-exports | ✅ | ⚠️ | ✅ |
| Speed | Medium | Fast (simple) | Fast |
| ESM + CJS .d.ts | ✅ (.d.ts + .d.cts) | ❌ (single) | ❌ (single) |
| Weekly downloads | ~5M (tsup) | ~500K | ~1M |
When to Use Each
Use tsup --dts if:
- Want the simplest possible library build setup
- One command: JS + declarations + source maps
- ESM + CJS dual output with matching declaration files
- Don't want to manage Rollup config
Use dts-bundle-generator if:
- Using esbuild, swc, or a non-Rollup bundler for JavaScript
- Need standalone CLI without Rollup dependency
- Simple library with straightforward exports
- CI scripts where you want explicit control
Use rollup-plugin-dts if:
- Already have a Rollup/Vite build pipeline
- Complex library with multiple entry points
- Need fine-grained control over declaration bundling
- Integrating into an existing rollup.config.ts
Methodology
Download data from npm registry (weekly average, February 2026). Feature comparison based on tsup v8.x, dts-bundle-generator v9.x, and rollup-plugin-dts v6.x.
Compare TypeScript build tools and bundler packages on PkgPulse →