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

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-utils

Linking 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:

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.