Workspaces & Monorepos

What problem do workspaces solve?

Imagine you're building a web app and a small library it uses, and you want them in the same repository so they're easy to change together. Without help, each folder would need its own install and you'd have to manually link them. Workspaces are npm's built-in way to manage many packages inside one repo — a setup often called a monorepo.

Heads up: this website is a single package, so it doesn't use workspaces — you only need them once you have several packages living together. This chapter is "here's how, when you do".

The shape of a workspace repo

You have one root package.json that lists where the sub-packages live, and each sub-package has its own package.json as normal:

my-repo/
├─ package.json          // the root — declares the workspaces
├─ packages/
│  ├─ app/
│  │  └─ package.json    // a package named "app"
│  └─ ui/
│     └─ package.json    // a package named "ui"

The root package.json just needs a workspaces field:

{
  "name": "my-repo",
  "private": true,
  "workspaces": ["packages/*"]
}

The root is marked "private": true because it's only a container — you'd never publish the repo wrapper itself.

One install for everything

The headline benefit: a single npm install at the root sets up all the packages at once. It also creates symlinks so that when "app" depends on "ui", it uses your local "ui" directly — edit one, the other sees it immediately, no publishing needed.

Install the whole monorepo

Terminal
$ npm install   # run once, in the repo root
In VS Code
  1. Open the repo root folder in VS Code
  2. Run npm install in the terminal
  3. Every workspace is installed together, with shared dependencies hoisted to the root node_modules

'Hoisting' means common dependencies are stored once at the root instead of duplicated in each package — saving lots of space.

Working with a specific package

The -w (workspace) flag targets one package. Use it to add a dependency to just that package, or to run one package's script:

Target one workspace

Terminal
$ npm install lodash -w ui      # add a dep to the 'ui' package only
$ npm run build -w app          # run app's build script
$ npm run test --workspaces     # run test in every package
In VS Code
  1. Run these from the repo root in the terminal
  2. -w <name> targets a single package; --workspaces fans out to all of them

The name after -w is the package's name from its own package.json, not the folder path.

When is a monorepo worth it?

  • Good fit: packages that change together (an app + its shared UI library), or a tool split into pieces you want to release in sync. One install, one place, atomic changes across packages.
  • Probably overkill: a single website or app (like this one), or unrelated projects that never share code — separate repos are simpler.

Beyond npm's built-in workspaces there are dedicated monorepo tools (Turborepo, Nx, pnpm workspaces) that add caching and smarter task running. They build on the same idea — start with npm workspaces, reach for those only when a big repo gets slow.

Up next: bending npm to your will with .npmrc configuration and custom registries.

FAQ

Frequently Asked Questions

What are npm workspaces?
Built-in support for keeping several packages in one repository (a "monorepo"). You declare a workspaces array in the root package.json, and a single npm install sets them all up together.
How do I install all packages in a monorepo?
Run npm install once in the repo root. npm installs every workspace, hoists shared dependencies to a single root node_modules, and symlinks the packages so they can use each other directly.
How do I run a script in just one workspace?
Use the -w flag: npm run build -w app runs the "app" package's build. --workspaces (plural) runs the script in every package instead.
When should I use a monorepo?
When packages change together — like an app and its shared UI library — so one commit can update both. For a single app or unrelated projects, separate repositories are simpler.