Turborepo & Monorepo Tooling
Build scalable monorepos with Turborepo: task pipelines, remote caching, workspace structure, and code sharing between apps and packages.
Turborepo is a high-performance build system for JavaScript/TypeScript monorepos. It accelerates CI by running tasks in parallel across workspaces, caching task outputs locally and remotely, and only re-running tasks when their inputs change.
Use Turborepo when you have or expect multiple related apps/packages in one repository. Its task pipeline and caching dramatically reduce CI times. Start with npm/pnpm workspaces + Turborepo - don't add NX unless you need its code generation and enforcement features.
Workflow
- Set up workspace structureCorrect workspace structure is the foundation for Turborepo's task graph.
- Define the task pipelinePipeline defines task dependency order - build must run before deploy, type-check before lint.
- Enable remote cachingRemote caching shares build artifacts between CI runs and team members - skip unchanged builds entirely.
- Share code with internal packagesInternal packages enable code reuse without npm publishing - changes are immediately reflected in all consumers.
- Filter tasks for changed packagesRunning all tasks on every commit is wasteful - filter to only changed packages and their dependents.
Output Format
pnpm workspaces, `turbo.json` pipeline with `^build` deps, outputs configured for caching, remote cache via Vercel, `--filter=[HEAD^1]` for affected-only CI runs.
Examples
Pipeline: `{ "pipeline": { "build": { "dependsOn": ["^build"], "outputs": ["dist/**", ".next/**", "!.next/cache/**"] }, "test": { "dependsOn": ["build"], "inputs": ["src/**/*.ts", "tests/**"], "outputs": ["coverage/**"] } } }`Root script: `{ "scripts": { "build": "turbo run build", "dev": "turbo run dev", "test": "turbo run test --filter=...[HEAD^1]", "lint": "turbo run lint" } }`Internal dep: `// apps/web/package.json: { "dependencies": { "@myorg/ui": "workspace:*" } }; // usage: import { Button } from "@myorg/ui"; // resolves to packages/ui/src/index.ts at dev time`Edge Cases & Gotchas
- Version management: in a monorepo, decide on fixed versioning (all packages same version, Lerna) or independent (each package versions separately, Changesets). Changesets is the current best practice
- Peer dependencies: shared packages should list React as `peerDependency`, not `dependency` - prevents multiple React instances when multiple packages are consumed together
- IDE performance: large monorepos with many packages can slow TypeScript language server. Use `ts-project-references` or Turborepo's `experimental.useProjectReferences` for better TS perf
When to Break These Rules
- For teams with very different tech stacks between services (Go backend, JS frontend, Python ML), polyrepo with shared contract testing is cleaner than forcing everything into one monorepo.
Reference Files
Turborepo docs: https://turbo.build/repo/docsTurborepo remote caching: https://turbo.build/repo/docs/core-concepts/remote-cachingChangesets: https://github.com/changesets/changesetspnpm workspaces: https://pnpm.io/workspaces