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:
<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.jsonThe "workspaces" key in the root package.json lists the subdirectories to treat as workspaces. By convention, they live in a directory called packages.
{
"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.
{
"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.
{
"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.1Workspaces 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 packagebdepends ona,bun installinstalls your localpackages/adirectory intonode_modulesinstead of downloading it from the npm registry. - Dependencies can be de-duplicated. If
aandbshare a common dependency, it is hoisted to the rootnode_modulesdirectory. 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
--filterflag to runpackage.jsonscripts in several packages at once, or--workspacesto 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
