turborepo
$
npx mdskill add openai/plugins/turborepoOptimize monorepo builds with intelligent caching and parallel execution.
- Accelerates CI by rebuilding only changed packages and sharing remote caches.
- Integrates with Vercel for distributed caching and Rust-powered core performance.
- Analyzes dependency graphs to execute only affected tasks via the --affected flag.
- Delivers actionable commands for parallel builds, pruning, and incremental CI.
SKILL.md
.github/skills/turborepoView on GitHub ↗
---
name: turborepo
description: Turborepo expert guidance. Use when setting up or optimizing monorepo builds, configuring task caching, remote caching, parallel execution, or the --affected flag for incremental CI.
metadata:
priority: 5
docs:
- "https://turborepo.dev/docs"
sitemap: "https://turborepo.dev/sitemap.xml"
pathPatterns:
- 'turbo.json'
- 'turbo/**'
bashPatterns:
- '\bturbo\s+(run|build|test|lint|dev)\b'
- '\bnpx\s+turbo\b'
- '\bbunx\s+turbo\b'
---
# 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
```bash
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
```json
{
"$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
```json
{
"$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:
```bash
# 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
```yaml
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
```yaml
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
```bash
# 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:
```bash
# 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:
```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`
```bash
# Check boundary compliance
turbo boundaries
# Add to your pipeline
{
"tasks": {
"check": {
"dependsOn": ["lint", "typecheck", "boundaries"]
},
"boundaries": {}
}
}
```
## Graph Visualization
Inspect your task dependency graph:
```bash
# 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
```bash
# 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+)
```bash
# 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:
```json
// packages/ui/turbo.json
{
"extends": ["@myorg/config"],
"tasks": {
"build": {
"outputs": ["dist/**"]
}
}
}
```
## Common Commands
```bash
# 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
```bash
# 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:
```bash
# 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:
```bash
# 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:
```json
{
"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:
```ts
// 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:
```json
{
"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
- [Vercel Microfrontends](https://vercel.com/docs/microfrontends)
- [Next.js Multi-Zones](https://nextjs.org/docs/app/building-your-application/deploying/multi-zones)
## 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.
```bash
# 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](https://github.com/vercel/turborepo/issues/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
- [Turborepo Documentation](https://turborepo.dev/repo/docs)
- [Getting Started](https://turborepo.dev/repo/docs/getting-started)
- [Crafting Your Repository](https://turborepo.dev/repo/docs/crafting-your-repository)
- [Task Configuration](https://turborepo.dev/repo/docs/reference/configuration)
- [Filtering](https://turborepo.dev/repo/docs/crafting-your-repository/running-tasks#using-filters)
- [GitHub: Turborepo](https://github.com/vercel/turborepo)