Catalog Pattern
How the pnpm catalog centralizes dependency versions across the monorepo -- full catalog reference, resolution mechanics, and update workflow.
What the Catalog Is
The pnpm catalog is a version map defined in the catalog: section of pnpm-workspace.yaml. When a package.json lists a dependency as "catalog:", pnpm resolves the version from this central map at install time.
# pnpm-workspace.yaml (excerpt)
catalog:
react: ^19.2.4
next: ^16.2.1
zod: ^4.3.6
// packages/api/package.json (excerpt)
{
"dependencies": {
"zod": "catalog:", // resolves to ^4.3.6 from the catalog
},
}
This means every package that uses "catalog:" for zod gets exactly the same version range. No drift, no mismatches, one place to update.
Full Catalog Reference
The complete catalog as defined in pnpm-workspace.yaml, organized by domain:
Auth and Authorization
| Package | Version |
|---|
@casl/ability | ^6.8.0 |
better-auth | ^1.6.0 |
AI
| Package | Version |
|---|
@google/genai | ^1.48.0 |
MCP
| Package | Version |
|---|
@modelcontextprotocol/sdk | ^1.28.0 |
React Ecosystem
| Package | Version |
|---|
react | ^19.2.4 |
react-dom | ^19.2.4 |
next | ^16.2.1 |
@tanstack/react-query | ^5.95.2 |
lucide-react | ^1.0.1 |
tRPC
| Package | Version |
|---|
@trpc/client | ^11.15.0 |
@trpc/react-query | ^11.15.0 |
@trpc/server | ^11.15.0 |
Database
| Package | Version |
|---|
drizzle-orm | ^0.45.2 |
drizzle-kit | ^0.31.10 |
drizzle-seed | ^0.3.1 |
pg | ^8.16.0 |
@types/pg | ^8.11.15 |
Cache
| Package | Version |
|---|
ioredis | ^5.10.1 |
Search
| Package | Version |
|---|
typesense | ^3.0.4 |
Background Jobs
| Package | Version |
|---|
inngest | ^4.1.0 |
Logging
| Package | Version |
|---|
pino | ^10.3.1 |
pino-pretty | ^13.1.3 |
Error Tracking
| Package | Version |
|---|
@sentry/nextjs | ^10.46.0 |
UI and Styling
| Package | Version |
|---|
tailwindcss | ^4.2.2 |
@tailwindcss/postcss | ^4.2.2 |
Validation
Documentation
| Package | Version |
|---|
fumadocs-core | ^16.7.10 |
fumadocs-ui | ^16.7.10 |
fumadocs-mdx | ^14.2.11 |
| Package | Version |
|---|
eslint | ^9.39.4 |
prettier | ^3.8.1 |
typescript | ^5.9.3 |
tsx | ^4.19.0 |
@vitejs/plugin-react | ^6.0.1 |
vitest | ^4.1.2 |
@vitest/coverage-v8 | ^4.1.2 |
How Resolution Works
When you run pnpm install:
- pnpm reads
pnpm-workspace.yaml and builds the catalog map.
- For each
package.json in the workspace, any dependency with "catalog:" as its version is resolved to the corresponding entry in the catalog.
- The resolved versions are written into
pnpm-lock.yaml.
- In CI and Docker builds,
pnpm install --frozen-lockfile uses the lockfile directly without re-resolving.
The "catalog:" string is a specifier, not a version range. It tells pnpm to look up the real range from the catalog. This is why pnpm update is dangerous -- it resolves the specifier and replaces "catalog:" with the actual version string, destroying the indirection.
How to Update a Catalog Dependency
Never use pnpm update for catalog dependencies. It replaces "catalog:" specifiers with pinned versions, breaking --frozen-lockfile in CI.
The correct workflow:
# 1. Edit the version in pnpm-workspace.yaml
# e.g., change zod: ^4.3.6 to zod: ^4.4.0
# 2. Run pnpm install to regenerate the lockfile
pnpm install
# 3. Verify the lockfile is consistent
pnpm install --frozen-lockfile
# 4. Run the full CI check suite
pnpm ci:check
Step 3 is the canary. If --frozen-lockfile fails, something went wrong -- most likely a pnpm update was run somewhere that corrupted a "catalog:" specifier.
Which Packages Use the Catalog
Every workspace package uses "catalog:" for its shared tooling dependencies (eslint, typescript, vitest). Feature-specific catalog usage varies:
| Package | Catalog Dependencies |
|---|
@repo/web | react, next, zod, tailwindcss, tRPC triplet, better-auth, inngest, @sentry/nextjs, fumadocs triplet, lucide-react |
@repo/db | drizzle-orm, drizzle-kit, drizzle-seed, pg, @types/pg |
@repo/api | @trpc/server, drizzle-orm, zod, @casl/ability, pino |
@repo/auth | better-auth, drizzle-orm |
@repo/ai | @google/genai, drizzle-orm |
@repo/cache | ioredis |
@repo/search | typesense |
@repo/logger | pino, pino-pretty |
| Root | prettier, tsx, vitest, @vitest/coverage-v8, @modelcontextprotocol/sdk |
Dependencies like class-variance-authority, clsx, sonner, framer-motion, and radix-ui are non-cataloged because they are used by a single package (@repo/web) and do not need cross-package version alignment.