In the ground since Sun Nov 16 2025
Last watered inSun Nov 16 2025
pnpm (performant npm) is a fast, disk space efficient package manager that uses a content-addressable store and symlinks to manage dependencies. In monorepo contexts, it provides workspace protocol features that allow packages to reference each other locally without publishing to npm.
Key Achievement: Understanding how pnpm's workspace protocol enables local package linking in monorepos, and how this integrates with Turborepo's build orchestration.
pnpm is an alternative to npm and yarn that solves several problems with traditional Node.js package managers:
npm and yarn (classic):
Problems:
pnpm uses a content-addressable store:
Benefits:
When you run pnpm install:
Example:
Why this matters:
When building a monorepo package that depends on another local package, you need to tell pnpm to link them together instead of fetching from npm.
We're building @peek-a-boo/react-sdk which needs types from @peek-a-boo/core:
Without workspace linking:
Add the local package as a dependency using pnpm's workspace protocol:
workspace: - Protocol telling pnpm "this is a local package in the monorepo"
* - Version range meaning "use whatever version the local package defines"
Alternatives:
Recommendation: Use workspace:* for monorepo packages - lets you change versions freely.
Created src/client/types.ts with import:
Ran type check:
Edited packages/react-sdk/package.json:
Why in dependencies, not devDependencies?
What happened:
pnpm reads workspace:*
Creates symlink
Outputs
Check the actual symlink:
pnpm handles linking, Turborepo handles build order.
Once pnpm creates the symlinks, Turborepo reads package.json dependencies to build a graph:
When you run pnpm build:
Turborepo reads the graph:
Build order:
Caching:
One common question: Where do I run pnpm add? At the root or in the specific package?
The answer depends on who needs the package.
When to use: Package is only needed by ONE workspace package.
Example: Adding vite-plugin-dts to react-sdk (TypeScript declaration generator for Vite).
Run from anywhere in the monorepo:
What happens:
Result:
When to use Method B:
When to use Method A:
When to use: Package is needed by MULTIPLE workspace packages, or for workspace-wide tooling.
What the -w flag does:
When to install at root:
✅ Good use cases:
❌ Bad use cases:
Rule of thumb: If it's in the root package.json, it should be used by ALL or MOST packages.
Let's add vite-plugin-dts to the react-sdk (needed for TypeScript declaration generation).
Question: Who needs vite-plugin-dts?
Decision: Add to react-sdk package, not root.
Using --filter (from root):
Or cd first:
| Command | Where it installs | When to use | |---------|-------------------|-------------| | pnpm add pkg --filter=@scope/name | Specific package (from anywhere) | Most common - package-specific deps | | cd path/to/pkg && pnpm add pkg | Specific package (after cd) | When already in package directory | | pnpm add -D -w pkg | Workspace root | Shared tooling (ESLint, TypeScript, etc.) | | pnpm add -D pkg (from root) | ❌ Error | Safety - use -w flag explicitly |
Error: "Running this command will add the dependency to the workspace root"
Fix: Add -w flag if you really want root install:
Package not found after install:
Wrong package.json was updated:
Check if symlink exists:
Verify workspace resolution:
Force reinstall:
Check Turborepo graph: