npm Workspaces: Linking Interdependent Packages in a Monorepo
npm workspaces let you manage interdependent packages in one repository by declaring them in a root package.json, hoisting shared dependencies, and symlinking local packages automatically.
28 Mar 2026, 23:39 UTC

When several packages in one repository import each other by relative path, every refactor risks breaking the import chain, and shared dependencies get installed multiple times. npm workspaces let you declare those packages in a single root package.json so that npm install links them automatically and hoists common dependencies to one top-level node_modules. The practical result is a monorepo that behaves like a single project without extra orchestration tools.
Prerequisites and version check
Workspaces require npm 7.0.0 or newer. Older npm versions silently ignore the workspaces field, which leaves you with an ordinary multi-folder repo and no linking. Run the following in any terminal:
npm --version
If the reported version is lower than 7.0.0, upgrade npm or switch to a Node.js distribution that bundles a newer npm before continuing.
Configure the workspace root
At the repository root, create or edit package.json so it lists the glob patterns that contain your packages. A common layout uses a packages/ directory:
{
"name": "acme-monorepo",
"private": true,
"workspaces": [
"packages/*"
]
}
The private: true flag prevents the root itself from being published. It does not protect the individual workspaces.
Declare local dependencies
Inside each workspace folder, add a normal package.json. When one package consumes another, reference it by its workspace name rather than a relative path. For example, if packages/core/package.json is named @acme/core, then packages/cli/package.json can depend on it like this:
{
"name": "@acme/cli",
"version": "1.0.0",
"dependencies": {
"@acme/core": "^1.0.0"
}
}
During installation, npm resolves @acme/core to the local folder and creates a symlink instead of downloading it from the registry. A symlink is a filesystem shortcut, so edits inside packages/core are visible to packages/cli immediately.
Install and verify linking
Run the install command from the repository root, not from inside a package:
npm install
This installs dependencies for every workspace and hoists shared versions to the root node_modules. To confirm the link, run npm ls from the dependent workspace directory:
# Run inside packages/cli
npm ls @acme/core
Check that the resolved path points to packages/core rather than the npm registry. You can also inspect node_modules/@acme/core to confirm it is a symlink.
Run scripts across workspaces
To execute a script defined in one workspace, use the -w (or --workspace) flag from the root:
npm run -w @acme/cli test
npm changes into that workspace before running the script, so relative paths inside the script behave as if you had run it locally. If you need a shared script that touches every workspace, define it in the root package.json and call npm run <script> --workspaces.
Publish selectively
Publishing changes public state, so verify metadata first with a dry run:
npm publish -w @acme/core --dry-run
When the reported contents look correct, remove --dry-run to publish. If your repository contains internal packages that must never reach the registry, add "private": true to their package.json; otherwise a bulk publish command can expose them.
Recovery options for common failures
Hoisting conflicts
Because npm hoists one version of a dependency to the root, two workspaces that need incompatible major versions can end up sharing the wrong copy. The safe recovery is to pin the exact version in the workspace that has the stricter requirement and let npm nest the conflicting copy inside that package's own node_modules.
Missing peer dependencies
npm does not automatically hoist peer dependencies. If a workspace expects a peer package, declare it explicitly in that workspace's devDependencies or dependencies; otherwise the package may resolve at install time but fail at runtime.
Migration breakage
When converting an existing single-package repository, build scripts that reference ../node_modules or hard-coded relative paths often break. Update them to rely on standard module resolution or workspace-relative paths so they work regardless of where npm hoists the dependency.
Accidental publication
If you published a workspace by mistake, you cannot delete the version, but you can deprecate it with npm deprecate <pkg>@<version> "reason" and immediately add "private": true to prevent recurrence.
Limitations
Workspaces do not replace task runners or build orchestrators; they only manage installation and linking. Complex cross-package build graphs still need a separate tool. Additionally, the workspaces field is npm-specific, so projects that must also support Yarn or pnpm need equivalent configuration in those package managers.
To check the final state, run npm install at the root, then execute npm ls in each dependent workspace and confirm every local dependency resolves to a folder inside the repository. If all links resolve and scripts run with -w, the monorepo is wired correctly.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.