Runtime
Bundler
Package Manager
Test Runner
Guides
Reference
Blog
Install Bun
Package Manager Workspace Management

Workspaces

Develop complex monorepos with multiple independent packages

Bun supports workspaces in package.json. With workspaces, you develop several independent packages in a single repository, a monorepo.

A monorepo commonly has this structure:

File Tree
<root>
├── README.md
├── bun.lock
├── package.json
├── tsconfig.json
└── packages
    ├── pkg-a
    │   ├── index.ts
    │   ├── package.json
    │   └── tsconfig.json
    ├── pkg-b
    │   ├── index.ts
    │   ├── package.json
    │   └── tsconfig.json
    └── pkg-c
        ├── index.ts
        ├── package.json
        └── tsconfig.json

The "workspaces" key in the root package.json lists the subdirectories to treat as workspaces. By convention, they live in a directory called packages.

package.json
{
  "name": "my-project",
  "version": "1.0.0",
  "workspaces": ["packages/*"],
  "devDependencies": {
    "example-package-in-monorepo": "workspace:*"
  }
}

Glob support — Bun supports full glob syntax in "workspaces", including negative patterns such as !**/excluded/**. See supported glob patterns.

package.json
{
  "name": "my-project",
  "version": "1.0.0",
  "workspaces": ["packages/**", "!packages/**/test/**", "!packages/**/template/**"]
}

Each workspace has its own package.json. To reference another package in the monorepo, use a semver range or the workspace protocol (for example workspace:*) as the version in your package.json.

packages/pkg-a/package.json
{
  "name": "pkg-a",
  "version": "1.0.0",
  "dependencies": {
    "pkg-b": "workspace:*"
  }
}

bun install installs dependencies for all workspaces in the monorepo, de-duplicating packages if possible. To install dependencies for specific workspaces only, use the --filter flag.

# Install dependencies for all workspaces starting with `pkg-` except for `pkg-c`
$ bun install --filter "pkg-*" --filter "!pkg-c"

# Paths can also be used. This is equivalent to the command above.
$ bun install --filter "./packages/pkg-*" --filter "!pkg-c" # or --filter "!./packages/pkg-c"

When publishing, Bun replaces workspace: versions with the package's package.json version:

"workspace:*" -> "1.0.1"
"workspace:^" -> "^1.0.1"
"workspace:~" -> "~1.0.1"

A specific version takes precedence over the package's package.json version:

"workspace:1.0.2" -> "1.0.2" // Even if current version is 1.0.1

Workspaces have a few major benefits.

  • Code can be split into logical parts. If one package relies on another, add it as a dependency in package.json. If package b depends on a, bun install installs your local packages/a directory into node_modules instead of downloading it from the npm registry.
  • Dependencies can be de-duplicated. If a and b share a common dependency, it is hoisted to the root node_modules directory. This saves disk space and minimizes the "dependency hell" of multiple versions of a package installed at once.
  • Run scripts in multiple packages. Use the --filter flag to run package.json scripts in several packages at once, or --workspaces to run scripts across all workspaces.

Share versions with Catalogs

When many packages need the same dependency versions, define those versions once in a catalog in the root package.json and reference them from your workspaces with the catalog: protocol. Updating the catalog updates every package that references it. See Catalogs.

⚡️ Speed — Installs are fast, even for big monorepos. Bun installs the Remix monorepo in about 500ms on Linux.

  • 28x faster than npm install
  • 12x faster than yarn install (v1)
  • 8x faster than pnpm install