turborepo

SKILL.md

Turborepo

You are an expert in Turborepo v2.8 — "the build system for agentic coding" — a high-performance build system for JavaScript/TypeScript monorepos, built by Vercel with a Rust-powered core.

Key Features

  • Task caching: Content-aware hashing — only rebuilds when files actually change
  • Remote caching: Share build caches across machines and CI via Vercel
  • Parallel execution: Uses all CPU cores automatically
  • Incremental builds: --affected flag runs only changed packages + dependents
  • Pruned subsets: Generate minimal monorepo for deploying a single app
  • Dependency graph awareness: Understands package relationships
  • Git worktree cache sharing: Automatically shares local cache across worktrees (2.8+)
  • Devtools: Visual package and task graph explorer via turbo devtools (2.8+)
  • Composable configuration: Extend turbo.json from any package, not just root (2.7+)
  • AI-enabled docs: turbo docs returns markdown responses optimized for AI agents (2.8+)

Setup

npx create-turbo@latest
# or add to existing monorepo:
npm install turbo --save-dev
# upgrade existing Turborepo:
npx @turbo/codemod migrate

turbo.json Task Pipeline

The turbo.json file defines your task dependency graph. Here are comprehensive examples:

Basic pipeline

{
  "$schema": "https://turborepo.dev/schema.json",
  "tasks": {
    "build": {
      "description": "Compile TypeScript and bundle the application",
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"]
    },
    "test": {
      "description": "Run the test suite",
      "dependsOn": ["build"]
    },
    "lint": {
      "description": "Lint source files"
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

Advanced pipeline with environment variables and inputs

{
  "$schema": "https://turborepo.dev/schema.json",
  "globalDependencies": [".env"],
  "globalEnv": ["CI", "NODE_ENV"],
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"],
      "env": ["DATABASE_URL", "NEXT_PUBLIC_API_URL"],
      "inputs": ["src/**", "package.json", "tsconfig.json"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"],
      "env": ["TEST_DATABASE_URL"]
    },
    "test:unit": {
      "dependsOn": [],
      "outputs": ["coverage/**"]
    },
    "lint": {
      "inputs": ["src/**", ".eslintrc.*"]
    },
    "typecheck": {
      "dependsOn": ["^build"],
      "inputs": ["src/**", "tsconfig.json"]
    },
    "db:generate": {
      "cache": false
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "clean": {
      "cache": false
    }
  }
}

Key Configuration

  • dependsOn: ["^build"] — Run build in dependencies first (^ = topological)
  • dependsOn: ["build"] — Run build in the same package first (no ^)
  • outputs — Files to cache (build artifacts)
  • inputs — Files that affect the task hash (default: all non-gitignored files)
  • env — Environment variables that affect the task hash
  • cache: false — Skip caching (for dev servers, codegen)
  • persistent: true — Long-running tasks (dev servers)
  • globalDependencies — Files that invalidate all task caches when changed
  • globalEnv — Env vars that invalidate all task caches when changed

Workspace Filtering

Run tasks in specific packages or subsets of your monorepo:

# Single package
turbo build --filter=web

# Package and its dependencies
turbo build --filter=web...

# Package and its dependents (what depends on it)
turbo build --filter=...ui

# Multiple packages
turbo build --filter=web --filter=api

# By directory
turbo build --filter=./apps/*

# Packages that changed since main
turbo build --filter=[main]

# Combine: changed packages and their dependents
turbo build --filter=...[main]

# Exclude a package
turbo build --filter=!docs

# Packages matching a pattern
turbo build --filter=@myorg/*

Filter syntax reference

Pattern Meaning
web Only the web package
web... web and all its dependencies
...web web and all its dependents
...web... web, its dependencies, and its dependents
./apps/* All packages in the apps/ directory
[main] Packages changed since main branch
{./apps/web}[main] web only if it changed since main
!docs Exclude the docs package

CI Matrix Strategies

GitHub Actions — parallel jobs per package

name: CI
on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # Required for --affected
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: turbo build test lint --affected
        env:
          TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
          TURBO_TEAM: ${{ vars.TURBO_TEAM }}

  deploy-web:
    needs: build
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: turbo build --filter=web
        env:
          TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
          TURBO_TEAM: ${{ vars.TURBO_TEAM }}

Dynamic matrix from workspace list

jobs:
  detect:
    runs-on: ubuntu-latest
    outputs:
      packages: ${{ steps.list.outputs.packages }}
    steps:
      - uses: actions/checkout@v4
      - id: list
        run: |
          PACKAGES=$(turbo ls --affected --output=json | jq -c '[.[].name]')
          echo "packages=$PACKAGES" >> "$GITHUB_OUTPUT"

  test:
    needs: detect
    if: needs.detect.outputs.packages != '[]'
    runs-on: ubuntu-latest
    strategy:
      matrix:
        package: ${{ fromJson(needs.detect.outputs.packages) }}
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: turbo test --filter=${{ matrix.package }}

Remote caching in CI

# Set in CI environment
TURBO_TOKEN=your-vercel-token
TURBO_TEAM=your-vercel-team

# Builds automatically use remote cache
turbo build

Watch Mode

Run tasks in watch mode for development — re-executes when source files change:

# Watch a specific task
turbo watch test

# Watch with a filter
turbo watch test --filter=web

# Watch multiple tasks
turbo watch test lint

Watch mode respects the task graph — if test depends on build, changing a source file re-runs build first, then test.

Persistent tasks vs watch

  • persistent: true in turbo.json: The task itself is long-running (e.g., next dev). Turbo starts it and keeps it alive.
  • turbo watch: Turbo re-invokes the task on file changes. Use for tasks that run and exit (e.g., vitest run, tsc --noEmit).

Boundary Rules

Enforce architectural constraints across your monorepo with boundaries in turbo.json:

{
  "boundaries": {
    "tags": {
      "apps/*": ["app"],
      "packages/ui": ["shared", "ui"],
      "packages/utils": ["shared"],
      "packages/config": ["config"]
    },
    "rules": [
      {
        "from": ["app"],
        "allow": ["shared"]
      },
      {
        "from": ["shared"],
        "deny": ["app"]
      }
    ]
  }
}

This enforces:

  • Apps can import shared packages
  • Shared packages cannot import from apps
  • Violations produce build-time errors with turbo boundaries
# Check boundary compliance
turbo boundaries

# Add to your pipeline
{
  "tasks": {
    "check": {
      "dependsOn": ["lint", "typecheck", "boundaries"]
    },
    "boundaries": {}
  }
}

Graph Visualization

Inspect your task dependency graph:

# Print graph to terminal
turbo build --graph

# Output as DOT format (Graphviz)
turbo build --graph=graph.dot

# Output as JSON
turbo build --graph=graph.json

# Open interactive graph in browser
turbo build --graph=graph.html

Dry run — see what would execute

# Show tasks that would run without executing them
turbo build --dry-run

# JSON output for programmatic use
turbo build --dry-run=json

The dry run output shows:

  • Each task that would execute
  • Cache status (HIT or MISS)
  • Dependencies and dependents
  • File hash used for caching

Devtools & Docs (2.8+)

# Visual package/task graph explorer (hot-reloads on changes)
turbo devtools

# Search Turborepo docs from the terminal (returns agent-friendly markdown)
turbo docs

# Upgrade to latest Turborepo
npx @turbo/codemod migrate

Note: turbo docs output is optimized for AI coding agents — markdown format preserves context windows. The docs site also includes sample prompts for common tasks you can copy directly into your agent.

Composable Configuration (2.7+)

Package configs can now extend from any workspace package, not just the root:

// packages/ui/turbo.json
{
  "extends": ["@myorg/config"],
  "tasks": {
    "build": {
      "outputs": ["dist/**"]
    }
  }
}

Common Commands

# Run build across all packages
turbo build

# Run only affected packages (changed since main branch)
turbo build --affected

# Run specific tasks in specific packages
turbo build --filter=web

# Run with remote caching
turbo build --remote-cache

# Prune monorepo for a single app deployment
turbo prune web --docker

# List all packages
turbo ls

# List affected packages
turbo ls --affected

Remote Caching

# Login to Vercel for remote caching
turbo login

# Link to a Vercel team
turbo link

# Now builds share cache across all machines
turbo build  # Cache hits from CI, teammates, etc.

Monorepo Structure

my-monorepo/
├── turbo.json
├── package.json
├── apps/
│   ├── web/           # Next.js app
│   │   └── package.json
│   ├── api/           # Backend service
│   │   └── package.json
│   └── docs/          # Documentation site
│       └── package.json
├── packages/
│   ├── ui/            # Shared component library
│   │   └── package.json
│   ├── config/        # Shared configs (eslint, tsconfig)
│   │   └── package.json
│   └── utils/         # Shared utilities
│       └── package.json
└── node_modules/

--affected Flag

The most important optimization for CI pipelines:

# Only build/test packages that changed since main
turbo build test lint --affected

This performs intelligent graph traversal:

  1. Identifies changed files since the base branch
  2. Maps changes to affected packages
  3. Includes all dependent packages (transitively)
  4. Runs tasks only for the affected subgraph

Microfrontends & Multi-App Composition

Turborepo is the recommended orchestration layer for Vercel's Microfrontends architecture — composing multiple independently-deployed apps behind a single URL.

Monorepo Structure for Microfrontends

my-platform/
├── turbo.json
├── package.json
├── apps/
│   ├── shell/          # Layout / shell app (owns top-level routing)
│   ├── dashboard/      # Micro-app: dashboard features
│   ├── settings/       # Micro-app: settings features
│   └── marketing/      # Micro-app: public marketing site
└── packages/
    ├── ui/             # Shared component library
    ├── auth/           # Shared auth utilities
    └── config/         # Shared tsconfig, eslint

Independent Deploys

Each micro-app is a separate Vercel project with its own build and deploy lifecycle:

# Deploy only the dashboard micro-app
turbo build --filter=dashboard

# Deploy all micro-apps in parallel
turbo build --filter=./apps/*

# Deploy only micro-apps that changed since main
turbo build --filter=./apps/*...[main]

Shared Packages Across Micro-Apps

Use Turborepo's dependency graph to share code without coupling deploys:

{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"]
    }
  }
}

Shared packages (ui, auth, config) are built first via ^build, then each micro-app builds against the latest shared code. Remote caching ensures shared package builds are never repeated across micro-app deploys.

Multi-Zone Patterns

Next.js multi-zones let each micro-app own a URL path prefix while sharing a single domain:

// apps/shell/next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  async rewrites() {
    return [
      { source: '/dashboard/:path*', destination: 'https://dashboard.example.com/dashboard/:path*' },
      { source: '/settings/:path*', destination: 'https://settings.example.com/settings/:path*' },
    ]
  },
}

export default nextConfig

Combine with Turborepo boundary rules to enforce architectural isolation:

{
  "boundaries": {
    "tags": {
      "apps/*": ["micro-app"],
      "packages/ui": ["shared"],
      "packages/auth": ["shared"]
    },
    "rules": [
      { "from": ["micro-app"], "allow": ["shared"] },
      { "from": ["shared"], "deny": ["micro-app"] }
    ]
  }
}

When to Use Turborepo for Microfrontends

Scenario Recommended?
Multiple teams owning independent features Yes — independent deploys + shared packages
Single team, single app No — standard Next.js is simpler
Shared component library across apps Yes — packages/ui with boundary rules
Gradual migration from monolith Yes — extract features into micro-apps incrementally
Need version-skew protection Yes — isolated builds per micro-app

Related Documentation

Bun Support & Lockfile Detection

Turborepo 2.6+ has stable Bun support with granular lockfile analysis:

  • Lockfile format: Turborepo requires bun.lock (text format). If only bun.lockb (binary) is found, it errors with a prompt to generate a text lockfile. Generate with bun install --save-text-lockfile.
  • Granular cache invalidation: Turborepo parses bun.lock to detect which specific packages changed and only invalidates caches for affected tasks — not the entire monorepo.
  • Pruning: turbo prune works with Bun workspaces, generating a minimal lockfile for single-app deploys.
  • Skip-builds detection: On Vercel, monorepo workspace detection automatically skips unaffected projects when bun.lock changes don't touch a project's dependencies. Combined with --affected, only changed packages and their dependents rebuild.
# Ensure text lockfile for Turborepo compatibility
bun install --save-text-lockfile

# Run only affected packages (works with Bun lockfile detection)
turbo build --affected

Known issue: turbo prune with Bun 1.3+ may produce lockfiles with formatting differences that break bun i --frozen-lockfile. Track fixes in turborepo#11007.

Deploying to Vercel

Vercel auto-detects Turborepo and optimizes builds. Each app in apps/ can be a separate Vercel project with automatic dependency detection.

When to Use Turborepo

Scenario Use Turborepo?
Single Next.js app No — Turbopack handles bundling
Multiple apps sharing code Yes — orchestrate builds
Shared component library Yes — manage dependencies
CI taking too long Yes — caching + affected
Team sharing build artifacts Yes — remote caching
Enforcing architecture boundaries Yes — boundary rules
Complex multi-step CI pipelines Yes — task graph + matrix

Official Documentation

Weekly Installs
9
GitHub Stars
7
First Seen
9 days ago
Installed on
opencode9
gemini-cli8
github-copilot8
codex8
kimi-cli8
cursor8