Monorepos let multiple projects share code, dependencies, and tooling. At Google, everything lives in one monorepo. Here's how to do it at smaller scale.
Why Monorepo?
- Code sharing: Shared UI components, utilities, types across apps
- Atomic changes: Update a shared library and all consumers in one PR
- Consistent tooling: Same ESLint, TypeScript, test config everywhere
- Simplified dependencies: One node_modules, no version conflicts
pnpm Workspaces (Foundation)
// pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
// Project structure
├── apps/
│ ├── web/ # Main web app
│ ├── admin/ # Admin dashboard
│ └── mobile/ # React Native app
├── packages/
│ ├── ui/ # Shared component library
│ ├── utils/ # Shared utilities
│ ├── types/ # Shared TypeScript types
│ └── config/ # Shared ESLint, TS configs
├── pnpm-workspace.yaml
└── package.jsonTurborepo
Build system for monorepos. Caches task results and runs tasks in parallel.
// turbo.json
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"]
},
"lint": {}
}
}
// Run all builds (with caching)
turbo run build
// >>> FULL TURBO: 3/3 tasks cachedNx
More opinionated than Turborepo. Includes code generation, dependency graph visualization, and affected commands.
// Only test what changed
nx affected --target=test
// Visualize dependency graph
nx graph
// Generate a new library
nx generate @nx/react:library shared-uiComparison
| Feature | Turborepo | Nx |
|---|---|---|
| Setup complexity | Low | Medium |
| Caching | Local + Remote | Local + Nx Cloud |
| Code generation | No | Yes (powerful) |
| Dependency graph | Basic | Advanced + visualization |
| Plugin ecosystem | Minimal | Rich |
| Learning curve | Easy | Moderate |
When to Use a Monorepo
- Multiple apps sharing significant code
- Team ownership across packages
- Consistent versioning and deployment
When NOT to
- Single application
- Teams with very different tech stacks
- When CI/CD can't handle the scale