- Yarn uses a two-level model: one global CLI plus a project-pinned version for consistent behavior.
- Deterministic installs with yarn.lock and aggressive caching deliver fast, reproducible dependency management.
- Modern Yarn Berry adds PnP, workspaces and .yarnrc.yml for flexible linking, caching and editor/CI integration.
- Correct PATH setup, lockfile handling and cache management are essential to avoid common installation issues.

If you are trying to install Yarn for your JavaScript projects and you feel a bit lost between so many options, you are not alone. Yarn has evolved a lot in recent years, it coexists with npm, and there are different versions and installation flows depending on the operating system and even the project style you use (monorepo, PnP, classic node_modules, and so on).
The good news is that once you understand how Yarn is installed and how its two-level model (global CLI + project-specific Yarn) works, everything starts to make sense. In this guide we’ll walk through, in detail, how to install Yarn on the most common systems, how to wire it into a real JavaScript project, what makes it different from npm, and how to troubleshoot typical problems that usually show up the first time you adopt it.
What Yarn Is And Why It Still Matters For JavaScript Projects
Yarn is a package manager for Node.js designed with three big goals in mind: speed, security, and deterministic installs. It was born as an alternative to npm at a time when npm had performance and reliability issues, and even though npm has improved a lot since then, Yarn remains extremely popular, especially around React and modern frontend stacks.
One of Yarn’s key strengths is its deterministic install process, based on the yarn.lock file. This file fixes the exact versions of all direct and transitive dependencies, so that installing on different machines or CI servers always yields the same dependency tree, eliminating the classic “it only works on my laptop” problem.
Another distinctive feature is its aggressive caching behavior, which lets Yarn reuse any package it has already downloaded. Thanks to this, repeated installs are dramatically faster, and Yarn can even work in offline mode if all required packages have been cached previously.
Modern Yarn (often called “Berry” for versions 2.x, 3.x and 4.x) brings additional advanced capabilities such as Plug’n’Play (PnP) and workspaces. PnP can completely remove the traditional node_modules folder by replacing it with manifest files that tell Node.js exactly where each package lives, saving a lot of disk space and speeding some operations. Workspaces, on the other hand, are perfect for monorepos, linking multiple packages in a single repository with a shared lockfile and central dependency management.
From a security perspective, Yarn verifies checksums for every package before it is executed. This integrity validation adds an extra layer of protection against corrupted or tampered artifacts, which is especially useful in corporate or sensitive environments.
Understanding Yarn Classic vs Yarn Berry (Modern Yarn)

