# NPM Workspaces
January 12, 2026 Javascript Node Setup
NPM workspaces is a mechanism for sharing code between different Javascript projects. This article walks through workspaces setup and configuration.
workspaces is a features provided by npm to create monorepos and share code between different projects.
Creating workspaces
# create root package.json
$ npm init -y
# create a workspace named "common"
$ npm init -y -w common
# create a workspace named "module-one"
$ npm init -y -w module-one
The above commands will result in the following project structure.
monorepo/
common/
package.json
module-one/
package.json
Ideally, we should have src directories inside all our workspaces.
$ mkdir ./common/src ./module-one/src
We will likely be building our projects using typescript and swc. We can add their configuration globally.
{
"compilerOptions": {
"target": "es2020",
"module": "es2020",
"allowJs": true,
"removeComments": true,
"resolveJsonModule": true,
"typeRoots": ["./node_modules/@types"],
"sourceMap": true,
"outDir": "dist",
"strict": true,
"lib": ["es2020"],
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"moduleResolution": "Node",
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"@common/*": ["./common/src/*"],
"@module-one/*": ["./module-one/src/*"]
}
},
"include": ["*/src/**/*"],
"exclude": ["node_modules", "**/node_modules/**"]
}
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": false,
"decorators": false,
"dynamicImport": true
},
"target": "es2020",
"baseUrl": ".",
"paths": {
"@common/*": ["./common/src/*"],
"@module-one/*": ["./module-one/src/*"]
}
},
"module": {
"type": "commonjs"
}
}
node_modules
build
.DS_Store
Note: Configurations for jest, eslint and prettier can also be added globally i.e. to the root package.json.
For reference, jest config entry inside root package.json will look like this.
{
"jest": {
"transform": {
"^.+\\.(t|j)sx?$": "@swc/jest"
},
"testEnvironment": "node",
"modulePathIgnorePatterns": ["<rootDir>/build/"],
"moduleNameMapper": {
"@common/(.*)": "<rootDir>/common/src/$1",
"@module-one/(.*)": "<rootDir>/module-one/src/$1"
}
}
}
Root level scripts
{
"scripts": {
"build": "npm run type-check && swc ./*/src --out-dir build",
"type-check": "tsc --noEmit",
"test": "jest",
"fmt": "npx prettier --write ./*/src/",
"lint": "npx eslint --fix ./*/src/ --ext .ts",
"clean": "rm -rvf ./build"
}
}
Usage example
// file: common/src/entities.ts
import crypto from "node:crypto";
class Password {
private value: string;
constructor(clearText: string) {
this.value = clearText;
}
}
export class User {
public id: string;
public password: Password;
constructor(
public email: string,
password: string,
) {
this.id = crypto.randomUUID();
this.password = new Password(password);
}
}
// file: module-one/src/main.ts
import { User } from "@common/user";
async function main() {
const user = new User("admin@site.com", "abc123123123");
console.log(user);
}
main().catch(console.error);
For references, module-one package.json will have the following script.
{
"scripts": {
"start": "NODE_ENV=production node ../build/module-one/src/main.js"
}
}
# build using root command
$ npm run build
# execute module one entry-point
$ npm run start -w module-one