Skip to main content

dts-bundle-generator vs rollup-plugin-dts vs tsup dts: TypeScript Declaration Bundling (2026)

·PkgPulse Team

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: tsc generates one .d.ts per source file — consumers want ONE file
  • A single .d.ts bundle: 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

Featuretsup --dtsdts-bundle-generatorrollup-plugin-dts
Bundlertsup (esbuild)StandaloneRollup/Vite
JS + DTS in one step❌ (DTS only)❌ (DTS only)
Zero configCLI argsRollup config
Requires tsc firstNo (runs internally)NoYes
Complex re-exports⚠️
SpeedMediumFast (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 →

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.