Skip to main content

pnpm Package Manager Guide

pnpm is a fast, disk space efficient package manager for Node.js projects, used as an alternative to npm or yarn at Quatico.

Why pnpm?

  • Very fast installation - optimized dependency resolution
  • Disk space efficient - uses symlinks to share dependencies
  • Strict dependency management - prevents "phantom dependencies" and workspace leakage
  • Active development - modern, actively maintained tool
  • Better for monorepos - excellent workspace support

Installation

Corepack is included with Node.js and is the recommended way to install pnpm:

npm i -g corepack
corepack enable

Standalone Script

Alternatively, use the standalone installation script:

curl -fsSL https://get.pnpm.io/install.sh | sh -

Essential Commands

Managing Dependencies

CommandDescription
pnpm installInstall all dependencies
pnpm add <package>Add a dependency
pnpm add -D <package>Add a dev dependency
pnpm remove <package>Remove a dependency
pnpm upgrade [package]Upgrade dependencies (respects package.json ranges)
pnpm upgrade --latestUpgrade to latest versions (updates package.json)
pnpm why <package>Show why a package is installed

Running Scripts

pnpm distinguishes between run and exec:

  • pnpm run <script> - Runs scripts defined in package.json
  • pnpm exec <command> - Executes binaries from installed packages
  • pnpm <command> - Tries script first, then binary (convenience)

Recommendation: In scripts and CI/CD, use explicit run or exec.

Monorepo Commands

For monorepos (workspaces):

  • pnpm -r <command> - Run command recursively in all packages
  • pnpm -F <package> - Filter to specific package(s)
  • pnpm -F ./frontend run build - Run build in frontend package
  • pnpm --filter "...[origin/develop]" test - Run tests only in changed packages

Project Configuration

package.json

Add to your package.json to enforce pnpm usage:

{
"packageManager": "pnpm@9.1.2",
"scripts": {
"preinstall": "npx only-allow pnpm"
}
}
warning

Don't add preinstall script to published packages - only use in private projects!

.npmrc Configuration

Common pnpm-specific settings:

# Allow peer dependency version mismatches (e.g., for eslint)
pnpm.peerDependencyRules.allowAny[]=eslint

# Hoist specific packages (if needed for compatibility)
hoist-pattern[]=@types/*
hoist-pattern[]=*babel*

Monorepo Workspaces

Setup

Create a pnpm-workspace.yaml file:

packages:
- 'packages/*'
- 'apps/*'

Workspace Dependencies

Use workspace:* protocol for internal dependencies:

{
"dependencies": {
"@my/package": "workspace:*"
}
}

This automatically links workspace packages during development and resolves to proper versions when published.

Migration from yarn/npm

Step-by-Step Migration

  1. Create workspace config (if monorepo):

    # pnpm-workspace.yaml
    packages:
    - 'packages/*'
  2. Import lockfile:

    pnpm import yarn.lock && rm yarn.lock
    # Or for npm
    pnpm import package-lock.json && rm package-lock.json
  3. Update package.json scripts:

    • yarn cleanpnpm clean
    • yarn --cwd ../path cleanpnpm --dir ../path run clean
    • yarn --cwd ../path cleanpnpm -F '@my/package' clean (in monorepo)
  4. Enable pnpm:

    corepack use pnpm@latest
  5. Install and normalize:

    pnpm install
  6. Test your build:

    pnpm run build

Troubleshooting

If you encounter issues with the symlinked node_modules structure:

  1. Try hoisted mode (temporary, for compatibility):

    # .npmrc
    node-linker=hoisted

    Then reinstall: rm -rf node_modules && pnpm install

  2. Fix specific packages - hoist only what's needed:

    hoist-pattern[]=@types/*
  3. Add missing dependencies - some packages may need explicit dependencies that were previously available through hoisting

Docker Integration

Use corepack in Dockerfiles:

RUN npm i -g corepack@latest && pnpm -v
tip

The pnpm -v call ensures pnpm is downloaded during build, preventing delays at runtime.

Optimizing Docker Layers

Use pnpm fetch to cache dependencies:

COPY pnpm-lock.yaml .npmrc package.json ./
RUN pnpm fetch
COPY . .
RUN pnpm install --offline

This keeps dependency cache separate from source code changes.

Best Practices

  1. Use corepack for consistent pnpm versions across team and CI
  2. Commit pnpm-lock.yaml to version control
  3. Use workspace:* for internal monorepo dependencies
  4. Set packageManager field in package.json
  5. Use explicit run/exec in scripts and CI/CD
  6. Avoid node-linker=hoisted unless necessary for compatibility

Resources