
Monorepo is a software development strategy that creates shared libraries and dependencies in a single repository. By centralizing codebases, improving collaboration, and streamlining dependency management, Monorepos offer a solution for crucial dependency management.
This article explains the process of creating monorepos using well-known tools and technologies like TypeScript, Turborepo, and Nx, allowing you to choose the best tool based on your requirements.
If you’re a hands-on learner, I created a video to walk through this process. Feel free to follow along as you read!
Imagine managing three separate applications for a project: a Web App, a Mobile App, and an Admin Panel. Each resides in its git repository and shares a common UI component library. Now think, when you need to update a critical button component for accessibility.
The process becomes a headache:
Sounds frustrating, right? This is where monorepos comes in—a simpler approach where all your projects live under one roof, making it easier to coordinate changes and maintain consistency across your codebase.
Let’s start with monorepos…
In the following sections, we will create basic utils monorepo which can be used anywhere in the project.
In this section, we will build a monorepo manually using just TypeScript and npm.
npm init -y
npm install typescript
npm install --save-dev @types/node ts-nodeCreate a packages folder and a utils directory within it to act as a shared library.
mkdir -p packages/utilsInit npm inside utils with scope @monorepo,
npm init --scope @monorepo --workspace ./packages/utils -ypackages/utils/package.json will look like this,
{
"name": "@monorepo/utils",
"version": "1.0.0",
"main": "index.js",
"devDependencies": {},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": ""
}This will also add a workspace field in the root’s package.json like below. If you have multiple monorepos, you can update workspace using wildcards like "packages/*".
"workspaces": [
"packages/utils" or "packages/*"
]root and packages, npx tsc --init && cd packages/utils && npx tsc --initpackages/utils’s tsconfig.jsonas needed. In my case it is,"rootDir": "./src",
"outDir": "./dist", // make sure to update mainNote: Make sure to update the main field in the package.json based on your outDir. For me, it is
dist/index.js.
utils/tsconfig.json .root’s tscofig.json"compilerOptions": {...},
"references": [
{"path": "./packages/utils" },
]"compilerOptions": {
...,
"baseUrl": "./",
"paths": {
"@monorepo/utils": ["packages/utils/*"],
}
},packages/utils/src/index.ts// packages/utils/src/index.ts
export function multiply(a: number, b: number): number {
return a * b;
}"build": "tsc --build" in the root and utils package.json scripts, then build with npm run build.@monorepo/utils into the root file to verify the function works as expected.npm install @monorepo/utilsimport { multiply } from "@monorepo/utils";
const result = multiply(2, 3);
console.log(`result: ${result}`);Yeah, You have created a basic monorepo setup. You can build as many as monorepos and use them like this.
Monorepo’s with only typescript is useful for small, simple projects or experimental purposes without requiring third-party tooling.
Next, let’s create monorepo with Turborepo.
Note: Repeat till step-3 to init setup in both turbo and nx, then follow below steps.
Turborepo is a high-performance monorepo build tool that simplifies dependency management and speeds up builds through caching.
Follow the below steps to configure monorepo using Turborepo.
npm install turbo -DpackageManager to each package.json to ensure compatibility with Turborepo."packageManager": "npm@<npm-version>",Create a turbo.json in the root directory:
{
"$schema": "https://turborepo.org/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"dev": {
"cache": false
}
}
}tasks section defines tasks like build dependencies and caching behavior.data-transform package to illustrate Turborepo's dependency management.data-transform,export function transformData(data: any): any {
return data.map((item) => ({ ...item, transformed: true }));
}npx turbo buildYou can install @monorepo/data-transform into the root or utils package, and verify it's working as expected.
Ideal for mid-size projects that need optimized build times and clear dependency management.
Now let’s create monorepo with NX.
Nx offers a highly integrated monorepo experience, by implementing an efficient caching mechanism, storing results from previous executions.
It includes code generation tools that scaffold various applications, libraries, and components like React, Angular, Vue, etc…
Nx provides Nx cloud for distributed caching, making builds even faster in CI/CD and visual dependency graphs for a better understanding of project dependencies for complex projects.
Follow the below steps to configure monorepo using Nx.
npx nx@latest initnx.json file and scaffold dependencies for managing projects. Add the below script in nx.json,{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"defaultBase": "master",
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"outputs": ["{projectRoot}/dist"], /** output directory **/
"cache": true
}
}
}build tasks. You can run a single task like this,npx nx build @monorepo/utilsnpx nx run-many -t buildNx is suited for large projects with complex dependency chains, making it ideal for enterprise-level applications.
Monorepo is a powerful way to unify your codebase, improve consistency, and reduce overhead, especially in teams handling multiple interconnected projects. Adopting a monorepo approach can lead to a cleaner development workflow, easier dependency management, and more scalable infrastructure for your applications.
Choosing the right tool from all three depends on factors like your project size, the frequency of cross-project dependencies, and the level of automation required:

