Skip to main content
Technology & EngineeringPackage Management285 lines

Yarn Berry

Yarn Berry (v2+) with Plug'n'Play, zero-installs, constraints, and workspace management

Quick Summary31 lines
You are an expert in Yarn Berry (Yarn v2 and above), including its Plug'n'Play resolution, zero-install strategy, constraints system, and workspace management.

## Key Points

- Massive `node_modules` directories
- Hoisting ambiguity and phantom dependencies
- Slow install times from file I/O
- **pnp** (default): Full Plug'n'Play with zip archives
- **pnpm**: pnpm-style symlinked `node_modules`
- **node-modules**: Traditional flat `node_modules` (compatibility fallback)
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
- uses: actions/checkout@v4
- run: yarn install --immutable
- uses: actions/setup-node@v4
- run: yarn install --immutable
- Use PnP as the default `nodeLinker`. Only fall back to `node-modules` if a critical tool is truly incompatible.

## Quick Example

```bash
yarn constraints
yarn constraints --fix
```

```yaml
# .yarnrc.yml
enableNetwork: false        # Fail if anything needs fetching
checksumBehavior: throw     # Fail on checksum mismatch
```
skilldb get package-management-skills/Yarn BerryFull skill: 285 lines
Paste into your CLAUDE.md or agent config

Yarn Berry — Package Management

You are an expert in Yarn Berry (Yarn v2 and above), including its Plug'n'Play resolution, zero-install strategy, constraints system, and workspace management.

Core Philosophy

Overview

Yarn Berry is the modern rewrite of Yarn (v2+). It replaces node_modules with Plug'n'Play (PnP), a system that maps packages to zip archives and resolves them through a generated .pnp.cjs file. Combined with zero-installs (committing the dependency cache), teams can clone and run projects without an install step.

Core Concepts

Plug'n'Play (PnP)

Instead of extracting packages into a node_modules tree, PnP stores dependencies as compressed .zip files in .yarn/cache/ and generates a .pnp.cjs manifest that tells Node.js exactly where every package lives. This eliminates:

  • Massive node_modules directories
  • Hoisting ambiguity and phantom dependencies
  • Slow install times from file I/O

Zero-Installs

By committing .yarn/cache/ and .pnp.cjs to source control, team members can git clone and immediately run the project — no yarn install step required.

nodeLinker Modes

Yarn Berry supports multiple linking strategies:

  • pnp (default): Full Plug'n'Play with zip archives
  • pnpm: pnpm-style symlinked node_modules
  • node-modules: Traditional flat node_modules (compatibility fallback)

Release Line

Yarn Berry uses a per-project binary checked into .yarn/releases/. The global yarn command (via corepack) delegates to this local version.

Implementation Patterns

Project Setup

# Initialize with corepack
corepack enable
corepack use yarn@4

# Creates .yarnrc.yml and .yarn/ directory
yarn init -2

.yarnrc.yml Configuration

nodeLinker: pnp

# Enable global cache sharing
enableGlobalCache: false

# TypeScript plugin for automatic @types/* resolution
plugins:
  - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
    spec: "@yarnpkg/plugin-typescript"

# Patch compatibility
packageExtensions:
  "react-dom@*":
    peerDependencies:
      react: "*"

.gitignore for Zero-Installs

# Zero-install: commit cache and PnP manifest
.yarn/*
!.yarn/cache
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

For non-zero-install (.yarn/cache not committed):

.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
.pnp.*

Workspace Configuration

// package.json (root)
{
  "name": "my-monorepo",
  "private": true,
  "workspaces": [
    "packages/*",
    "apps/*"
  ]
}

Workspace Commands

# Run a script in a specific workspace
yarn workspace @myorg/web build

# Run across all workspaces
yarn workspaces foreach -A run build

# Topological order (dependencies first)
yarn workspaces foreach -At run build

# Parallel execution
yarn workspaces foreach -Ap run lint

# Only changed workspaces (since main)
yarn workspaces foreach -A --since=main run test

# Add dependency to a workspace
yarn workspace @myorg/web add react

# Add workspace sibling
yarn workspace @myorg/web add @myorg/shared

PnP and Editor SDKs

Many editors need SDK support to resolve PnP packages. Install the SDKs:

yarn dlx @yarnpkg/sdks vscode

# This creates .yarn/sdks/ with settings for:
# - TypeScript
# - ESLint
# - Prettier

For VS Code, add to .vscode/settings.json:

{
  "typescript.tsdk": ".yarn/sdks/typescript/lib",
  "typescript.enablePromptUseWorkspaceTsdk": true,
  "search.exclude": {
    "**/.yarn": true,
    "**/.pnp.*": true
  }
}

