---
url: /guide/migrate-from-tsup.md
---
# Migrate from tsup

[tsup](https://tsup.egoist.dev/) is a powerful and widely-used bundler that shares many similarities with `tsdown`. While `tsup` is built on top of [esbuild](https://esbuild.github.io/), `tsdown` leverages the power of [Rolldown](https://rolldown.rs/) to deliver a **faster** and more **powerful** bundling experience.

## Migration Guide

If you're currently using `tsup` and want to migrate to `tsdown`, the process is straightforward thanks to the dedicated `migrate` command:

```bash
npx tsdown-migrate
```

For monorepos, you can specify directories using glob patterns:

```bash
npx tsdown-migrate packages/*
```

Or specify multiple directories explicitly:

```bash
npx tsdown-migrate packages/foo packages/bar
```

> \[!WARNING]
> Please save your changes before migration. The migration process may modify your configuration files, so it's important to ensure all your changes are committed or backed up beforehand.

> \[!TIP]
> The migration tool will automatically install dependencies after migration. Make sure to run the command from within your project directory.

> \[!IMPORTANT]
> Migration is a **two-stage** process. The migration tool installs **tsdown v0.22.14**, the last version that still accepts deprecated tsup-compatible options (with warnings). Newer versions of tsdown have removed these options entirely — they fail type checking and are silently ignored at runtime.
>
> 1. Run your build on v0.22.14 and resolve **every** deprecation warning — zero warnings means your config is fully migrated.
> 2. Only then upgrade `tsdown` to the latest version (`^0.23.0` or later).

### Migration Options

The `migrate` command supports the following options to customize the migration process:

* `[...dirs]`: Specify directories to migrate. Supports glob patterns (e.g., `packages/*`). Defaults to the current directory if not specified.
* `--dry-run` (or `-d`): Perform a dry run to preview the migration without making any changes.
* `--yes` (or `-y`): Skip the migration confirmation prompt. This is required in non-interactive environments.
* `--package-manager <name>`: Override package manager auto-detection for dependency installation.
* `--no-install`: Skip dependency installation, for example when another migration tool will install dependencies afterward.

For non-interactive environments, explicitly confirm the migration:

```bash
npx tsdown-migrate --yes --no-install
```

When installation is enabled, the migration tool detects the package manager from the project's `packageManager` field or lockfile. If detection fails in a non-interactive environment, pass `--package-manager <name>` or `--no-install`.

With these options, you can easily tailor the migration process to fit your specific project setup.

## Differences from tsup

While `tsdown` aims to be highly compatible with `tsup`, there are some differences to be aware of:

### Default Values

| Option   | tsup     | tsdown                                                             |
| -------- | -------- | ------------------------------------------------------------------ |
| `format` | `'cjs'`  | `'esm'`                                                            |
| `clean`  | `false`  | `true` (cleans `outDir` before each build)                         |
| `dts`    | `false`  | Auto-enabled if `package.json` contains `types` or `typings` field |
| `target` | *(none)* | Auto-reads from `engines.node` in `package.json`                   |

### Option Renames

Some options have been renamed for clarity:

| tsup                       | tsdown                        | Notes                              |
| -------------------------- | ----------------------------- | ---------------------------------- |
| `entryPoints`              | `entry`                       | Also deprecated in tsup itself     |
| `cjsInterop`               | `cjsDefault`                  | CJS default export handling        |
| `esbuildPlugins`           | `plugins`                     | Now uses Rolldown/Unplugin plugins |
| `outExtension`             | `outExtensions`               | Custom output extensions           |
| `skipNodeModulesBundle`    | `deps: { neverBundle: true }` | Externalize all dependencies       |
| `publicDir`                | `copy`                        | Copy static files to output        |
| `bundle: false`            | `unbundle: true`              | Inverted to positive form          |
| `removeNodeProtocol: true` | `nodeProtocol: 'strip'`       | More flexible with multiple modes  |
| `injectStyle: true`        | `css: { inject: true }`       | Moved into CSS namespace           |

None of the old names work in the latest tsdown — they fail type checking and are silently ignored at runtime. Those that previously emitted deprecation warnings (`entryPoints`, `outExtension`, `skipNodeModulesBundle`, `publicDir`, `bundle`, `removeNodeProtocol`, `injectStyle`) were accepted up to tsdown v0.22.14 — if your config still uses any of them, migrate on v0.22.14 first.

### Deprecated but Compatible Options

The following tsup options still work in tsdown for backward compatibility, but they emit deprecation warnings and **will be removed in a future version**. Migrate them to the preferred alternatives immediately.

| tsup (deprecated)   | tsdown (preferred)              | Notes                   |
| ------------------- | ------------------------------- | ----------------------- |
| `external: [...]`   | `deps: { neverBundle: [...] }`  | Moved to deps namespace |
| `noExternal: [...]` | `deps: { alwaysBundle: [...] }` | Moved to deps namespace |

tsdown also adds `deps.onlyBundle` for whitelisting allowed bundled packages.

### Output Filename Differences

For IIFE builds, `tsdown` emits names like `[name].iife.js`, while `tsup` commonly emitted `[name].global.js`. `outExtensions` customizes output extensions or suffixes, but it does not remove the built-in `.iife` or `.umd` segment. To preserve older full filename patterns, use Rolldown output options:

```ts
export default {
  format: 'iife',
  outputOptions: {
    entryFileNames: '[name].global.js',
  },
}
```

### Plugin System

tsdown uses [Rolldown](https://rolldown.rs/) plugins instead of esbuild plugins. If you use [unplugin](https://github.com/unjs/unplugin) plugins, update the import path:

```ts
// Before (tsup)
import plugin from 'unplugin-example/esbuild'
// After (tsdown)
import plugin from 'unplugin-example/rolldown'
```

### Unsupported Options

The following tsup options are not available in tsdown:

| Option                        | Status         | Alternative                                                |
| ----------------------------- | -------------- | ---------------------------------------------------------- |
| `splitting: false`            | Always enabled | Code splitting cannot be disabled                          |
| `metafile`                    | Not available  | Use `devtools: true` for bundle analysis via Vite DevTools |
| `swc`                         | Not supported  | tsdown uses oxc for transformation (built-in)              |
| `experimentalDts`             | Superseded     | Use the `dts` option instead                               |
| `legacyOutput`                | Not supported  | No alternative                                             |
| `plugins` (tsup experimental) | Incompatible   | Migrate to Rolldown plugins                                |

If you find an option missing that you need, please [open an issue](https://github.com/rolldown/tsdown/issues) to let us know your requirements.

### New Features in tsdown

`tsdown` introduces many features not available in `tsup`:

* **`nodeProtocol`**: Control how Node.js built-in module imports are handled:
  * `true`: Add `node:` prefix to built-in modules (e.g., `fs` → `node:fs`)
  * `'strip'`: Remove `node:` prefix from imports (e.g., `node:fs` → `fs`)
  * `false`: Keep imports as-is (default)
* **`workspace`**: Build multiple packages in a monorepo with `workspace: 'packages/*'`
* **`exports`**: Auto-generate the `exports` field in `package.json` with `exports: true`
* **`publint`** / **`attw`**: Validate your package for common issues and type correctness
* **`exe`**: Bundle as a Node.js standalone executable (SEA) with `exe: true`
* **`devtools`**: Vite DevTools integration for bundle analysis with `devtools: true`
* **`hooks`**: Lifecycle hooks (`build:prepare`, `build:before`, `build:done`) for custom build logic
* **`css`**: Full CSS pipeline with preprocessors, Lightning CSS, PostCSS, CSS modules, and code splitting
* **`globImport`**: Support for `import.meta.glob` (Vite-style glob imports)

Please review your configuration after migration to ensure it matches your expectations.

> \[!TIP]
> An AI skill is available for guided migration assistance: `npx skills add rolldown/tsdown --skill tsdown-migrate`

## Acknowledgements

`tsdown` would not have been possible without the inspiration and contributions of the open-source community. We would like to express our heartfelt gratitude to the following:

* **[tsup](https://tsup.egoist.dev/)**: `tsdown` was heavily inspired by `tsup`, and even incorporates parts of its codebase. The simplicity and efficiency of `tsup` served as a guiding light during the development of `tsdown`.
* **[@egoist](https://github.com/egoist)**: The creator of `tsup`, whose work has significantly influenced the JavaScript and TypeScript tooling ecosystem. Thank you for your dedication and contributions to the community.
