Trovella Wiki

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

PackageVersion
@casl/ability^6.8.0
better-auth^1.6.0

AI

PackageVersion
@google/genai^1.48.0

MCP

PackageVersion
@modelcontextprotocol/sdk^1.28.0

React Ecosystem

PackageVersion
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

PackageVersion
@trpc/client^11.15.0
@trpc/react-query^11.15.0
@trpc/server^11.15.0

Database

PackageVersion
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

PackageVersion
ioredis^5.10.1
PackageVersion
typesense^3.0.4

Background Jobs

PackageVersion
inngest^4.1.0

Logging

PackageVersion
pino^10.3.1
pino-pretty^13.1.3

Error Tracking

PackageVersion
@sentry/nextjs^10.46.0

UI and Styling

PackageVersion
tailwindcss^4.2.2
@tailwindcss/postcss^4.2.2

Validation

PackageVersion
zod^4.3.6

Documentation

PackageVersion
fumadocs-core^16.7.10
fumadocs-ui^16.7.10
fumadocs-mdx^14.2.11

Quality and Tooling

PackageVersion
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:

  1. pnpm reads pnpm-workspace.yaml and builds the catalog map.
  2. For each package.json in the workspace, any dependency with "catalog:" as its version is resolved to the corresponding entry in the catalog.
  3. The resolved versions are written into pnpm-lock.yaml.
  4. 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:

PackageCatalog Dependencies
@repo/webreact, next, zod, tailwindcss, tRPC triplet, better-auth, inngest, @sentry/nextjs, fumadocs triplet, lucide-react
@repo/dbdrizzle-orm, drizzle-kit, drizzle-seed, pg, @types/pg
@repo/api@trpc/server, drizzle-orm, zod, @casl/ability, pino
@repo/authbetter-auth, drizzle-orm
@repo/ai@google/genai, drizzle-orm
@repo/cacheioredis
@repo/searchtypesense
@repo/loggerpino, pino-pretty
Rootprettier, 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.

On this page