When working on a monorepo with multiple packages, getting everything up and running efficiently can be a challenge. I’ve been there—jumping between terminal windows, manually starting different services, and hoping I didn’t forget anything. Fortunately, NPM workspaces provide a great way to manage multiple packages in a single repository, and with the right setup, starting all of them in development mode can be effortless.
Thank me by sharing on Twitter 🙏
I’ll walk through a few approaches to achieve this, ranging from built-in NPM commands to using turbo
, a high-performance task runner that makes managing workspaces even easier.
Setting Up a Dev Script with NPM Workspaces
NPM has built-in support for workspaces, which means we can run commands across multiple packages from the root of the project. To start, I make sure my package.json
at the root level includes the following:
{
"private": true,
"workspaces": ["packages/*"]
}
This tells NPM that all projects inside the packages/
folder should be treated as part of the workspace.
Next, I add a dev
script to my root package.json
:
The Technological Republic: Hard Power, Soft Belief, and the Future of the West
$22.51 (as of March 14, 2025 13:37 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)AI Engineering: Building Applications with Foundation Models
$59.98 (as of March 14, 2025 13:37 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)etguuds USB to USB C Cable 3ft, 2-Pack USB A to Type C Charger Cord Fast Charging for Samsung Galaxy A15 A25 A35 A55 A54, S24 S23 S22 S21 S20 S10 S10E, Note 20 10, Moto G, for iPhone 16 15, Gray
$6.99 (as of March 14, 2025 13:37 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.){
"scripts": {
"dev": "npm run dev --workspaces --if-present"
}
}
This command runs dev
in all workspace packages that define the script. The --if-present
flag ensures that it won’t fail if some packages don’t have a dev
script.
Each package inside packages/*
should have its own dev
script, like so:
{
"scripts": {
"dev": "next dev"
}
}
Now, when I run:
npm run dev
it starts the dev
script in all workspaces that have one. This is simple and effective but doesn’t allow much flexibility if I need to control execution order or handle dependencies between packages.
Running Scripts in Parallel with npm-run-all
While the built-in NPM approach is useful, sometimes I want more control. That’s where npm-run-all
comes in. First, I install it as a development dependency:
npm install npm-run-all --save-dev
Then, I modify my root package.json
:
{
"scripts": {
"dev": "npm-run-all --parallel dev:package1 dev:package2"
}
}
Since npm-run-all
doesn’t work with --workspaces
, I create scripts that explicitly run dev
for each package:
{
"scripts": {
"dev:package1": "npm --workspace=packages/package1 run dev",
"dev:package2": "npm --workspace=packages/package2 run dev"
}
}
Now, running npm run dev
starts both package1
and package2
in parallel. This approach works well if I need to start only specific workspaces instead of all of them at once.
Using turbo
for Efficient Execution
For larger projects, I prefer using turbo
, which speeds up execution by caching tasks and running only what’s necessary. First, I install turbo
:
npm install turbo --save-dev
Then, I update my root package.json
:
{
"scripts": {
"dev": "turbo run dev"
}
}
To configure turbo
, I create a turbo.json
file in the root of the project:
{
"$schema": "https://turbo.build/schema.json",
"ui": "tui",
"tasks": {
"build": {
"inputs": ["$TURBO_DEFAULT$", ".env*"],
"dependsOn": ["^build"],
"outputs": ["build/**", ".vercel/**", "dist/**", ".next/**", "!.next/cache/**"]
},
"test": {
"outputs": ["coverage/**"],
"dependsOn": []
},
"lint": {
"dependsOn": ["^build", "^lint"]
},
"check-types": {
"dependsOn": ["^build", "^check-types"]
},
"dev": {
"dependsOn": ["^dev"],
"cache": false,
"persistent": true
}
}
}
This setup ensures that turbo
runs dev
scripts efficiently across all workspaces. Running npm run dev
now starts everything while respecting dependencies and avoiding unnecessary work.
Choosing the Right Approach
Each of these methods works well depending on the needs of the project:
- Built-in NPM Workspaces – Great for simple monorepos where all packages need to start at once.
npm-run-all
– Useful when needing to control which workspaces run in parallel.turbo
– Best for performance optimization, ensuring only necessary tasks run.
By using the right tool for the job, I can make working with multiple packages in a monorepo much smoother. No more juggling terminal windows—just a single command to kick everything off.