| |

Yarn 15 ๐Ÿงถ Migrating from npm to Yarn

โฑ๏ธ Estimated read: 8 min | |

Migrating from npm to Yarn is not a single command. It is a sequence of decisions: which Yarn version to target, whether to use Plug’n’Play or node_modules, how to convert the lockfile, and how to reconfigure the settings that npm stored in .npmrc. The most common mistake is running yarn install on an npm project and assuming the migration is complete. The package-lock.json remains, the .npmrc settings are ignored by Yarn Berry, and the dependency tree may resolve differently than it did under npm.

Key point: Modern Yarn (2+) does not read .npmrc. Every registry, token, and configuration setting must be migrated to .yarnrc.yml. The lockfile conversion is handled by yarn import (for Yarn Classic) or by running yarn install after removing the npm lockfile (for Yarn Berry).


Why migration requires a deliberate sequence

The lockfile incompatibility problem. npm and Yarn use different lockfile formats. package-lock.json records the physical node_modules layout, including hoisting decisions. yarn.lock records only the logical dependency tree. Yarn Classic provides yarn import to generate a yarn.lock from an existing package-lock.json, minimizing the differences between the two resolutions . Yarn Berry, however, regenerates the lockfile during yarn install and does not use yarn import in the same way. The recommended path for Berry is to remove the npm lockfile and let Yarn resolve from package.json.

The .npmrc problem. Yarn Classic read .npmrc for registry configuration. Yarn Berry ignores it entirely. Settings like registry, //registry.npmjs.org/:_authToken, and always-auth must be translated into .yarnrc.yml syntax: npmRegistryServer, npmAuthToken, and npmAlwaysAuth . A project that migrates without this step will fail to install private packages.

The pre/post script problem. Yarn Classic and npm automatically ran prestart before start and poststart after. Yarn Berry removed this implicit behavior. A script named prestart is no longer called automatically; it must be invoked explicitly: "start": "yarn prestart && http-server" . This is a breaking change that affects any project relying on lifecycle script magic.

The yarn global problem. Yarn Berry removed the yarn global command entirely. System-wide package installation is outside Yarn’s scope. The replacement for one-off global tools is yarn dlx, which installs the package temporarily and runs it without polluting the global environment .

The bundleDependencies problem. This npm feature embeds dependencies inside the package archive. It assumes a node_modules layout, which does not exist under Plug’n’Play. Yarn Berry recommends removing bundleDependencies and using a bundler (Esbuild, Webpack, Rollup) for standalone distributions .


a. Migrating to Yarn Classic (1.x)

Yarn Classic is the simplest migration path. It is compatible with the npm package.json format, reads .npmrc, and can import the npm lockfile directly.

# Install Yarn Classic globally (if not already installed)
npm install -g yarn

# Import the existing package-lock.json
yarn import

# Run the install
yarn install

The yarn import command reads package-lock.json and generates a yarn.lock with the same resolution. If no package-lock.json exists, it falls back to reading node_modules using standard require.resolve() rules . Warnings are printed when Yarn cannot express the npm tree identicallyโ€”for example, when npm resolved two different versions of the same semver range, which yarn.lock cannot represent .

After yarn import succeeds, delete package-lock.json to prevent confusion. Yarn Classic still reads .npmrc for registry settings, so no .yarnrc migration is required at this stage.


b. Migrating to Yarn Berry (2+)

Yarn Berry is the modern version (2, 3, 4). It uses .yarnrc.yml, supports Plug’n’Play, and requires Corepack for version management.

# Enable Corepack (once per machine)
corepack enable

# Set the Yarn version to Berry
yarn set version berry

# Remove the npm lockfile
rm package-lock.json

# Run the install
yarn install

The yarn set version berry command downloads the latest Berry release and configures the project to use it . The package-lock.json is removed because Berry generates its own yarn.lock during install. The resulting dependency tree may differ from npm’s, particularly in hoisting decisions.

By default, Yarn Berry uses Plug’n’Play. If the project’s tools are incompatible with PnP, fall back to node_modules:

