I care about details and perfection in user interfaces. I love writing low-level code and have a passion for mathematics.

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:

bash
$ 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:

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

bunup.config.ts
export default defineConfig({
  entry: ['src/index.ts'],
  plugins: [exports()]
});

Your package.json automatically gets:

package.json
{
  "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

bunup.config.ts
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.

The above are just a few examples, there is a lot more to explore.

Monorepo Support

Building multiple packages? Bunup workspaces make it effortless:

bunup.config.ts
export default defineWorkspace([
  {
    name: "core",
    root: "packages/core",
  },
  {
    name: "utils", 
    root: "packages/utils",
  }
]);

and then run:

bash
$ 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.