npx skills add ...
npx skills add vercel/turbo --skill turborepo
Turborepo monorepo build system guidance. Triggers on: turbo.json, task pipelines, dependsOn, caching, remote cache, the "turbo" CLI, --filter, --affected, CI optimization, environment variables, internal packages, monorepo structure/best practices, and boundaries.
This repo is now called vercel/turborepo. Both names install the same content, but the install count here only covers this one.
npx skills add vercel/turbo --skill turborepo
Build system for JavaScript/TypeScript monorepos. Turborepo caches task outputs and runs tasks in parallel based on dependency graph.
Prefer package tasks over Root Tasks.
When creating tasks/scripts/pipelines, you MUST default to package tasks:
package.jsonturbo.jsonpackage.json only delegates via turbo run <task>DO NOT put task logic in root package.json when it can live in packages. This defeats Turborepo's parallelization.
Root Tasks (//#taskname) are ONLY for tasks that truly cannot exist in packages, such as Vitest Projects' //#test, repo-wide release scripts, or tooling that does not invoke turbo itself.
turbo run vs turboAlways use turbo run when the command is written into code:
The shorthand turbo <tasks> is ONLY for one-off terminal commands typed directly by humans or agents. Never write turbo build into package.json, CI, or scripts.
--affected is the primary way to run only changed packages. It compares against main (falling back to master) — not the repo's configured default branch — and includes dependents. Set TURBO_SCM_BASE for any other base branch.
turbo Shorthand in Codeturbo run is recommended in package.json scripts and CI pipelines. The shorthand turbo <task> is intended for interactive terminal use.
Root package.json scripts MUST delegate to turbo run, not run tasks directly.
&& to Chain Turbo TasksDon't chain turbo tasks with &&. Let turbo orchestrate.
prebuild Scripts That Manually Build DependenciesScripts like prebuild that manually build other packages bypass Turborepo's dependency graph.
However, the fix depends on whether workspace dependencies are declared:
If dependencies ARE declared (e.g., "@repo/types": "workspace:*" in package.json), remove the prebuild script. Turbo's dependsOn: ["^build"] handles this automatically.
If dependencies are NOT declared, the prebuild exists because ^build won't trigger without a dependency relationship. The fix is to:
"@repo/types": "workspace:*"prebuild scriptKey insight: ^build only runs build in packages listed as dependencies. No dependency declaration = no automatic build ordering.
globalDependenciesglobalDependencies affects ALL tasks in ALL packages via the global hash — tasks cannot opt out of specific files, even with negation globs in inputs. Be specific.
With futureFlags.globalConfiguration, this problem is reduced because global.inputs files are folded into each task's inputs (not the global hash). Tasks can exclude specific files:
Look for repeated configuration across tasks that can be collapsed. Turborepo supports shared configuration patterns.
When to use global vs task-level:
globalEnv / globalDependencies - affects ALL tasks, use for truly shared configenv / inputs - use when only specific tasks need itenv ArraysA large env array (even 50+ variables) is not a problem. It usually means the user was thorough about declaring their build's environment dependencies. Do not flag this as an issue.
--parallel FlagThe --parallel flag bypasses Turborepo's dependency graph. It is deprecated and will be removed in a future major version—use task configuration (persistent, with) instead.
When multiple packages need different task configurations, use Package Configurations (turbo.json in each package) instead of cluttering root turbo.json with package#task overrides.
Benefits of Package Configurations:
$TURBO_EXTENDS$ to inherit + extend arraysWhen to use package#task in root:
"deploy": { "dependsOn": ["web#build"] })See references/configuration/RULE.md#package-configurations for full details.
../ to Traverse Out of Package in inputsDon't use relative paths like ../ to reference files outside the package. Use $TURBO_ROOT$ instead.
outputs for File-Producing TasksBefore flagging missing outputs, check what the task actually produces:
"build": "tsc", "test": "vitest")Common outputs by framework:
[".next/**", "!.next/cache/**", "!.next/dev/**"]["dist/**"]["dist/**"] or custom outDirTypeScript --noEmit can still produce cache files:
When incremental: true in tsconfig.json, tsc --noEmit writes .tsbuildinfo files even without emitting JS. Check the tsconfig before assuming no outputs:
To determine correct outputs for TypeScript tasks:
incremental or composite is enabled in tsconfigtsBuildInfoFile for custom cache location (default: alongside outDir or in project root)tsc --noEmit produces no files^build vs build Confusion.env Files Not in InputsTurbo does NOT load .env files - your framework does. But Turbo needs to know about changes:
.env File in MonorepoA .env file at the repo root is an anti-pattern — even for small monorepos or starter templates. It creates implicit coupling between packages and makes it unclear which packages depend on which variables.
Problems with root .env:
If you must share variables, use globalEnv to be explicit about what's shared, and document why.
By default, Turborepo filters environment variables to only those in env/globalEnv. CI variables may be missing:
Or use --env-mode=loose (not recommended for production).
Add a transit task if you have tasks that need parallel execution with cache invalidation (see below).
^dev Pattern (for turbo watch)A dev task with dependsOn: ["^dev"] and persistent: false in root turbo.json may look unusual but is correct for turbo watch workflows:
Why this works:
@acme/db, @acme/validators) have "dev": "tsc" — one-shot type generation that completes quicklypersistent: true for actual dev servers (Next.js, etc.)turbo watch re-runs the one-shot package dev scripts when source files change, keeping types in syncIntended usage: Run turbo watch dev (not turbo run dev). Watch mode re-executes one-shot tasks on file changes while keeping persistent tasks running.
Alternative pattern: Use a separate task name like prepare or generate for one-shot dependency builds to make the intent clearer:
Some tasks can run in parallel (don't need built output from dependencies) but must invalidate cache when dependency source code changes.
The problem with dependsOn: ["^taskname"]:
The problem with dependsOn: [] (no dependencies):
Transit Nodes solve both:
The transit task creates dependency relationships without matching any actual script, so tasks run in parallel with correct cache invalidation.
How to identify tasks that need this pattern: Look for tasks that read source files from dependencies but don't need their build outputs.
With futureFlags.globalConfiguration, the same config moves global settings under global — and .env becomes a per-task input instead of a global hash input:
| File | Purpose |
|---|---|
| configuration/RULE.md | turbo.json overview, Package Configurations |
| configuration/tasks.md | dependsOn, outputs, inputs, env, cache, persistent |
| configuration/global-options.md | globalEnv, globalDependencies, global key, futureFlags, cacheDir, envMode |
| configuration/gotchas.md | Common configuration mistakes |
| File | Purpose |
|---|---|
| caching/RULE.md | How caching works, hash inputs |
| caching/remote-cache.md | Vercel Remote Cache, self-hosted, login/link |
| caching/gotchas.md | Debugging cache misses, --summarize, --dry |
| File | Purpose |
|---|---|
| environment/RULE.md | env, globalEnv, passThroughEnv |
| environment/modes.md | Strict vs Loose mode, framework inference |
| environment/gotchas.md | .env files, CI issues |
| File | Purpose |
|---|---|
| filtering/RULE.md | --filter syntax overview |
| filtering/patterns.md | Common filter patterns |
| File | Purpose |
|---|---|
| ci/RULE.md | General CI principles |
| ci/github-actions.md | Complete GitHub Actions setup |
| ci/vercel.md | Vercel deployment, turbo-ignore |
| ci/patterns.md | --affected, caching strategies |
| File | Purpose |
|---|---|
| cli/RULE.md | turbo run basics |
| cli/commands.md | turbo run flags, turbo-ignore, other commands |
| File | Purpose |
|---|---|
| best-practices/RULE.md | Monorepo best practices overview |
| best-practices/structure.md | Repository structure, workspace config, TypeScript/ESLint setup |
| best-practices/packages.md | Creating internal packages, JIT vs Compiled, exports |
| best-practices/dependencies.md | Dependency management, installing, version sync |
| File | Purpose |
|---|---|
| watch/RULE.md | turbo watch, interruptible tasks, dev workflows |
| File | Purpose |
|---|---|
| boundaries/RULE.md | Enforce package isolation, tag-based dependency rules |
This skill is based on the official Turborepo documentation at:
apps/docs/content/docs/ in the Turborepo repository