# .yarnrc.yml
nodeLinker: node-modules

This preserves the traditional layout while still using Berry’s resolution and caching mechanisms .


c. Migrating .npmrc to .yarnrc.yml

Every .npmrc setting that affects registry access must be translated. The syntax changes from INI to YAML, and the keys are renamed.

# .yarnrc.yml
npmRegistryServer: "https://registry.npmjs.org"
npmAuthToken: "${NPM_TOKEN}"
npmAlwaysAuth: true

For scoped registries, use npmScopes:

npmScopes:
  my-company:
    npmRegistryServer: "https://npm.internal.example.com"
    npmAuthToken: "${INTERNAL_NPM_TOKEN}"

The .npmrc file can be deleted after migration. Yarn Berry does not read it, and leaving it in place creates confusion about which configuration is authoritative .


Complete Example Session

# ============================================
# PART 1: VERIFY NODE VERSION
# ============================================

node --version
# v20.11.0 or later required for Yarn Berry
# ============================================
# PART 2: ENABLE COREPACK
# ============================================

corepack enable
# Corepack is now the router for yarn commands
# ============================================
# PART 3: SET YARN VERSION TO BERRY
# ============================================

yarn set version berry
# Downloads Yarn 4.x and configures .yarnrc.yml
# ============================================
# PART 4: REMOVE NPM LOCKFILE
# ============================================

rm package-lock.json
# Berry generates its own lockfile
# ============================================
# PART 5: MIGRATE .NPMRC TO .YARNRC.YML
# ============================================

# Delete .npmrc after translating its settings
cat > .yarnrc.yml <<'EOF'
npmRegistryServer: "https://registry.npmjs.org"
npmAuthToken: "${NPM_TOKEN}"
EOF
# ============================================
# PART 6: CHOOSE LINKER
# ============================================

# For PnP (default in Berry):
# (nothing to add)

# For node_modules compatibility:
echo "nodeLinker: node-modules" >> .yarnrc.yml
# ============================================
# PART 7: RUN INSTALL
# ============================================

yarn install
# Generates yarn.lock and .pnp.cjs (or node_modules)
# ============================================
# PART 8: UPDATE SCRIPTS FOR BERRY
# ============================================

# Rewrite pre/post scripts to be explicit
# "start": "yarn prestart && http-server"
# ============================================
# PART 9: COMMIT CHANGES
# ============================================

git add .yarnrc.yml yarn.lock .pnp.cjs .pnp.loader.mjs
git commit -m "Migrate from npm to Yarn Berry"

The nine parts trace the Berry migration: verifying Node, enabling Corepack, setting the version, removing the npm lockfile, translating .npmrc, choosing the linker, installing, updating scripts, and committing.


Quick Reference

Command Translation

npmYarn ClassicYarn Berry
npm installyarnyarn install
npm ciyarn install --frozen-lockfileyarn install --immutable
npm install <pkg>yarn add <pkg>yarn add <pkg>
npm install -D <pkg>yarn add -D <pkg>yarn add -D <pkg>
npm run <script>yarn run <script>yarn <script>
npm uninstall <pkg>yarn remove <pkg>yarn remove <pkg>
npm install -g <pkg>yarn global add <pkg>yarn dlx <pkg>

File Migration

npm FileYarn Berry FileAction
package-lock.jsonyarn.lockRemove npm lockfile; Yarn generates new
.npmrc.yarnrc.ymlTranslate registry settings
node_modules/.yarn/cache/ or node_modules/Depends on nodeLinker

Configuration Translation

.npmrc.yarnrc.yml
registry=https://...npmRegistryServer: "https://..."
//registry/:_authToken=xxxnpmAuthToken: "xxx"
always-auth=truenpmAlwaysAuth: true

Best Practices

โœ… Do This:

yarn set version berry
<!-- Use Berry for new migrations -->                # โœ…
nodeLinker: node-modules
<!-- Fall back if PnP breaks tools -->               # โœ…
# Rewrite pre/post scripts explicitly
  • โœ… Berry does not run them automatically

