Plate 91
tsup vs unbuild: Should You Switch? 931ms vs 2034ms
tsup 8.5.1 vs unbuild 3.6.1 on a 150-module TypeScript library (ESM+CJS): median 931 ms vs 2034 ms (~2.2x); with .d.ts 3213 ms vs 3740 ms (~1.16x). unbuild ESM 4.9% smaller, CJS 8.3% smaller; tsup README says not actively maintained.
Aditya Challa5 min read
On a 150-file TypeScript library built to ESM plus CJS, tsup 8.5.1 took a median 931 ms and unbuild 3.6.1 took 2034 ms (~2.2x slower). With .d.ts generation on, the gap shrank to 3213 ms vs 3740 ms, and unbuild's ESM output was about 5% smaller (114.6 KB vs 120.5 KB).
Short answer: stay on tsup if raw build speed is your main concern and you are fine with a project whose README now says it is not actively maintained. Move to unbuild if you want an actively developed Rollup-based builder, smaller output, and --stub linking during development. No affiliate links in this post.
What I tested
- Machine: 8 vCPU Intel Xeon / 15 GB RAM Linux cloud box, Node 20.19.2, tested 5 Oct 2026 (about 23:16 to 23:17 IST). Shared box; variants interleaved each round.
- Fixture: 150
.tsmodules plus oneindex.tsbarrel. Each module has an interface, a union type, a string enum, a generic class, and a function that imports from the previous module (600 exports in total). - Tools:
tsup8.5.1 (esbuild 0.27.7) andunbuild3.6.1 (rollup 4.64.0, rollup-plugin-dts 6.5.1), TypeScript 5.9.3. npm installed TS 5.9 rather than the 7.0latesttag for these two builders. Both set totarget: es2022, formats ESM and CJS, one entry. - Timing: one warmup each, then five interleaved rounds of four variants: tsup and unbuild, each with and without declaration output. Wall clock via
process.hrtimearound the fullnpxcommand. - Findings: no-dts medians 930.9 ms (tsup) vs 2033.9 ms (unbuild); with dts 3213.0 ms vs 3740.2 ms. Both outputs passed a smoke test:
util149(1)returned 11176 from ESM and CJS, and both exposed 600 exports. - One surprise: unbuild exited with code 1 on the first run because of a "Potential missing package.json files" warning about my bare
package.json. SettingfailOnWarn: false(or adding a properexportsmap) fixed it. - Not tested: tsdown, unbuild's
mkdistbundleless mode, multi-entry libraries, watch mode, Vue or JSX sources, Windows/macOS.
Is tsup faster than unbuild?
| Variant | Median wall | Runs (ms) |
|---|---|---|
| tsup 8.5.1, ESM+CJS | 931 ms | 1027 / 890 / 931 / 1004 / 870 |
| unbuild 3.6.1, ESM+CJS | 2034 ms | 2034 / 2099 / 1939 / 2054 / 1964 |
| tsup 8.5.1, ESM+CJS + .d.ts | 3213 ms | 3448 / 3130 / 3162 / 3213 / 3234 |
| unbuild 3.6.1, ESM+CJS + .d.ts | 3740 ms | 3817 / 3740 / 3570 / 3802 / 3707 |
Yes. tsup transpiles and bundles with esbuild, while unbuild runs Rollup with an esbuild transform, and on this fixture that Rollup path cost about 1.1 s more. Once declaration files are on, both spend most of their time in the TypeScript compiler, so the difference drops to about 0.5 s (~1.16x). For most libraries that publish types, that is the number that matters.
The bundler underneath is the real split. The Rolldown vs Rollup post and the SWC vs esbuild vs Babel post have engine-level numbers from the same box.
Which one produces smaller output?
| File | tsup 8.5.1 | unbuild 3.6.1 |
|---|---|---|
| ESM bundle | index.js 120,537 B | index.mjs 114,578 B |
| CJS bundle | index.cjs 136,252 B | index.cjs 124,942 B |
| Type declarations | index.d.ts + index.d.cts, 106,671 B each | index.d.ts + .d.mts + .d.cts, 105,187 B each |
| Total on disk | 470,131 B | 555,081 B |
unbuild's JS was smaller: 4.9% for ESM and 8.3% for CJS. Most of the gap is tsup writing a // src/modN.ts comment before each module and emitting each class as var Svc0 = class with an extra prefix; field line, where Rollup kept plain class Svc0. unbuild's total is larger only because declaration mode compatible writes a third .d.ts copy. Neither output was minified; if you minify, see the Terser vs esbuild vs SWC minify post.
Should you switch from tsup to unbuild?
| Situation | My pick |
|---|---|
| Single-entry library that ships types, builds in CI | Either works; unbuild cost about 0.5 s more here |
| You rebuild constantly without types (local loops) | tsup was 2.2x faster in this test |
| You want an actively developed tool for the next few years | Plan a move: unbuild, or tsdown as the tsup README suggests |
| You link the package into an app during development | unbuild, for unbuild --stub |
Strict "no warnings" CI and no exports map yet | Fix package.json first or unbuild will fail the build |
Bottom line: tsup won on speed (931 ms vs 2034 ms without types, 3.2 s vs 3.7 s with types) and unbuild won on output size and maintenance status. Who should not switch yet: teams on tsup with no build pain and no plan to touch their tooling this quarter. If you are also weighing TypeScript itself, read the TypeScript 7 upgrade post first, because declaration builds here still ran on TS 5.9.
How this was made: I generated the TypeScript fixture, ran the interleaved builds on the ShopperCove test box, and kept the timing and size JSON; the write-up was drafted with AI help and checked against that output.
Sources
- https://github.com/egoist/tsup
- https://tsup.egoist.dev/
- https://github.com/unjs/unbuild
- https://www.npmjs.com/package/tsup
- https://www.npmjs.com/package/unbuild
- https://tsdown.dev/guide/migrate-from-tsup
Related
- https://www.shoppercove.com/blog/rolldown-vs-rollup
- https://www.shoppercove.com/blog/swc-vs-esbuild-babel
- https://www.shoppercove.com/blog/terser-vs-esbuild-swc-minify
- https://www.shoppercove.com/blog/typescript-7-should-you-upgrade
- https://www.shoppercove.com/blog/tsx-vs-ts-node
- https://www.shoppercove.com/blog/rspack-vs-webpack
- https://www.shoppercove.com/blog/turbo-vs-nx
- https://www.shoppercove.com/blog/knip-vs-depcheck
Lab evidence
What I found running this
Hands-on on ShopperCove box 5 Oct 2026 ~23:16-23:17 IST (8 vCPU Intel Xeon / 15 GB shared Linux, Node 20.19.2). Fixture: 150 .ts modules + index.ts barrel (interface, union type, string enum, generic class, chained function each; 600 exports). Versions: tsup 8.5.1 (esbuild 0.27.7), unbuild 3.6.1 (rollup 4.64.0, rollup-plugin-dts 6.5.1), typescript 5.9.3; target es2022, ESM+CJS, single entry. 1 warmup each, 5 interleaved rounds. Medians: tsup no-dts 930.9 ms (1027.3/890/930.9/1003.7/870.2); unbuild no-dts 2033.9 ms (2033.9/2099.3/1939/2054.2/1964.2); tsup dts 3213 ms (3447.8/3130.3/3161.6/3213/3234.1); unbuild dts (compatible) 3740.2 ms (3817.4/3740.2/3570.4/3802.4/3707.1). Sizes: tsup index.js 120537, index.cjs 136252, d.ts/d.cts 106671 each; unbuild index.mjs 114578, index.cjs 124942, d.ts/d.mts/d.cts 105187 each. Smoke: util149(1)=11176 ESM+CJS both, 600 exports. Gotcha: unbuild failOnWarn exit 1 on missing package.json exports; set failOnWarn:false. Raw: /workspace/bench-tsup-unbuild/res-tsup-unbuild.json. Not tested: tsdown, mkdist mode, multi-entry, watch, Windows/macOS. No affiliate.
Related links
Plate 31
Should You Leave Rollup? 964ms vs 246ms
Rolldown 1.2.12 vs Rollup 4.64.0 on 501 ESM files (197380 B source): median wall 245.617 ms vs 964.3 ms (~3.9x). Output 130522 vs 126894 B. Direct node bin, 5 interleaved warm runs, no minify.
5 Oct 2026
Plate 74
Should You Leave Nx? 3378ms vs 1177ms
Turbo 2.11.7 vs Nx 23.2.1 on 7 packages / 336 JS files: cold median 1177 ms vs 3378 ms (~2.9x). Warm both 7/7 local cache hits (Turbo wall ~138 ms / tool ~16 ms; Nx wall ~668 ms with daemon / tool ~31 ms).
5 Oct 2026
Plate 99
Rspack vs Webpack: Which Builds Faster? 0.54s vs 4.3s
Rspack 2.2.8 vs Webpack 5.111.1 on a 201-module React fixture: cold production medians 0.54 s vs 4.33 s (~8.0x). Webpack+Babel vs Rspack+builtin:swc-loader. Raw bundles 174 KB vs 365 KB; gzip ~48 KB vs ~52 KB.
5 Oct 2026