Monorepos enable code sharing, consistent tooling, and atomic commits across packages. Here's how to set one up that scales.
Turborepo Setup
// package.json
{
"name": "my-monorepo",
"private": true,
"workspaces": ["apps/*", "packages/*"],
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev --parallel",
"test": "turbo run test",
"lint": "turbo run lint"
},
"devDependencies": { "turbo": "^2.0.0" }
}
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"remoteCache": { "enabled": true },
"tasks": {
"build": {
"dependsOn": ["^build"],
"inputs": ["src/**", "package.json", "tsconfig.json"],
"outputs": ["dist/**", ".next/**"]
},
"test": {
"dependsOn": ["build"],
"cache": true
},
"dev": {
"cache": false,
"persistent": true
}
}
}
Package Architecture
apps/
├── web/ # Next.js
├── mobile/ # React Native
└── api/ # Node.js
packages/
├── ui/ # Shared components
├── utils/ # Shared utilities
├── types/ # Shared TypeScript types
└── config/ # Shared ESLint/TS configs
// packages/ui/package.json
{
"name": "@company/ui",
"main": "./src/index.ts",
"exports": {
".": "./src/index.ts",
"./button": "./src/components/button/index.ts"
},
"scripts": {
"build": "tsup src/index.ts --format esm,cjs --dts"
},
"peerDependencies": { "react": ">=18" }
}
Shared TypeScript Config
// packages/config/tsconfig-base.json
{
"compilerOptions": {
"target": "ES2022",
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"verbatimModuleSyntax": true
}
}
// apps/web/tsconfig.json
{
"extends": "@company/config/tsconfig-base.json",
"compilerOptions": {
"lib": ["ES2022", "DOM"],
"module": "ESNext",
"jsx": "react-jsx"
}
}
CI with Remote Cache
# .github/workflows/ci.yml
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm }
- run: npm ci
- run: turbo run build test lint
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
# Remote cache: unchanged packages take 0ms
# Typical: 20min CI → 2min with cache hits
Nx for Very Large Repos
# Affected commands: only process changed packages
nx affected --target=build --base=origin/main
nx affected --target=test --base=HEAD~1
# Generate library
nx generate @nx/react:library --name=feature-auth
# Dependency graph visualization
nx graph
The monorepo investment pays off the moment your second app needs code from the first. Design boundaries around feature domains, not technical layers.
→ Analyze your package.json dependencies with the JSON Viewer tool.