Guide for implementing Turborepo - a high-performance build system for JavaScript and TypeScript monorepos. Use when setting up monorepos, optimizing build performance, implementing task pipelines, configuring caching strategies, or orchestrating tasks across multiple packages.
71
71%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Optimize this skill with Tessl
npx tessl skill review --optimize ./claude/skills/turborepo/SKILL.mdTurborepo is a high-performance build system optimized for JavaScript and TypeScript monorepos, written in Rust. It provides intelligent caching, task orchestration, and remote execution capabilities to dramatically speed up development workflows.
https://turborepo.com/llms.txt
Use this skill when:
Turborepo organizes code into packages within a single repository:
Tasks are organized in a dependency graph:
Turborepo caches task outputs based on inputs:
Define what gets cached:
# Requires Node.js 18+ and a package manager
node --version # v18.0.0+# npm
npm install turbo --global
# yarn
yarn global add turbo
# pnpm
pnpm add turbo --global
# bun
bun add turbo --global# npm
npm install turbo --save-dev
# yarn
yarn add turbo --dev
# pnpm
pnpm add turbo --save-dev
# bun
bun add turbo --devUsing official examples:
npx create-turbo@latestInteractive prompts will ask:
1. Initialize workspace:
// package.json (root)
{
"name": "my-turborepo",
"private": true,
"workspaces": ["apps/*", "packages/*"],
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"test": "turbo run test",
"lint": "turbo run lint"
},
"devDependencies": {
"turbo": "latest"
}
}2. Create directory structure:
my-turborepo/
├── apps/
│ ├── web/ # Next.js app
│ └── docs/ # Documentation site
├── packages/
│ ├── ui/ # Shared UI components
│ ├── config/ # Shared configs (ESLint, TS)
│ └── tsconfig/ # Shared TypeScript configs
├── turbo.json # Turborepo configuration
└── package.json # Root package.json3. Create turbo.json:
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**", "dist/**"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {},
"test": {
"dependsOn": ["build"]
}
}
}{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": [".env", "tsconfig.json"],
"globalEnv": ["NODE_ENV"],
"pipeline": {
// Task definitions
}
}Task with dependencies:
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"],
"env": ["NODE_ENV", "API_URL"]
}
}
}Key properties:
dependsOn: Tasks to run first
["^build"]: Run dependencies' build first["build"]: Run own build first["^build", "lint"]: Run deps' build and own lintoutputs: Files/directories to cacheinputs: Override input detection (default: all tracked files)cache: Enable/disable caching (default: true)env: Environment variables that affect outputpersistent: Keep task running (for dev servers)outputMode: Control output displayTopological (^):
{
"build": {
"dependsOn": ["^build"] // Run dependencies' build first
}
}Regular:
{
"deploy": {
"dependsOn": ["build", "test"] // Run own build and test first
}
}Combined:
{
"test": {
"dependsOn": ["^build", "lint"] // Deps' build, then own lint
}
}{
"pipeline": {
"build": {
"outputMode": "full" // Show all output
},
"dev": {
"outputMode": "hash-only" // Show cache hash only
},
"test": {
"outputMode": "new-only" // Show new output only
},
"lint": {
"outputMode": "errors-only" // Show errors only
}
}
}Global environment variables:
{
"globalEnv": ["NODE_ENV", "CI"],
"globalDependencies": [".env", ".env.local"]
}Per-task environment variables:
{
"pipeline": {
"build": {
"env": ["NEXT_PUBLIC_API_URL", "DATABASE_URL"],
"passThroughEnv": ["CUSTOM_VAR"] // Pass without hashing
}
}
}Run tasks across packages:
# Run build in all packages
turbo run build
# Run multiple tasks
turbo run build test lint
# Run in specific packages
turbo run build --filter=web
turbo run build --filter=@myorg/ui
# Run in packages matching pattern
turbo run build --filter='./apps/*'
# Force execution (skip cache)
turbo run build --force
# Run from specific directory
turbo run build --filter='[./apps/web]'
# Run with dependencies
turbo run build --filter='...^web'
# Parallel execution control
turbo run build --concurrency=3
turbo run build --concurrency=50%
# Continue on error
turbo run test --continue
# Dry run
turbo run build --dry-run
# Output control
turbo run build --output-logs=new-only
turbo run build --output-logs=hash-only
turbo run build --output-logs=errors-only
turbo run build --output-logs=fullCreate a subset of the monorepo:
# Prune for specific app
turbo prune --scope=web
# Prune with Docker
turbo prune --scope=api --docker
# Output to custom directory
turbo prune --scope=web --out-dir=./deployUse cases:
Generate code in your monorepo:
# Generate new package
turbo gen workspace
# Generate from custom generator
turbo gen my-generator
# List available generators
turbo gen --listLink local repo to remote cache:
# Link to Vercel
turbo link
# Unlink
turbo unlinkAuthenticate with Vercel:
turbo loginList packages in monorepo:
# List all packages
turbo ls
# JSON output
turbo ls --json# Single package
turbo run build --filter=web
# Multiple packages
turbo run build --filter=web --filter=api
# Scoped package
turbo run build --filter=@myorg/ui# All apps
turbo run build --filter='./apps/*'
# Pattern matching
turbo run build --filter='*-ui'# From specific directory
turbo run build --filter='[./apps/web]'# Changed since main
turbo run build --filter='[main]'
# Changed since HEAD~1
turbo run build --filter='[HEAD~1]'
# Changed in working directory
turbo run test --filter='...[HEAD]'# Package and its dependencies
turbo run build --filter='...web'
# Package's dependencies only
turbo run build --filter='...^web'
# Package and its dependents
turbo run test --filter='ui...'
# Package's dependents only
turbo run test --filter='^ui...'Enabled by default, stores in ./node_modules/.cache/turbo
Cache behavior:
{
"pipeline": {
"build": {
"outputs": ["dist/**"], // Cache dist directory
"cache": true // Enable caching (default)
},
"dev": {
"cache": false // Disable for dev servers
}
}
}Clear cache:
# Clear Turbo cache
rm -rf ./node_modules/.cache/turbo
# Or use turbo command
turbo run build --force # Skip cache for this runShare cache across team and CI:
1. Link to Vercel (recommended):
turbo login
turbo link2. Custom remote cache:
// .turbo/config.json
{
"teamid": "team_123",
"apiurl": "https://cache.example.com",
"token": "your-token"
}Benefits:
Cache is invalidated when:
Control inputs:
{
"pipeline": {
"build": {
"inputs": ["src/**/*.ts", "!src/**/*.test.ts"],
"env": ["NODE_ENV"]
}
}
}1. Internal packages (packages/*):
// packages/ui/package.json
{
"name": "@myorg/ui",
"version": "0.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"lint": "eslint ."
}
}2. Applications (apps/*):
// apps/web/package.json
{
"name": "web",
"version": "1.0.0",
"private": true,
"dependencies": {
"@myorg/ui": "*",
"next": "latest"
},
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}Workspace protocol (pnpm/yarn):
{
"dependencies": {
"@myorg/ui": "workspace:*"
}
}Version protocol (npm):
{
"dependencies": {
"@myorg/ui": "*"
}
}ESLint config package:
// packages/eslint-config/index.js
module.exports = {
extends: ["next", "prettier"],
rules: {
// shared rules
}
}TypeScript config package:
// packages/tsconfig/base.json
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}Usage:
// apps/web/tsconfig.json
{
"extends": "@myorg/tsconfig/base.json",
"compilerOptions": {
"jsx": "preserve"
}
}name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- name: Install dependencies
run: npm install
- name: Build
run: npx turbo run build
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
- name: Test
run: npx turbo run testimage: node:18
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .turbo/
build:
stage: build
script:
- npm install
- npx turbo run build
variables:
TURBO_TOKEN: $TURBO_TOKEN
TURBO_TEAM: $TURBO_TEAMFROM node:18-alpine AS base
# Prune workspace
FROM base AS builder
RUN npm install -g turbo
COPY . .
RUN turbo prune --scope=web --docker
# Install dependencies
FROM base AS installer
COPY --from=builder /app/out/json/ .
COPY --from=builder /app/out/package-lock.json ./package-lock.json
RUN npm install
# Build
COPY --from=builder /app/out/full/ .
RUN npx turbo run build --filter=web
# Runner
FROM base AS runner
COPY --from=installer /app/apps/web/.next/standalone ./
COPY --from=installer /app/apps/web/.next/static ./apps/web/.next/static
COPY --from=installer /app/apps/web/public ./apps/web/public
CMD node apps/web/server.jsenv:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}- uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}turbo run build test --filter='...[origin/main]'// apps/web/package.json
{
"name": "web",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"next": "latest",
"react": "latest"
}
}turbo.json:
{
"pipeline": {
"build": {
"outputs": [".next/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}{
"pipeline": {
"build": {
"outputs": [".output/**", ".nuxt/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", "*.tsbuildinfo"]
},
"typecheck": {
"dependsOn": ["^build"]
}
}
}{
"pipeline": {
"lint": {
"dependsOn": ["^build"],
"outputs": []
}
}
}{
"pipeline": {
"test": {
"dependsOn": ["build"],
"outputs": ["coverage/**"],
"cache": true
}
}
}{
"pipeline": {
"db:generate": {
"cache": false
},
"db:push": {
"cache": false
}
}
}my-monorepo/
├── apps/ # Applications
│ ├── web/ # Frontend app
│ ├── api/ # Backend API
│ └── docs/ # Documentation
├── packages/ # Shared packages
│ ├── ui/ # UI components
│ ├── config/ # Shared configs
│ ├── utils/ # Utilities
│ └── tsconfig/ # TS configs
├── tooling/ # Development tools
│ ├── eslint-config/
│ └── prettier-config/
└── turbo.json{
"pipeline": {
"build": {
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["build"]
},
"lint": {
"dependsOn": ["^build"]
},
"deploy": {
"dependsOn": ["build", "test", "lint"]
}
}
}.next/cache){
"pipeline": {
"build": {
"outputs": [
"dist/**",
".next/**",
"!.next/cache/**",
"storybook-static/**"
]
},
"dev": {
"cache": false,
"persistent": true
}
}
}{
"globalEnv": ["NODE_ENV", "CI"],
"pipeline": {
"build": {
"env": ["NEXT_PUBLIC_API_URL"],
"passThroughEnv": ["DEBUG"] // Don't affect cache
}
}
}# Build only changed packages
turbo run build --filter='...[origin/main]'
# Build specific app with dependencies
turbo run build --filter='...web'
# Test only affected packages
turbo run test --filter='...[HEAD^1]'Root package.json:
{
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "turbo run lint",
"test": "turbo run test",
"clean": "turbo run clean && rm -rf node_modules"
}
}{
"pipeline": {
"dev": {
"cache": false,
"persistent": true // Keeps running
}
}
}apps/
├── web/ # Next.js frontend
│ └── package.json
├── api/ # Express backend
│ └── package.json
└── mobile/ # React Native
└── package.json
packages/
├── ui/ # Shared UI components
├── database/ # Database client/migrations
├── types/ # Shared TypeScript types
└── config/ # Shared configspackages/
├── ui/ # Component library
│ ├── src/
│ ├── package.json
│ └── tsconfig.json
└── ui-docs/ # Storybook
├── .storybook/
├── stories/
└── package.jsonapps/
├── shell/ # Container app
├── dashboard/ # Dashboard MFE
└── settings/ # Settings MFE
packages/
├── shared-ui/ # Shared components
└── router/ # Routing logicProblem: Task not using cache when it should
# Check what's causing cache miss
turbo run build --dry-run=json
# Force rebuild
turbo run build --force
# Clear cache
rm -rf ./node_modules/.cache/turboProblem: Cache too large
# Limit cache size in turbo.json
{
"cacheDir": ".turbo",
"cacheSize": "50gb"
}Problem: Internal package not found
# Ensure workspace is set up correctly
npm install
# Check package names match
npm ls @myorg/ui
# Rebuild dependencies
turbo run build --filter='...web'Problem: Tasks running in wrong order
dependsOn configuration^task for dependency tasksProblem: Dev server not starting
{
"pipeline": {
"dev": {
"cache": false,
"persistent": true // Add this
}
}
}Problem: Builds taking too long
# Run with concurrency limit
turbo run build --concurrency=2
# Use filters to build less
turbo run build --filter='...[origin/main]'
# Check for unnecessary dependencies
turbo run build --dry-runProblem: Remote cache not working
# Verify authentication
turbo link
# Check environment variables
echo $TURBO_TOKEN
echo $TURBO_TEAM
# Test connection
turbo run build --output-logs=hash-onlynpm uninstall lerna
npm install turbo --save-dev{
"pipeline": {
"build": {
"dependsOn": ["^build"]
}
}
}{
"scripts": {
"build": "turbo run build",
"test": "turbo run test"
}
}npm install turbo --save-devWhen setting up Turborepo:
b1b2fe0
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.