โŒ Don’t Do This:

# Keep .npmrc after migrating to Berry
  • โŒ Berry ignores it; configuration is invisible
# Assume yarn import works in Berry
  • โŒ yarn import is a Classic feature; Berry regenerates
# Use yarn global in Berry
  • โŒ Removed; use yarn dlx

Common Pitfalls

PitfallWhy It HappensFix
Private packages fail.npmrc not migratedTranslate to .yarnrc.yml
prestart not runningBerry removed implicit lifecycleCall explicitly in start script
PnP breaks a toolTool assumes node_modulesSet nodeLinker: node-modules
Lockfile conflictsBoth package-lock.json and yarn.lock presentDelete npm lockfile
yarn global not foundRemoved in BerryUse yarn dlx

Real-World Examples

1. Classic Import

yarn import

2. Set Berry

yarn set version berry

3. Remove npm Lockfile

rm package-lock.json

4. Fallback to node_modules

nodeLinker: node-modules

5. Translate Registry

npmRegistryServer: "https://registry.npmjs.org"

6. Translate Token

npmAuthToken: "${NPM_TOKEN}"

7. Scope Configuration

npmScopes:
  my-company:
    npmRegistryServer: "https://npm.internal"

8. Explicit Script Call

"start": "yarn prestart && http-server"

9. One-Off Tool

yarn dlx create-react-app

10. Immutable Install

yarn install --immutable

Visual

Migration Decision Path

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Which Yarn version?                         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
           โ”‚ CLASSIC (1.x)         โ”‚ BERRY (2+)
           โ–ผ                       โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  yarn import         โ”‚  โ”‚  yarn set version    โ”‚
โ”‚  (uses npm lockfile) โ”‚  โ”‚  berry               โ”‚
โ”‚                      โ”‚  โ”‚  rm package-lock.jsonโ”‚
โ”‚  Reads .npmrc        โ”‚  โ”‚  Translate .npmrc โ†’  โ”‚
โ”‚                      โ”‚  โ”‚  .yarnrc.yml         โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Classic is a drop-in replacement. Berry requires configuration migration.

Configuration Migration

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  .npmrc (ignored by Berry)                   โ”‚
โ”‚  registry=https://registry.npmjs.org         โ”‚
โ”‚  //registry/:_authToken=xxx                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  .yarnrc.yml                                 โ”‚
โ”‚  npmRegistryServer: "https://..."            โ”‚
โ”‚  npmAuthToken: "${NPM_TOKEN}"                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The syntax changes; the intent is preserved.


Summary

ItemValue
Classic importyarn import (uses package-lock.json)
Berry migrationyarn set version berry + remove npm lockfile
.npmrcIgnored by Berry; translate to .yarnrc.yml
Registry keynpmRegistryServer
Token keynpmAuthToken
PnP fallbacknodeLinker: node-modules
pre/post scriptsMust be called explicitly
yarn globalRemoved; use yarn dlx

Key takeaways:

  • Yarn Classic migrates smoothly; Berry requires configuration work. yarn import handles the lockfile for Classic. Berry ignores .npmrc and regenerates the lockfile.
  • .npmrc is dead in Berry. Every registry and authentication setting must be translated to .yarnrc.yml with the new key names .
  • PnP is the default in Berry, but not mandatory. Setting nodeLinker: node-modules preserves compatibility with tools that assume a filesystem layout.
  • Lifecycle scripts are explicit. prestart and poststart are no longer implicit. The start script must call them directly.
  • yarn global is gone. One-off tools are executed with yarn dlx, which installs temporarily and discards the package.

Remember: Migration is not a command. It is a sequence of decisions. Choose the Yarn version. Choose the linker. Translate the configuration. Remove the conflicting lockfile. Then install. The dependency tree may change, and that is expectedโ€”Yarn’s resolution algorithm is not identical to npm’s. Test the application after migration, and commit the new lockfile alongside the configuration.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!