Building a TypeScript Library in 2025
If you've built a TypeScript library recently, you know the pain. Heavy, slow TypeScript declaration builds that take seconds to complete. Your development flow constantly interrupted while waiting for tsup to finish.
It's 2025. We can do better.
Speed Revolution
Here's what modern TypeScript library development should look like:
$ tsup src/index.ts
✓ Build completed in 1.4s
$ bunup src/index.ts
✓ Build completed in 37ms
37 milliseconds. Not 1.8 seconds. Not 400ms. 37ms.
Bunup is powered by Bun's native bundler, making it fast by default with instant TypeScript declaration generation.
This isn't just about saving time. Instant builds transform your development workflow, enabling faster iteration, efficient watch mode, frictionless pre-commit hooks, and a more enjoyable coding experience.
Modern TypeScript
TypeScript 5.5 introduced isolatedDeclarations
, a game-changer for library authors that bunup uses for fastest declaration generation.
Why It Matters
Traditional declaration generation analyzes your entire codebase to infer types:
export function getUserData(id: string) {
return database.users.find(user => user.id === id);
}
With isolatedDeclarations, you're explicit about your public API:
export function getUserData(id: string): Promise<User | null> {
return database.users.find(user => user.id === id);
}
The Benefits
The advantages are compelling: 10x faster declaration generation, more predictable types for library consumers, better encapsulation where your internal types stay internal, and clearer intent as you explicitly define what's public.
Enable it in your tsconfig.json:
{
"compilerOptions": {
"declaration": true,
"isolatedDeclarations": true
}
}
Modern bundlers should embrace this, not fight it.
Beautiful Ergonomics
Fast builds are just the beginning. Modern library tooling should understand your needs.
Package Exports
export default defineConfig({
entry: ['src/index.ts'],
plugins: [exports()]
});
Your package.json automatically gets:
{
"name": "my-library",
"version": "1.0.0",
"description": "A library for my project",
"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"
}
}
}
}
No more manual exports field maintenance. No more forgetting the types field. No more mismatched paths.
Unused Dependencies
plugins: [unused()]
Get warnings about dependencies you're not actually using, dev dependencies that should be regular dependencies, missing peer dependencies, and a lot more beautiful plugins that you love.
Monorepo Support
Building multiple packages? Bunup workspaces make it effortless:
export default defineWorkspace([
{
name: "core",
root: "packages/core",
},
{
name: "utils",
root: "packages/utils",
}
]);
and then run:
$ bunup
What's more, you can build all packages with a single command, with incremental builds that only rebuild what's changed. Perfect for design systems, component libraries, and complex projects.
Star bunup on GitHub and join the speed revolution.