Yarn workspaces let a single repository (or a monorepo) hold multiple packages that are developed together. You declare them in the root package.json workspaces field, and yarn install resolves the whole project at once, hoisting shared dependencies to the root and linking workspace packages to each other.
For the full configuration reference, see the Yarn workspaces documentation.
Set up Yarn workspaces
Section titled “Set up Yarn workspaces”1. Declare workspaces in package.json
Section titled “1. Declare workspaces in package.json”A Yarn workspace is a repository whose root package.json has a workspaces field listing the directories that hold package.json files. Mark the root private so it can't be published:
{ "name": "my-workspace", "private": true, "workspaces": ["apps/*", "packages/*"],}2. Create the workspace packages
Section titled “2. Create the workspace packages”Every directory matched by those globs that contains a package.json becomes a workspace. A common layout separates applications from shared packages:
Directorymy-workspace/
Directoryapps/
Directoryweb/
- package.json
Directorypackages/
Directoryshared-ui/
- package.json
- package.json
3. Link workspaces with the workspace: protocol
Section titled “3. Link workspaces with the workspace: protocol”To depend on another workspace, reference it with the workspace: protocol (Yarn 2 and later) so Yarn always resolves it to the local package. On publish, Yarn replaces workspace:* with the package's exact version:
{ "name": "web", "dependencies": { "react": "^19.0.0", "@my-workspace/shared-ui": "workspace:*", },}On Yarn 1 (Classic) there's no workspace: protocol, so reference the package by a version range that matches the local version, as npm workspaces do.
Run yarn install once at the root to install every package's dependencies and link the internal ones.
Run scripts with yarn workspaces foreach
Section titled “Run scripts with yarn workspaces foreach”yarn workspaces foreach runs a command across workspaces. The -A (--all) flag selects every workspace, -p runs in parallel, and -t orders execution topologically so dependencies build first:
yarn workspaces foreach -A run build # every workspaceyarn workspaces foreach -Apt run build # parallel, dependencies firstyarn workspace web run build # a single workspaceYarn 4 ships foreach out of the box. On Yarn 3, add it first with yarn plugin import workspace-tools. On Yarn 1 (Classic), the equivalent is yarn workspaces run build.
Do Yarn workspaces hoist dependencies?
Section titled “Do Yarn workspaces hoist dependencies?”Yes. With the default node-modules linker, Yarn hoists shared dependencies to the root node_modules, so each version is installed once and packages resolve it by walking up the directory tree. Only conflicting versions get nested inside an individual workspace's node_modules folder.
Hoisting means a workspace can import a dependency it never declared, which breaks the moment that package is published or moved. Declare every import in the workspace's own package.json. To restrict hoisting, set nmHoistingLimits in .yarnrc.yml (Yarn 2+). Yarn 1 used the nohoist field instead. Yarn PnP sidesteps hoisting entirely by resolving imports from a lockfile-driven map instead of node_modules.
Do Yarn workspaces replace a monorepo tool?
Section titled “Do Yarn workspaces replace a monorepo tool?”No. Yarn workspaces install and link packages, foreach -t orders scripts, and --since selects workspaces changed since a git ref, but nothing is cached and that change detection compares files, not task inputs, so CI still reruns tasks a change couldn't have affected.
When that becomes the bottleneck, add Nx on top without changing how Yarn works:
npx nx@latest initYour existing package.json scripts keep working and Yarn keeps managing installs.
Run tasks before and after Nx
Section titled “Run tasks before and after Nx”Before Nx, Yarn runs a script across workspaces, but it runs every task every time and has no notion of which workspaces a change affects:
yarn workspaces foreach -A run build # every workspaceyarn workspace web run build # a single workspaceAfter nx init, run the same scripts through Nx:
nx run-many -t build # every projectnx build web # a single projectnx affected -t build # only projects touched by your changesThe first run executes your scripts. A second run with no changes is restored from the cache, and nx affected skips the projects your change does not touch.
Configure caching and dev servers
Section titled “Configure caching and dev servers”nx init writes an nx.json with targetDefaults that control how targets behave:
{ "targetDefaults": { "build": { "cache": true, "dependsOn": ["^build"], }, "dev": { "continuous": true, }, },}cache: true makes a target cacheable: Nx hashes each project's inputs (source files, dependencies, and config) and restores its outputs from the cache when nothing has changed. Nothing is cached unless you opt in, so you stay in control. dependsOn: ["^build"] builds a project's dependencies first. continuous: true marks tasks that don't exit, like dev servers and watchers, so tasks that depend on them don't wait for them to complete.
Speed up CI with Nx Cloud
Section titled “Speed up CI with Nx Cloud”Nx Cloud adds more ways to speed up CI for a Yarn workspace: remote caching shares the cache across CI runs and teammates, Nx Agents distribute tasks across machines, and self-healing CI proposes fixes when tasks fail. For the full walkthrough, see Adding Nx to an existing monorepo and CI setup.
Working with a different package manager? See pnpm workspaces, npm workspaces, or Bun workspaces. New to monorepos? Start with what a monorepo is and why teams use one.