Skip to main content

Turborepo

What is Turborepoโ€‹

Turborepo is a task runner and build system for JavaScript and TypeScript monorepos, maintained by Vercel and licensed under MIT (currently version 2.x). It runs the package.json scripts you already have, orders them by the dependency graph between workspaces, runs independent tasks in parallel, and caches their outputs so unchanged work is not repeated.

It sits on top of the package manager rather than replacing it, so it works with npm workspaces out of the box.

Why we recommend itโ€‹

  • Fills the gaps in npm workspaces โ€” npm run --workspaces runs scripts in declaration order with no caching. Turborepo adds topological ordering, parallelism, and caching without changing the package manager.
  • Local and remote caching โ€” task outputs and logs are cached by a hash of their inputs. A cache hit replays the result instead of re-running the task, locally or in CI via a shared remote cache.
  • Affected-only runs โ€” turbo run test --affected only runs tasks for packages changed relative to the base branch, which keeps CI time under control as the repo grows.
  • Low adoption cost โ€” one turbo.json at the root; existing scripts stay as they are and can be migrated incrementally.
  • Pruned deploys โ€” turbo prune <app> produces a minimal subset of the monorepo (with a trimmed lockfile) for building a single app's Docker image.

When to useโ€‹

  • Monorepos with several apps or packages where some depend on the build output of others (e.g. a shared UI library consumed by multiple apps)
  • When CI time is dominated by rebuilding and retesting packages that did not change
  • When build, lint, test, and typecheck need to run consistently across every workspace from the root

When NOT to useโ€‹

  • Single-package repositories โ€” plain npm scripts are enough
  • Monorepos with two or three packages and a fast build โ€” npm run <script> --workspaces is simpler
  • Polyglot monorepos (e.g. JVM, Python, or Go alongside JS) โ€” consider a language-agnostic tool such as Nx or Bazel instead

Minimal setupโ€‹

npm install --save-dev --save-exact turbo
package.json (root)
{
"private": true,
"workspaces": ["apps/*", "packages/*"],
"scripts": {
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test"
}
}
turbo.json
{
"$schema": "https://turborepo.dev/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"lint": {},
"test": {
"dependsOn": ["^build"]
}
}
}
  • "^build" means "build this package's workspace dependencies first".
  • outputs lists what to cache and restore; tasks without outputs (like lint) only cache logs.
  • Turborepo detects the package manager from the packageManager field (e.g. "packageManager": "npm@<version>"), so set it in the root package.json.
caution

Environment variables are part of the cache key only if Turborepo knows about them. Declare the ones a task depends on in env (per task) or globalEnv, otherwise a cache hit can replay output built with a different configuration.

Remote cacheโ€‹

The remote cache is opt-in. Options are Vercel's hosted cache (npx turbo login and npx turbo link) or a self-hosted server implementing the Remote Cache API, for example backed by a Google Cloud Storage bucket. For client projects, check where build artifacts are allowed to be stored before enabling a third-party cache.

Resourcesโ€‹