Before talking about installation, it is crucial to understand that there are two big families of Yarn: Classic (1.x) and Berry (2.x/3.x/4.x). Yarn Classic lives in its own historical repository and only receives security fixes, while all active development focuses on the newer Berry line hosted in the yarnpkg/berry repo.
Yarn Classic (around 1.22) is what most people still get when installing the global yarn CLI with npm. That global CLI nowadays mostly acts as a launcher: inside a project that has been configured with Berry, the global command simply delegates execution to the local project-specific Yarn version, so you can upgrade the project toolchain without touching global tooling.
Yarn Berry introduces deep architectural changes, most notably Plug’n’Play and a powerful plugin system. By default, Berry prefers PnP instead of node_modules, supports zero-install workflows (dependencies committed to the repo), and allows fine-grained configuration through .yarnrc.yml, including how modules are linked, how caches are managed, or how registries and proxies are defined.
The Yarn maintainers themselves strongly encourage migrating from 1.x to the latest Berry release whenever possible. Modern releases have been actively maintained for longer, fix whole classes of issues present in Classic, and offer configuration flexibility through the nodeLinker setting so you can still use the classic node_modules layout or pnpm-style symlinks when PnP is not a good fit.
If your ecosystem or tooling is not yet ready for PnP, you are not stuck. By simply setting nodeLinker: node-modules in .yarnrc.yml, Berry behaves closer to traditional npm installs while preserving its deterministic lockfile, caching behavior, and improved CLI experience.
System Requirements And Global Yarn Installation Strategy
Regardless of your operating system, the real hard requirement for Yarn is having Node.js installed. Yarn is a Node-based CLI and depends on Node to run, so you should first confirm that Node is available with a quick node -v in your terminal. If you see a version number instead of a “command not found” error, you are good to go.
If Node.js is missing or outdated, install or upgrade it using the method recommended for your platform. On Linux you might use distribution packages or NodeSource repositories, on macOS you might prefer Homebrew, MacPorts or nvm, and on Windows the official installer or a package manager like Chocolatey or Scoop. Many Yarn workflows assume at least Node 14.18, and for Berry, Node 16 or 18 LTS is usually the sweet spot.
The authors of Yarn recommend a two-tier installation model. First, install a global yarn CLI once (most commonly via npm), and then, inside each project, use that global command to configure a project-specific Yarn version that lives right in the repository. This guarantees that all contributors and CI jobs execute exactly the same Yarn version defined by the project.
Installing the global Yarn CLI via npm is straightforward. Since npm ships by default with Node.js, you can run:
sudo npm install -g yarn
Once the installation completes, verify that the global CLI is reachable. Run:
yarn --version
If you see something like 1.22.x, that is the global Classic launcher working properly. From here on, when you step into a project that uses Berry, this global command will transparently hand control to the locally configured Yarn release stored in the repository.
Installing Yarn In A Specific JavaScript Project
With the global CLI ready, the next step is to “pin” a particular Yarn version to each project. This is exactly what ensures consistency across your teammates’ machines and your CI/CD pipelines: everyone runs the same Yarn binary and therefore shares the same behavior.
Start by navigating to your project’s directory (or create a new one if you are kicking off a fresh app). For example:
mkdir my-project
cd my-project
Inside that folder, use the special yarn set version command to select a modern Berry release. A typical choice is to track the actively developed “berry” line:
yarn set version berry
Behind the scenes, Yarn resolves “berry” to the latest Berry binary, downloads it, and stores it inside a .yarn/releases directory in your project. At the same time, it creates or updates a .yarnrc.yml file at the project root to tell the global launcher to delegate to that local binary.
If you now run yarn --version again from inside the project, the output will change to the project-pinned version. You might see something like 4.5.0 or another 3.x/4.x release, indicating that you are no longer using the global Classic CLI, but the local Berry one hosted in your repository.
From this moment, every Yarn command executed in that directory (or its subdirectories) will use the project-specific Yarn version. This allows a team to gradually migrate different projects to newer Yarn releases at their own pace while still having a single global launcher installed on developer machines.
Core Yarn Commands For Everyday Development
Once Yarn is installed and wired into your project, you only need a small subset of commands to cover most daily tasks. The CLI is extensive, but a handful of subcommands already take you far in terms of managing dependencies and scripts.
Whenever you feel stuck or want to explore more options, every command accepts a help flag. Running:
yarn --help
prints general help, while appending --help after a specific subcommand gives you contextual usage tips. For example, yarn install --help explains all available flags for the dependency installation process.
To bootstrap a brand-new project, you can ask Yarn to generate the basic configuration files. Inside an empty folder, simply run:
yarn init
That command walks you through a few prompts and writes a package.json plus a yarn.lock file. The former declares your project metadata, scripts and dependencies, while the latter acts as the canonical record of the exact versions Yarn resolved at install time.
When joining an existing repository that already uses Yarn, the typical starting point is installing all dependencies. From the project root, you just run:
yarn install
Yarn then reads package.json and yarn.lock, downloads whatever is missing, and sets up the dependency tree. Thanks to caching, subsequent installs, even on CI, will be much faster than the initial run.
Adding new dependencies is equally straightforward with the add subcommand. To install, for instance, Express, you would use:
yarn add express
This single command fetches the package, updates package.json and refreshes yarn.lock. No need to manually edit JSON files; Yarn keeps declared ranges and locked versions in sync for you.
Quick Sanity Check: Small Express Server With Yarn
If you want to be absolutely sure Yarn is working as intended, you can write a tiny smoke test using Express. Assuming you are inside a project and have already run yarn add express, create a file named index.js with the following minimal server:
const express = require("express");
const app = express();
app.get("/", (req, res) => res.send("Yarn is working!"));
app.listen(3000, () => console.log("Server running on http://localhost:3000"));
Instead of calling Node directly, you can run this script through Yarn, which can be convenient in PnP environments. Use:
yarn node index.js
Open another terminal and verify that the server responds correctly. A simple:
curl http://localhost:3000
should return the message “Yarn is working!”. If that happens, you know Yarn resolved the dependency, wired up the module resolution, and executed the script with no issues.
Managing Dependencies, Scripts And The Yarn Cache
Beyond installation and basic additions, Yarn exposes several utilities to maintain and evolve your dependency graph cleanly. These commands avoid manual editing and keep package.json and yarn.lock consistent at all times.
To remove a dependency you no longer need, use the remove subcommand. For example:
yarn remove package-name
Yarn will uninstall the package, drop it from package.json, and update the lockfile accordingly. This prevents stale or unused modules from lingering in your dependency tree.
Upgrading dependencies can be done in bulk or for a specific package. Without arguments,
yarn upgrade
resolves compatible newer versions according to your declared ranges, while:
yarn upgrade package-name
targets a single dependency. In both cases, Yarn rewrites yarn.lock to reflect the updated dependency graph.
When your project defines scripts in package.json, Yarn’s run subcommand is the way to execute them. A script like:
"scripts": {
"start": "node index.js"
}
can be launched with:
yarn run start
and in many cases the shorter yarn start alias also works. This provides a clean abstraction layer on top of Node or other tools, regardless of your underlying module linking strategy.
Yarn keeps a local or global cache of all previously downloaded packages to speed up installs and provide offline behavior. Sometimes, especially after experiments or multiple version switches, that cache can become noisy or corrupted. Whenever you suspect cache issues, you can reset it with:
yarn cache clean
If you are curious about where Yarn is storing those cached artifacts, yarn cache dir prints the location. This is particularly handy when you need to whitelist the cache folder in an antivirus on Windows to avoid slow installs caused by aggressive scanning of every downloaded file.
Configuring Yarn With .yarnrc.yml
Modern Yarn centralizes project configuration in the .yarnrc.yml file. This YAML document controls how dependencies are linked, where caches live, how strict PnP should be, registry URLs, telemetry, and more.
A typical configuration could look like this:
nodeLinker: pnp
pnpMode: strict
compressionLevel: mixed
enableGlobalCache: true
enableTelemetry: false
The nodeLinker setting is especially important because it defines how modules are resolved. Valid options include pnp (Plug’n’Play with no node_modules folder), node-modules (classic layout), and sometimes a pnpm-style linker. If you run into compatibility problems with tools that hardcode node_modules assumptions, switching to node-modules often solves them.
compressionLevel tells Yarn how aggressively it should compress cached packages. A value of 0 disables compression entirely for maximum speed, 1 forces full compression for minimal disk usage, and mixed balances both worlds, which tends to be the sensible default for most teams.
Enabling enableGlobalCache causes Yarn to reuse a shared cache directory across multiple projects. That way, if several repos depend on the same libraries, Yarn avoids downloading them multiple times, saving both network bandwidth and disk space.
Lastly, enableTelemetry controls whether Yarn sends anonymous usage information back to the maintainers. Many companies prefer turning this off for privacy and compliance reasons, while others leave it on to help guide the project roadmap; either way, it is just a flag in this configuration file.
Git Integration: What To Commit And What To Ignore
Because Yarn stores some of its machinery inside a .yarn directory, it is important to be deliberate about what goes into version control. Some of those files should absolutely be tracked, while others are cache or build artifacts that would only bloat the repository.
A minimal .gitignore strategy used in many Berry projects looks like this:
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
.pnp.*
This pattern ignores the entire .yarn folder but then whitelists subdirectories that need to be committed. The releases directory, for example, contains the project-specific Yarn binary; without it, other developers might not get the exact same CLI version when cloning the repo.
Other whitelisted paths such as .yarn/plugins or .yarn/sdks hold custom plugins and editor integrations. Keeping them under version control ensures that everyone on the team shares the same plugin set and language tooling support.
The .pnp.* entries are the Plug’n’Play manifest files that describe the dependency tree if you are using PnP mode. Committing them is essential for reproducible and sometimes even zero-install workflows, where CI or new clones can run the project immediately without having to regenerate the manifests.
On top of this, remember that yarn.lock is a first-class citizen in your repository. It must always be committed and updated alongside dependency changes, otherwise all the determinism benefits of Yarn disappear.
Yarn vs npm: When Yarn Really Shines
Yarn and npm solve the same core problem: managing Node.js dependencies, but Yarn differentiates itself in several practical scenarios. The most visible difference is often performance: through parallelised installs and smarter caching, Yarn frequently completes installs significantly faster on large projects.
Disk usage is another strong point, especially if you embrace PnP. By eliminating node_modules, a typical project can consume dramatically less disk space, and tools that integrate well with PnP can benefit from faster module resolutions since Node no longer has to walk deep and repetitive directory trees.
In terms of lockfile behavior, Yarn’s yarn.lock is widely appreciated for being compact and highly deterministic. It explicitly records resolution decisions, making it easier to understand why a particular version was chosen and to debug version conflicts.
Monorepos are an area where Yarn has long been ahead thanks to its workspaces feature. With workspaces, multiple packages in a single repository share a lockfile, dependencies are hoisted efficiently, and local packages are automatically linked without configuration boilerplate.
Real-world use cases where Yarn clearly stands out often include complex CI/CD setups, large shared codebases, or environments behind corporate proxies and custom certificates. For example, running yarn install --immutable inside CI ensures that the install will fail if the yarn.lock file does not match package.json, which catches inconsistent dependency states before they make it to production.
On the other hand, npm is still a perfectly valid choice for smaller projects or teams deeply invested in the npm ecosystem. If you only maintain a handful of services with modest dependency trees and do not rely heavily on monorepos or PnP, the simplicity of sticking to npm may outweigh Yarn’s advanced features.
Operating-System Specific Installation Options
Although the npm-based installation of the global CLI works almost everywhere, Yarn also offers OS-specific distributions and scripts. These are useful if you prefer native package managers or if you are operating on systems without a convenient npm setup.
On macOS, a very popular choice is installing Yarn via Homebrew. A typical flow, assuming you already have Node (possibly also via Homebrew), is:
brew install yarn
If you use nvm or another Node version manager, ensure that the shims directory appears before any Homebrew Node in your PATH. Otherwise, you may end up using a different Node version than expected when running Yarn scripts.
Another option on macOS is MacPorts, which can install both Node.js and Yarn if they are not yet present. For even more control, Yarn also publishes an installation shell script that works on macOS and generic Unix; piping that script into your shell downloads and sets up Yarn in one go.
On Windows, the recommended paths are the MSI installer, Chocolatey, or Scoop. The MSI installer walks you through a graphical wizard and usually ensures Node.js is present as part of the process. Scoop, on the other hand, lets you install Yarn from the command line, optionally suggesting Node if it is missing.
When installing Yarn on Windows, it is a very good idea to whitelist both your project folder and the Yarn cache directory, typically under %LocalAppData%\Yarn, in your antivirus. Otherwise, every single file download and write may be scanned, dramatically slowing your installs.
Linux distributions often provide multiple options: official system packages, Yarn’s own repositories, or manual tarball installations. On Debian and Ubuntu, for instance, you can add Yarn’s APT repository, optionally configure NodeSource to get a recent Node.js, and then install Yarn through apt.
On distributions like CentOS, Fedora, RHEL, or Arch, Yarn offers GPG-signed tarballs that you can download and unpack anywhere on disk. In those manual setups, you typically need to verify the tarball’s signature with GPG and then add the unpacked Yarn directory to your PATH so the yarn command is available system-wide.
PATH Configuration On Unix, Linux And Windows
A common source of confusion during installation is PATH configuration: Yarn might be installed, but the shell cannot find the binary. In that case, you must update your environment so the Yarn directory is included in the PATH variable.
For manual tarball installs on Unix-like systems, the usual pattern is to export a path pointing to Yarn’s bin directory. For example:
export PATH="$PATH:/opt/yarn-[version]/bin"
You place this line in your shell profile file (such as .bashrc, .bash_profile, .zshrc, or similar), then open a new terminal session or source the file so the change takes effect. Once done, yarn --version should work from any directory.
If you lean on yarn global commands, Yarn also needs to ensure that its global bin folder is on the PATH. A quick way to accomplish this is to extend your profile with:
export PATH="$PATH:`yarn global bin`"
Fish shell users instead rely on fish_user_paths and can run:
set -U fish_user_paths (yarn global bin) $fish_user_paths
On Windows, you might need to manually add the Yarn binary directory to the PATH environment variable as well. A simple example would be:
set PATH=%PATH%;C:\.yarn\bin
In practice, graphical installers or Windows package managers often handle PATH configuration for you, but it is useful to know how to adjust it manually when something does not work as expected.
Typical Installation Problems And How To Fix Them
Even with clear documentation, certain installation issues appear again and again when teams adopt Yarn. Fortunately, most of them have well-understood, repeatable solutions.
One recurring problem is permission-related errors when installing the global CLI via npm. If your npm prefix points to a system-owned directory, a command like:
sudo npm install -g yarn
may work but is not ideal in the long term. A better pattern is to configure npm to use a user-owned global directory instead. You can check your current prefix with:
npm config get prefix
If it points to something under /usr, create your own directory and reconfigure npm. For instance:
mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
export PATH=~/.npm-global/bin:$PATH
After reloading your shell configuration, you should be able to install Yarn globally without sudo, avoiding many permission headaches.
Another frequent source of confusion is version discrepancies between the global Yarn and the project Yarn. Remember that this is intentional: the global 1.x CLI is just a launcher, and when used inside a Berry-configured project, it delegates to whatever release resides in .yarn/releases.
If packages appear to be missing even though Yarn reported a successful install, you might be running into a tool that does not yet understand PnP. Some editors, linters, or build tools assume a node_modules directory and fail when it does not exist. Common solutions include enabling Yarn’s SDKs for editors, using compatible tools from Yarn’s support matrix, or temporarily switching the linker to node-modules via .yarnrc.yml.
Lockfile conflicts are inevitable in active teams where multiple branches add or change dependencies in parallel. When yarn.lock conflicts during a merge, an effective strategy is to pick one branch as the baseline, resolve textual conflicts by hand in favor of that baseline where possible, and then run yarn install to regenerate a clean lockfile that you commit again as the new source of truth.
Cache-related issues are usually solved by a simple yarn cache clean followed by a fresh yarn install. If Yarn behaves oddly, packages look outdated, or strange resolution errors pop up, cleaning the cache and reinstalling often returns the system to a sane state without further investigation.
Post-Installation Checks And Ongoing Performance Tuning
Once Yarn is installed and successfully running commands, it is worth running a few quick health checks to be sure everything is wired correctly for your project. The first and simplest is confirming the version:
yarn --version
After that, in any project that already has a package.json, executing yarn install without errors is a strong indicator that your environment, registry access, and Node version are compatible. If your dependencies are heavy, you may want to watch the install time; on subsequent runs, Yarn’s caching and concurrency should bring that time down considerably.
Yarn also offers commands like yarn outdated to see which packages have newer versions available, and yarn list --depth=0 to print all top-level dependencies actually installed. These tools help you keep track of dependency drift and decide when to schedule upgrades.
Performance-wise, there are several levers you can pull after installation. Settings like networkConcurrency, custom cache folders, or disabling verbose progress bars in CI can shave seconds or even minutes off big installs. For example, increasing concurrency with:
yarn config set network-concurrency 8
allows Yarn to send more network requests in parallel, often speeding downloads on fast connections.
Finally, for very large monorepos or multi-environment setups, combining Yarn with scalable infrastructure (such as container-based CI pipelines or cloud build platforms) lets you fully exploit its deterministic and cache-friendly design. Because every install is guided by yarn.lock and PnP or node_modules metadata, caches shared between CI nodes or reused across builds can dramatically cut down installation times.
All in all, taking the time to understand how to install Yarn correctly, pin it per project, adjust PATH and configuration, and leverage its caching and workspace features pays off quickly. You end up with faster installs, more predictable builds, better monorepo ergonomics, and a dependency management workflow that is easier to reproduce across teammates, CI/CD systems, and production environments.