Patching Dependencies

# Create a patch for a broken dependency
yarn patch lodash

# Edit files in the temporary directory it opens, then:
yarn patch-commit -s path/to/patched/lodash

# The patch is stored in .yarn/patches/ and applied on install

Constraints (Programmatic Policy Enforcement)

Create constraints.pro (Prolog) or yarn.config.cjs (JS) to enforce workspace rules:

// yarn.config.cjs
module.exports = {
  async constraints({ Yarn }) {
    // All workspaces must have a license
    for (const workspace of Yarn.workspaces()) {
      workspace.set('license', 'MIT');
    }

    // Enforce consistent dependency versions
    for (const dep of Yarn.dependencies()) {
      if (dep.ident === 'typescript') {
        dep.update('^5.5.0');
      }
    }

    // All workspaces must have a build script
    for (const workspace of Yarn.workspaces()) {
      if (workspace.ident !== 'my-monorepo') {
        workspace.set('scripts.build', expect.stringMatching(/.+/));
      }
    }
  }
};
yarn constraints
yarn constraints --fix

Protocols

{
  "dependencies": {
    "@myorg/shared": "workspace:^",
    "lodash": "npm:4.17.21",
    "my-fork": "git@github.com:me/lodash.git#commit=abc123",
    "local-tool": "portal:../local-tool",
    "patched-lib": "patch:left-pad@1.3.0#.yarn/patches/left-pad-npm-1.3.0-abc123.patch"
  }
}

CI Configuration

# GitHub Actions
- uses: actions/checkout@v4

# For zero-installs, no install step needed. Just verify:
- run: yarn install --immutable

# For non-zero-install:
- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: 'yarn'
- run: yarn install --immutable

Offline Mirror / Controlled Installs

# .yarnrc.yml
enableNetwork: false        # Fail if anything needs fetching
checksumBehavior: throw     # Fail on checksum mismatch

Best Practices

  • Use PnP as the default nodeLinker. Only fall back to node-modules if a critical tool is truly incompatible.
  • Install editor SDKs (yarn dlx @yarnpkg/sdks vscode) immediately after setting up a project.
  • Use yarn workspaces foreach -At for builds to respect dependency topological order.
  • Use constraints to enforce consistency across workspace packages (license, version alignment, required scripts).
  • Use yarn patch instead of patch-package for fixing broken dependencies.
  • Run yarn install --immutable in CI to verify lockfile integrity.
  • Use packageExtensions in .yarnrc.yml to fix missing peer dependency declarations in third-party packages.
  • Keep .yarn/releases/ committed so all team members use the exact same Yarn version.
  • Use yarn dedupe to reduce duplicate dependency versions in the lockfile.
  • For zero-installs, commit .yarn/cache/ using Git LFS or accept the repository size trade-off.

Common Pitfalls

  • PnP-incompatible packages: Some packages use dynamic require() or hardcode node_modules paths. Use packageExtensions or fall back to nodeLinker: node-modules as a last resort.
  • Missing editor SDK: TypeScript and ESLint appear broken in the editor without running yarn dlx @yarnpkg/sdks. This is the most common source of frustration.
  • Cannot find module errors: Caused by phantom dependency access. The package must be added as an explicit dependency.
  • Large repository from zero-installs: Committing .yarn/cache/ bloats the Git history. Evaluate whether the trade-off is worth it for your team.
  • Forgetting --immutable in CI: Without it, CI may silently modify the lockfile, masking discrepancies.
  • Using npm publish instead of yarn npm publish: npm CLI does not understand PnP. Use yarn npm publish or yarn pack for publishing.
  • Postinstall scripts in PnP: Packages with native build steps need to be listed in dependenciesMeta with built: true in .yarnrc.yml if they fail silently.
  • Conflating Yarn Classic (v1) and Berry (v2+): Configuration, commands, and behavior differ significantly. Yarn Berry uses .yarnrc.yml (not .yarnrc), and yarn.lock format changed.

Anti-Patterns

Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.

Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.

Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.

Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.

Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.

Install this skill directly: skilldb add package-management-skills

Get CLI access →