Monorepos with Nx and Turborepo: How to Manage Multiple Projects Without Losing Your Mind
Hello HaWkers, does your company have frontend, backend, mobile app, shared libraries, all in separate repositories creating versioning chaos?
Monorepos emerged as a solution to this problem. Google, Facebook, Microsoft - tech giants maintain MILLIONS of lines of code in single repositories. In 2025, tools like Nx and Turborepo made this approach accessible to any team. Let's explore.
What is a Monorepo and Why It Matters
Monorepo: Single Git repository containing multiple related projects (apps, libs, packages).
Don't confuse with:
- Monolith: Single application architecture
- Multirepo: Each project in separate repository
Nx: The Complete Giant
Nx is developed by Nrwl and is the most powerful and complex. Google uses Nx internally to manage Angular, and the tool supports ANY stack.
Initial Setup
# Create Nx workspace
npx create-nx-workspace@latest my-monorepo
# Choose: integrated (recommended) or package-based
# Generated structure:
my-monorepo/
βββ apps/
β βββ frontend/ # React/Vue/Angular app
β βββ backend/ # Node.js API
β βββ mobile/ # React Native
βββ libs/
β βββ ui/ # Shared components
β βββ data-access/ # Data layer
β βββ utils/ # Utilities
βββ nx.json # Nx configuration
βββ package.jsonCreating Apps and Libs
# Generate React application
nx g @nx/react:app frontend
# Generate shared library
nx g @nx/react:lib ui --directory=libs/ui
# Generate Node.js API
nx g @nx/node:app backend
# Generate pure TypeScript lib
nx g @nx/js:lib utils --directory=libs/utils
Dependency Graph: Powerful Visualization
# View dependency graph
nx graph
# Nx generates interactive visualization showing:
# - Which apps depend on which libs
# - Circular dependencies (errors!)
# - Impact of changes
Smart Builds: Affected Commands
# Test ONLY what changed
nx affected:test
# Build only affected apps/libs
nx affected:build
# Lint only modified code
nx affected:lint
# How does Nx know what changed?
# Compares with base branch (ex: main) and analyzes dependenciesPractical Example
# You modify libs/utils/src/date.ts
# Nx automatically detects:
# - apps/frontend depends on libs/utils
# - apps/backend depends on libs/utils
# - apps/mobile does NOT depend on libs/utils
# affected:build command only rebuilds:
nx affected:build
# β libs/utils
# β apps/frontend
# β apps/backend
# β apps/mobile (skipped!)
# Savings: 40-60% build time
Turborepo: Simplicity and Speed
Turborepo (created by Vercel) focuses on simplicity and aggressive caching. Fewer features than Nx, but instant setup.
Turborepo Setup
# Create monorepo
npx create-turbo@latest my-turborepo
# Generated structure:
my-turborepo/
βββ apps/
β βββ web/ # Next.js app
β βββ docs/ # Documentation
βββ packages/
β βββ ui/ # Component library
β βββ config/ # Shared configs
β βββ tsconfig/ # TypeScript configs
βββ turbo.json # Pipeline config
βββ package.jsonPipeline Configuration
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"]
},
"lint": {
"outputs": []
},
"dev": {
"cache": false
}
}
}Running Tasks
# Build everything (with cache)
turbo run build
# Test in maximum parallel
turbo run test
# Dev mode in multiple apps
turbo run dev --parallel
# Remote cache (Vercel)
turbo run build --token=<your-token>
# Cache shared across entire team!
Nx vs Turborepo: Direct Comparison
Performance: Nx Wins at Scale
Official benchmarks (large-monorepo):
- Nx: ~50s for complete build
- Turborepo: ~350s for complete build
- Nx is 7x faster in large monorepos!
Simplicity: Turborepo Wins
# Turborepo: Setup in minutes
npx create-turbo@latest
# Nx: More initial decisions
npx create-nx-workspace@latest
# β Choose preset
# β Configure CI
# β Define caching strategyEcosystem: Nx More Complete
Nx offers:
- Official plugins (React, Angular, Node, Next, Nest, etc)
- Generators (automatic code scaffolding)
- Migrate (automatic dependency updates)
- Nx Cloud (enterprise distributed caching)
Turborepo offers:
- Core build/cache system (extremely fast)
- Perfect integration with Vercel
- Simplicity (fewer abstractions)
When to Use Each?
Use Nx if:
- Large monorepo (10+ apps/libs)
- Large team (15+ devs)
- Multiple technologies (React + Angular + Node)
- Need complex CI/CD
- Want advanced tools (generators, migrations)
Use Turborepo if:
- Small/medium monorepo (< 10 projects)
- Homogeneous stack (ex: only Next.js)
- Small team (< 10 devs)
- Value simplicity over features
- Use Vercel for deployment
Practical Example: Shared Components Library
// packages/ui/src/Button.tsx
import React from 'react';
export interface ButtonProps {
variant?: 'primary' | 'secondary';
children: React.ReactNode;
onClick?: () => void;
}
export function Button({ variant = 'primary', children, onClick }: ButtonProps) {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
>
{children}
</button>
);
}
// apps/web/pages/index.tsx
import { Button } from '@my-monorepo/ui';
export default function Home() {
return (
<div>
<h1>My App</h1>
<Button variant="primary">Click Me</Button>
</div>
);
}
// apps/admin/pages/dashboard.tsx
import { Button } from '@my-monorepo/ui';
export default function Dashboard() {
return (
<div>
<h1>Admin Dashboard</h1>
<Button variant="secondary">Settings</Button>
</div>
);
}
// Changes in packages/ui affect BOTH apps
// Nx/Turborepo rebuild automatically
CI/CD Optimized for Monorepos
# .github/workflows/ci.yml (Nx)
name: CI
on: [push]
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0 # Important for affected
- uses: actions/setup-node@v3
- run: npm ci
# Run ONLY affected tests
- run: npx nx affected:test --base=origin/main
# Build only modified apps
- run: npx nx affected:build --base=origin/main
# Conditional deploy
- run: npx nx affected --target=deploy --base=origin/main# .github/workflows/ci.yml (Turborepo)
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
# Vercel remote cache
- run: npx turbo run test --token=${{ secrets.TURBO_TOKEN }}
- run: npx turbo run build --token=${{ secrets.TURBO_TOKEN }}
Common Pitfalls
1. Phantom Dependencies
App works locally but breaks in production because it depended on undeclared lib.
Solution: Use --strict mode in Nx, or externals in Turborepo.
2. Initially Slow Builds
First build can be VERY slow.
Solution: Configure remote cache (Nx Cloud or Turborepo Remote Cache).
3. Version Conflicts
Multiple apps need different versions of React.
Solution: Use resolutions (Yarn) or overrides (npm) carefully.
The Future of Monorepos
- Rust-based tools (like Turbopack) bringing 10x superior performance
- AI-powered refactoring that understands complex dependencies
- Distributed execution native (builds on multiple machines)
Monorepos are not for all projects, but for teams managing multiple related apps, they are game changers. Choose Nx for maximum power, Turborepo for maximum simplicity.
If you want to understand patterns that help in any project, big or small, see: Shortcuts for Conditional Operations where we explore clean and efficient code.
Let's go! π¦
π» Master JavaScript for Real
The knowledge you acquired in this article is just the beginning. There are techniques, patterns, and practices that transform beginner developers into sought-after professionals.
Invest in Your Future
I've prepared complete material for you to master JavaScript:
Payment options:
- 3x of R$34.54 interest-free
- or R$97.90 upfront

