Skip to main content
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