.agents/skills/turborepo/references/configuration/RULE.md
Configuration reference for Turborepo. Full docs: https://turborepo.dev/docs/reference/configuration
Root turbo.json lives at repo root, sibling to root package.json:
my-monorepo/
├── turbo.json # Root configuration
├── package.json
└── packages/
└── web/
├── turbo.json # Package Configuration (optional)
└── package.json
Always use package tasks. Only use Root Tasks if you cannot succeed with package tasks.
Package tasks enable parallelization, individual caching, and filtering. Define scripts in each package's package.json:
// packages/web/package.json
{
"scripts": {
"build": "next build",
"lint": "eslint .",
"test": "vitest",
"typecheck": "tsc --noEmit"
}
}
// packages/api/package.json
{
"scripts": {
"build": "tsc",
"lint": "eslint .",
"test": "vitest",
"typecheck": "tsc --noEmit"
}
}
// Root package.json - delegates to turbo
{
"scripts": {
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test",
"typecheck": "turbo run typecheck"
}
}
When you run turbo run lint, Turborepo finds all packages with a lint script and runs them in parallel.
Root Tasks are a fallback, not the default. Only use them for tasks that truly cannot run per-package (e.g., repo-level CI scripts, workspace-wide config generation).
// AVOID: Task logic in root defeats parallelization
{
"scripts": {
"lint": "eslint apps/web && eslint apps/api && eslint packages/ui"
}
}
{
"$schema": "https://v2-8-21-canary-9.turborepo.dev/schema.json",
"globalEnv": ["CI"],
"globalDependencies": ["tsconfig.json"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
The $schema key enables IDE autocompletion and validation.
futureFlags.globalConfigurationWhen the globalConfiguration future flag is enabled, global options move under a global key with cleaner names:
{
"$schema": "https://v2-8-21-canary-9.turborepo.dev/schema.json",
"futureFlags": { "globalConfiguration": true },
"global": {
"inputs": ["tsconfig.json"],
"env": ["CI"],
"ui": "tui"
},
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}
See the global options reference for the full rename mapping and behavior changes.
Global options - Settings affecting all tasks:
globalEnv, globalDependencies, globalPassThroughEnv, cacheDir, daemon, envMode, ui, remoteCacheglobalConfiguration flag: all of the above move under the global key (see global options)Task definitions - Per-task settings in tasks object:
dependsOn, outputs, inputs, envcache, persistent, interactive, outputLogsUse turbo.json in individual packages to override root settings:
// packages/web/turbo.json
{
"extends": ["//"],
"tasks": {
"build": {
"outputs": [".next/**", "!.next/cache/**"]
}
}
}
The "extends": ["//"] is required - it references the root configuration.
When to use Package Configurations:
You can extend from config packages instead of just root:
// packages/web/turbo.json
{
"extends": ["//", "@repo/turbo-config"]
}
$TURBO_EXTENDS$By default, array fields in Package Configurations replace root values. Use $TURBO_EXTENDS$ to append instead:
// Root turbo.json
{
"tasks": {
"build": {
"outputs": ["dist/**"]
}
}
}
// packages/web/turbo.json
{
"extends": ["//"],
"tasks": {
"build": {
// Inherits "dist/**" from root, adds ".next/**"
"outputs": ["$TURBO_EXTENDS$", ".next/**", "!.next/cache/**"]
}
}
}
Without $TURBO_EXTENDS$, outputs would only be [".next/**", "!.next/cache/**"].
Works with:
dependsOnenvinputsoutputspassThroughEnvwithUse extends: false to exclude a task from a package:
// packages/ui/turbo.json
{
"extends": ["//"],
"tasks": {
"e2e": {
"extends": false // UI package doesn't have e2e tests
}
}
}
turbo.jsonc for CommentsUse turbo.jsonc extension to add comments with IDE support:
// turbo.jsonc
{
"tasks": {
"build": {
// Next.js outputs
"outputs": [".next/**", "!.next/cache/**"]
}
}
}