NPM Workspaces

Written by: Gimzani
Published on: 2026-05-15
NodeJS Logo.

I had been using NodeJS and NPM for years before I was aware of NPM Workspaces.

In my myriad of project work I had a desperate need for encapsulating packages into their own modules and running them in the same project, but I never knew how.

Now I’m going to write about it.

What are NPM Workspaces?

Workspaces are a way to bind together similar projects into a single project.

That’s really it.

Workspaces use subfolders to store projects and share a single node_modules folder to save space on disk. (node_modules folders can have thousands of files!)

It also gives you a single location from which to run your NPM scripts, while still defining each project individually.

Here, I’ll show you…

The best way to understand this is to run an experiment-project.

Create a folder somewhere on your PC and navigate into it. When there, open up a terminal in that folder and type:

npm init -y

This initializes an NPM package. The “-y” answers ‘yes’ to all of the questions, which will generate a default package.json file.

We’re going to delete 90% of what’s in there, so really, don’t bother with the questions. :)

Open the Package JSON file and change it to:

{
  "name": "my-project",
  "workspaces": [ ]
}

You can add “description”, “author”, “version” - whatever, but it isn’t necessary for our purposes here.

Now - create two folders at the root of the project, “client” and “server”, and run npm init -y on each of them.

You should have something like this:

├─ my-project
  ├─ client
  └─ package.json
  ├─ server
  └─ package.json
└─ package.json

The last thing to do is to add the folder names to the root package.json, like this:

{
  "name": "my-project",
  "workspaces": [ 
    "client",
    "server"
  ]
}

…and run:

npm i

This command sets up the ‘sym-links’ in your Operating System so that the project folder knows about your sub-projects!

This is the basic setup for “NPM Workspaces”.

How do you Use Workspaces?

Now that things are set up in their most basic form, let’s work with them.

Let’s say you want to create an ExpressJS server. To do this we need to install ExpressJS.

To install dependencies, you have to alter your install command slightly. Instead of npm i express you need to tell npm where you want to install express. You do that like this:

npm i express -w=server

You can probably see it already, but the “-w=” is a flag that points to the workspace via its workspace name.

You can also use this syntax to point to a path:

npm i express -w ./server

But I want to keep all of my “sub-packages” is a folder.

Organization! I like it!

You can do that in the root package.json file like this:

{
  "name": "my-project",
  "workspaces": [ 
    "client",
    "server",
    "packages/*"   <-- add this!
  ]
}

As you can see, you just need to give the folder name with a ”*” - (any GLOB pattern will work). Now you have an unlimited number of sub-folders in the “packages” directory that you can use as an NPM module.

You still have to target your install, though. So, let’s say I have a module “packages/cool-buttons”

First set up the folder in packages/cool-buttons and run npm init -y.

Then install your dependencies - (like maybe, dart sass?) - from the root, like this:

npm i sass -w=packages/cool-buttons

NPM will install your dependencies into the packages/cool-buttons folder.

What about scripts?

That’s the beauty of this system. If you define your scripts in your project packages, they are available.

If I have a script in /server to run my express server:

  "scripts": {
    "serve": "node index.js"
  },

I can run it from the root like this - (we’ll use the ‘path’ syntax here):

npm run serve -w ./server

BUT - I’m lazy! I don’t want to do all that typing!

That’s fine too, we can add scripts to our root package and call them from there:

{
  "name": "my-project",
  "scripts": {
    "server:dev": "npm run dev -w ./server",
    "server:serve": "npm run serve -w ./server",
    "client:dev": "npm run dev -w ./client"
  },
  "workspaces": [ 
    "client",
    "server",
    "packages/*"
  ]
}

In the example above, notice how there are two “dev” scripts - (one in “server” and one in “client”)?

You can use the following command to run them both:

npm run dev --workspaces --if-present

What’s with the --if-present flag?

Good eye!

I added this because of the “packages/*” folder. It may (or may not) have a “dev” command. If one of these packages does NOT have a dev command, the entire command will fail. --if-present prevents this.

If you only have “client” and “server” folders, then you wouldn’t need --if-present.

What if I want to run all of my tests in ONLY the /packages directory?

Now’re we’re talking strategy! Yes!

Let’s say you have unit tests in most of your /packages projects. A single command for all of those would be nice.

You’re in luck - to do that, run this:

npm run tests -w ./packages -- --if-present

Use a double dash (--) to pass extra arguments to the underlying script.

In this case, -w ./packages is an argument and not just a flag.

NPM Workspaces – The Game-Changer for NodeJS Development

NPM Workspaces revolutionize how we manage multiple projects within a single repository. By consolidating dependencies, scripts, and modules into a unified structure, they eliminate the chaos of juggling multiple node_modules folders and scattered project files. Whether you’re building a full-stack application with a client and server, or organizing reusable packages in a packages directory, Workspaces streamline your workflow while keeping everything tightly integrated.

The best part? You don’t need to sacrifice individuality—each workspace retains its own package.json, dependencies, and scripts, while benefiting from shared resources and centralized management. From installing dependencies with -w flags to running scripts across multiple workspaces with —if-present, the flexibility is unmatched.

If you’ve ever felt overwhelmed by fragmented projects or redundant installations, NPM Workspaces are your solution. Give them a try, and say goodbye to the days of juggling multiple VSCode instances!

I know I have, and I’m never looking back!

-GIM