Bootstrap a pnpm Monorepo with Workspace Protocol
Create a pnpm workspace with shared tooling, declare cross-package dependencies with the workspace protocol, and verify that changes in one package rebuild dependents.
Task
Set up a pnpm monorepo where local packages depend on each other through the workspace protocol and a change in one rebuilds its dependents.
When to use this
Three packages share a common logger and a config loader. Today they copy code between repos, so a fix in one lands days late in the others. The team wants a single repo with shared tooling.
Prerequisites
- pnpm 9 or later installed globally
- Node.js 20 LTS
- A git repository with an initial commit
Steps
1. Create the workspace manifest and declare the packages directory.
// pnpm-workspace.yaml
packages:
- "packages/*"
- "apps/*"
Expected output: pnpm list --depth -1 shows the workspace root and recognizes the packages and apps globs.
2. Add a shared package and reference it from a consumer using the workspace protocol.
// apps/web/package.json
"dependencies": {
"@repo/logger": "workspace:^"
}
Expected output: pnpm install symlinks @repo/logger into apps/web/node_modules and the import resolves.
3. Run a build that depends on the shared package and confirm a change propagates.
pnpm -r --filter=web... buildExpected output: Editing the logger source and rerunning the build produces a changed web bundle.
Troubleshooting
Problem
workspace:^ does not resolve
Solution
Confirm the shared package name in its package.json matches the dependency key exactly, including the @repo scope.
Problem
Builds run in the wrong order
Solution
Use the ... filter syntax to include dependents, or run pnpm -r --sort rebuild to respect topological order.
Limitations
- The workspace protocol only resolves inside the same workspace; publishing requires replacing it with a real version.
- pnpm does not hoist by default, so a package that relies on phantom dependencies may break.
One repo, one toolchain
A pnpm workspace lets every package share the same lockfile, the same TypeScript config, and the same test runner. Cross-package dependencies use the workspace protocol so a local edit is visible immediately, without a publish round-trip.
# pnpm-workspace.yaml
packages:
- "packages/*"
- "apps/*"
Wire the dependencies
Reference a local package with workspace:^ and pnpm symlinks it into the consumer. A change in the logger is visible to the web app on the next build, no version bump required.
{
"dependencies": { "@repo/logger": "workspace:^" }
}
Expected outcome
pnpm -r build builds packages in topological order, a change in @repo/logger rebuilds apps/web, and the lockfile stays single and shared across the whole workspace.