How to Manage Monorepos with npm Workspaces
This article provides an overview of npm workspaces, explaining how this built-in npm feature enables developers to manage multi-package JavaScript repositories (monorepos) efficiently. You will learn the core concepts behind workspaces, why they are beneficial, how to configure them in a root repository, and how to execute common dependency management and script-running tasks across multiple packages.
What Are npm Workspaces?
npm workspaces is a feature introduced in npm v7 that provides native support for managing multiple packages from within a single top-level root package. Commonly referred to as a monorepo architecture, this structure allows teams to keep distinct libraries, services, or applications inside a single repository while treating each subdirectory as an independent npm package.
Workspaces solve common monorepo challenges by automating symlinking between local packages and handling dependency resolution from the root level, removing the need for external tooling like Lerna or Yarn for basic monorepo functionality.
Key Benefits of npm Workspaces
- Centralized Dependency Management: Dependencies
shared across packages can be hoisted to a single root
node_modulesfolder, reducing disk usage and preventing version drift. - Automatic Symlinking: If Package A depends on Package B within the same repository, npm creates a direct symlink during installation. Changes made to Package B are instantly reflected in Package A without needing to publish or run manual linking commands.
- Single Install Step: Running
npm installat the root installs all dependencies across all defined workspaces simultaneously. - Targeted Script Execution: You can run tests, builds, or custom scripts across all workspaces at once or target specific packages individually from the root directory.
Setting Up an npm Workspace
To create an npm workspace, define a workspaces array in
your root package.json file pointing to the folders
containing your individual packages.
Example Directory Structure
my-monorepo/
├── package.json
└── packages/
├── shared-utils/
│ └── package.json
└── web-app/
└── package.json
Root package.json Configuration
{
"name": "my-monorepo",
"private": true,
"workspaces": [
"packages/*"
]
}The "private": true setting prevents the root repository
from being accidentally published to the npm registry. The wildcard
"packages/*" tells npm to treat every subdirectory within
packages/ as a standalone workspace.
Managing Dependencies
Installing Dependencies for a Specific Workspace
To install an external library into a specific package, use the
-w (or --workspace) flag:
npm install lodash -w packages/shared-utilsLinking Local Packages
To make web-app depend on shared-utils, add
the package name and version to the dependencies field of
packages/web-app/package.json:
{
"name": "web-app",
"version": "1.0.0",
"dependencies": {
"shared-utils": "^1.0.0"
}
}Run npm install at the root. npm will automatically link
packages/shared-utils directly inside
packages/web-app/node_modules.
Running Scripts Across Workspaces
npm provides straightforward commands to run lifecycle scripts defined inside individual packages:
Run a script in a specific package:
npm run build -w web-appRun a script across all packages:
npm run test --workspacesRun a script across all packages while ignoring missing scripts:
npm run build --workspaces --if-present
Summary
npm workspaces offer a built-in, lightweight solution for organizing complex JavaScript and TypeScript projects into multi-package repositories. By simplifying dependency installation, local package linking, and unified script execution, workspaces streamline development workflows without requiring third-party orchestration tools.