|$ curl https://forge-ai.dev/api/markdown?path=docs/npm/yarn
$cat docs/yarn-classic-vs-yarn-berry.md
updated Today·24-32 min read·published

Yarn Classic vs Yarn Berry

YarnPnPWorkspacesIntermediate🎯Free Tools
Introduction

Yarn exists in two eras. Yarn Classic (v1) is a parallel installer that writes a familiar hoisted node_modules tree and a text yarn.lock. Yarn Berry(v2+) is a rewrite: project-local binaries, Plug'n'Play (PnP), optional Zero-Installs, a plugin system, and stronger workspace tooling.

New projects should target Berry (stable). Classic remains only for legacy repos that cannot migrate yet. Compare managers on npm vs Yarn vs pnpm vs Bun.

📝

note

Prefer installing Yarn via Corepack and the packageManager field — not a global npm i -g yarn (that often still installs Classic).
Classic vs Berry
AspectYarn Classic (v1)Yarn Berry (v2+)
Install layoutHoisted node_modulesPnP by default; node-modules optional
BinaryGlobal yarn.yarn/releases/*.cjs per project
Config.yarnrc.yarnrc.yml
PluginsLimitedFirst-class plugin API
Zero-InstallsNoYes
MaintenanceLegacyActive
enable-berry.sh
Bash
1corepack enable
2# from project root
3yarn set version stable
4# writes .yarn/releases/yarn-*.cjs and updates packageManager
5yarn --version
How / Why / Requirements
QuestionAnswer
Why choose Yarn?Deterministic installs, workspaces, PnP/Zero-Installs, constraints for monorepo policy
How to adopt?Corepack → yarn set version stable → yarn install → commit yarn.lock + .yarn/releases
RequirementsNode with Corepack (or committed Yarn binary); CI must use same Yarn version; PnP needs editor SDKs
When not to?Team prefers pnpm's store model; many packages break under PnP and you refuse nodeLinker fallback
yarn.lock

yarn.lock pins exact resolved versions and integrity hashes for every transitive dependency. Always commit it. Never hand-edit unless you know the format — regenerate with yarn install.

lockfile-ops.sh
Bash
1yarn install # update tree + lockfile as needed
2yarn install --immutable # CI: fail if lockfile would change (Berry)
3yarn up lodash # interactive/range-aware upgrade (Berry)
4yarn dedupe # reduce duplicate versions when possible

warning

Classic and Berry share the filename yarn.lock but the lockfile format differs. Migrating Classic → Berry rewrites the lockfile; review the diff in PRs.
Plug'n'Play (PnP)

PnP replaces nested node_modules with a generated map (.pnp.cjs / .pnp.loader.mjs). Node resolves packages through Yarn's runtime instead of walking directories. Packages you did not declare cannot be required — phantom dependencies fail loudly.

.yarnrc.yml
YAML
1# Default Berry behavior is PnP (nodeLinker: pnp)
2# Explicit:
3nodeLinker: pnp
4
5# Compatibility escape hatch:
6# nodeLinker: node-modules
7# nodeLinker: pnpm # hybrid layout option in newer Yarn
editor-sdks.sh
Bash
1# Generate IDE / TypeScript SDKs for PnP
2yarn dlx @yarnpkg/sdks vscode
3# commit .yarn/sdks as recommended by Yarn docs for your editor
BenefitTrade-off
Faster installs, less disk churnSome packages assume node_modules paths
Strict dependency graphNeed packageExtensions for broken packages
Enables Zero-Installs cleanlyEditor/tooling setup required

info

If a dependency breaks under PnP, prefer packageExtensions in .yarnrc.yml over flipping the whole repo to node-modules — fix the graph, do not abandon strictness unless necessary.
Zero-Installs

Zero-Installs commit the Yarn cache (zip archives under .yarn/cache) plus PnP maps so clones can run without a network install. CI skips yarn install or reduces it to a no-op validation.

.yarnrc.yml
YAML
1enableGlobalCache: false
2# keep cache inside the project so it can be committed
3compressionLevel: mixed
4# ensure .yarn/cache is not gitignored
zero-install-workflow.sh
Bash
1yarn install
2git add .yarn/cache .yarn/releases .pnp.cjs .pnp.loader.mjs yarn.lock .yarnrc.yml
3git commit -m "chore: enable yarn zero-installs"
4
5# Fresh machine / CI
6git clone ...
7cd project
8# often: yarn immediately works; or yarn install --immutable as a check
9yarn test

warning

Repo size grows with dependency zips (often 100MB+). Use Git LFS for .yarn/cache if clone size becomes painful. Re-evaluate if most contributors use shallow CI caches instead.
Constraints

Yarn Constraints are Prolog-inspired (or JS) rules that enforce workspace policies: same React version everywhere, forbidden packages, required peer ranges.

constraints.sh
Bash
1yarn plugin import constraints # if not already present
2yarn constraints # report violations
3yarn constraints --fix # auto-fix when possible
yarn.config.cjs (conceptual)
JavaScript
1// Berry constraints configuration varies by Yarn major —
2// define rules that all workspaces use the same typescript major
3// and disallow left-pad-style banned packages.
4module.exports = {
5 // See Yarn docs for your version's constraints API shape
6};

best practice

Run yarn constraints in CI after install. Treat violations like lint failures — they prevent version drift across a monorepo.
Workspaces

Workspaces live under one root lockfile. Internal packages link by name without publishing.

package.json
JSON
1{
2 "name": "acme",
3 "private": true,
4 "workspaces": ["apps/*", "packages/*"],
5 "packageManager": "yarn@4.5.0"
6}
workspace-cmds.sh
Bash
1yarn workspaces list
2yarn workspace @acme/web add zod
3yarn workspaces foreach -A --topological run build
4yarn workspaces focus @acme/web # install subset for deploy images
Command patternUse when
yarn workspace name …Operate on one package
yarn workspaces foreach …Run script across many packages
workspaces focusProduction image with only needed deps
yarn dlx

yarn dlx is the Berry equivalent of npx: download a package to a temporary environment and execute its binary without adding it to your project.

dlx.sh
Bash
1yarn dlx create-next-app@latest my-app
2yarn dlx eslint . --fix
3yarn dlx serve dist
🔥

pro tip

Prefer project-local binaries via yarn run for CI. Use dlx for scaffolding and one-off tools so versions stay intentional.
Plugins

Berry ships a slim core; optional features arrive as plugins stored under .yarn/plugins and referenced from .yarnrc.yml.

plugins.sh
Bash
1yarn plugin import interactive-tools
2yarn plugin import workspace-tools
3yarn plugin import version
4yarn plugin list
.yarnrc.yml (plugins excerpt)
YAML
1plugins:
2 - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
3 spec: "@yarnpkg/plugin-interactive-tools"
📝

note

Commit plugins with the repo so every developer and CI gets the same Yarn surface area without global installs.
Migrating from npm
migrate-npm-to-yarn.sh
Bash
1corepack enable
2cd my-app
3yarn set version stable
4yarn install
5# review yarn.lock; delete package-lock.json
6rm package-lock.json
7
8# If tools break under PnP:
9yarn config set nodeLinker node-modules
10yarn install
11
12# Block accidental npm usage
13npm pkg set scripts.preinstall="npx only-allow yarn"
npm habitYarn Berry equivalent
npm ciyarn install --immutable
npm i pkgyarn add pkg
npm i -D pkgyarn add -D pkg
npxyarn dlx or yarn exec
overridesresolutions in package.json
resolutions
JSON
1{
2 "resolutions": {
3 "lodash": "4.17.21",
4 "**/left-pad": "npm:@stdlib/string-left-pad@0.0.0"
5 }
6}
CI Pattern
.github/workflows/ci.yml
YAML
1- uses: actions/setup-node@v4
2 with:
3 node-version: 22
4 cache: yarn
5- run: corepack enable
6- run: yarn install --immutable
7- run: yarn constraints
8- run: yarn workspaces foreach -A --topological run test
Adoption Checklist
  • Set packageManager and enable Corepack
  • Commit .yarn/releases and yarn.lock
  • Decide PnP vs nodeLinker: node-modules early
  • Add editor SDKs if using PnP
  • Replace npm ci with yarn install --immutable
  • Document Zero-Installs policy (commit cache or not)
  • Add constraints for shared dependency versions
  • Remove competing lockfiles

danger

Do not run Classic and Berry interchangeably on the same branch. Pin one major and teach the team the Berry CLI.

Community

Get help on Slack, Discord or VIP

Stuck on a guide? Join the community and ask.