turborepo

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.

$ Installieren

git clone https://github.com/einverne/dotfiles /tmp/dotfiles && cp -r /tmp/dotfiles/claude/skills/turborepo ~/.claude/skills/dotfiles

// tip: Run this command in your terminal to install the skill


name: turborepo description: 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. license: MIT version: 1.0.0

Turborepo Skill

Turborepo 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.

Reference

https://turborepo.com/llms.txt

When to Use This Skill

Use this skill when:

  • Setting up a new monorepo with multiple packages
  • Optimizing build performance in existing monorepos
  • Implementing task pipelines across packages
  • Configuring intelligent caching strategies
  • Setting up remote caching for teams
  • Orchestrating tasks with dependency awareness
  • Integrating monorepo with CI/CD pipelines
  • Migrating from Lerna, Nx, or other monorepo tools
  • Building microfrontends or shared libraries
  • Managing workspace dependencies

Core Concepts

1. Monorepo Architecture

Turborepo organizes code into packages within a single repository:

  • Root Package: Contains workspace configuration
  • Internal Packages: Shared libraries, utilities, configs
  • Applications: Frontend apps, backend services, etc.
  • Workspaces: npm/yarn/pnpm workspace configuration

2. Task Pipeline

Tasks are organized in a dependency graph:

  • Task Dependencies: Define execution order (build before test)
  • Package Dependencies: Respect internal package relationships
  • Parallel Execution: Run independent tasks simultaneously
  • Topological Ordering: Execute tasks in correct dependency order

3. Intelligent Caching

Turborepo caches task outputs based on inputs:

  • Local Cache: Stores outputs on local machine
  • Remote Cache: Shares cache across team/CI (Vercel or custom)
  • Content-Based Hashing: Only re-run when inputs change
  • Cache Restoration: Instant task completion from cache

4. Task Outputs

Define what gets cached:

  • Build artifacts (dist/, build/)
  • Test results
  • Generated files
  • Type definitions

Installation

Prerequisites

# Requires Node.js 18+ and a package manager
node --version  # v18.0.0+

Global Installation

# npm
npm install turbo --global

# yarn
yarn global add turbo

# pnpm
pnpm add turbo --global

# bun
bun add turbo --global

Per-Project Installation

# npm
npm install turbo --save-dev

# yarn
yarn add turbo --dev

# pnpm
pnpm add turbo --save-dev

# bun
bun add turbo --dev

Project Setup

Create New Monorepo

Using official examples:

npx create-turbo@latest

Interactive prompts will ask:

  • Project name
  • Package manager (npm/yarn/pnpm/bun)
  • Example template selection

Manual Setup

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.json

3. 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"]
    }
  }
}

Configuration (turbo.json)

Basic Structure

{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": [".env", "tsconfig.json"],
  "globalEnv": ["NODE_ENV"],
  "pipeline": {
    // Task definitions
  }
}

Pipeline Configuration

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 lint
  • outputs: Files/directories to cache
  • inputs: Override input detection (default: all tracked files)
  • cache: Enable/disable caching (default: true)
  • env: Environment variables that affect output
  • persistent: Keep task running (for dev servers)
  • outputMode: Control output display

Task Dependency Patterns

Topological (^):

{
  "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
  }
}

Output Modes

{
  "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
    }
  }
}

Environment Variables

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
    }
  }
}

Commands

turbo run

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=full

turbo prune

Create 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=./deploy

Use cases:

  • Docker builds (only include necessary packages)
  • Deploy specific apps
  • Reduce CI/CD context size

turbo gen

Generate code in your monorepo:

# Generate new package
turbo gen workspace

# Generate from custom generator
turbo gen my-generator

# List available generators
turbo gen --list

turbo link

Link local repo to remote cache:

# Link to Vercel
turbo link

# Unlink
turbo unlink

turbo login

Authenticate with Vercel:

turbo login

turbo ls

List packages in monorepo:

# List all packages
turbo ls

# JSON output
turbo ls --json

Filtering

Filter by Package Name

# Single package
turbo run build --filter=web

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

# Scoped package
turbo run build --filter=@myorg/ui

Filter by Pattern

# All apps
turbo run build --filter='./apps/*'

# Pattern matching
turbo run build --filter='*-ui'

Filter by Directory

# From specific directory
turbo run build --filter='[./apps/web]'

Filter by Git

# 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]'

Filter by Dependencies

# 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...'

Caching Strategies

Local Caching

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 run

Remote Caching

Share cache across team and CI:

1. Link to Vercel (recommended):

turbo login
turbo link

2. Custom remote cache:

// .turbo/config.json
{
  "teamid": "team_123",
  "apiurl": "https://cache.example.com",
  "token": "your-token"
}

Benefits:

  • Share builds across team
  • Speed up CI/CD
  • Consistent builds
  • Reduce compute costs

Cache Signatures

Cache is invalidated when:

  • Source files change
  • Dependencies change
  • Environment variables change (if specified)
  • Global dependencies change
  • Task configuration changes

Control inputs:

{
  "pipeline": {
    "build": {
      "inputs": ["src/**/*.ts", "!src/**/*.test.ts"],
      "env": ["NODE_ENV"]
    }
  }
}

Workspace Patterns

Package Types

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"
  }
}

Dependency Management

Workspace protocol (pnpm/yarn):

{
  "dependencies": {
    "@myorg/ui": "workspace:*"
  }
}

Version protocol (npm):

{
  "dependencies": {
    "@myorg/ui": "*"
  }
}

Shared Configuration

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"
  }
}

CI/CD Integration

GitHub Actions

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 test

GitLab CI

image: 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_TEAM

Docker

FROM 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.js

Optimization Tips

  1. Use remote caching in CI:
env:
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
  TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
  1. Cache node_modules:
- uses: actions/cache@v3
  with:
    path: node_modules
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
  1. Run only affected tasks:
turbo run build test --filter='...[origin/main]'

Framework Integration

Next.js

// 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
    }
  }
}

Vite

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

NuxtJS

{
  "pipeline": {
    "build": {
      "outputs": [".output/**", ".nuxt/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

Development Tools Integration

TypeScript

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", "*.tsbuildinfo"]
    },
    "typecheck": {
      "dependsOn": ["^build"]
    }
  }
}

ESLint

{
  "pipeline": {
    "lint": {
      "dependsOn": ["^build"],
      "outputs": []
    }
  }
}

Jest / Vitest

{
  "pipeline": {
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"],
      "cache": true
    }
  }
}

Prisma

{
  "pipeline": {
    "db:generate": {
      "cache": false
    },
    "db:push": {
      "cache": false
    }
  }
}

Best Practices

1. Structure Your Monorepo

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

2. Define Clear Task Dependencies

{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"]
    },
    "test": {
      "dependsOn": ["build"]
    },
    "lint": {
      "dependsOn": ["^build"]
    },
    "deploy": {
      "dependsOn": ["build", "test", "lint"]
    }
  }
}

3. Optimize Cache Configuration

  • Cache build outputs, not source files
  • Include all generated files in outputs
  • Exclude cache directories (e.g., .next/cache)
  • Disable cache for dev servers
{
  "pipeline": {
    "build": {
      "outputs": [
        "dist/**",
        ".next/**",
        "!.next/cache/**",
        "storybook-static/**"
      ]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

4. Use Environment Variables Wisely

{
  "globalEnv": ["NODE_ENV", "CI"],
  "pipeline": {
    "build": {
      "env": ["NEXT_PUBLIC_API_URL"],
      "passThroughEnv": ["DEBUG"]  // Don't affect cache
    }
  }
}

5. Leverage Remote Caching

  • Enable for all team members
  • Configure in CI/CD
  • Reduces build times significantly
  • Especially beneficial for large teams

6. Use Filters Effectively

# 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]'

7. Organize Scripts Consistently

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"
  }
}

8. Handle Persistent Tasks

{
  "pipeline": {
    "dev": {
      "cache": false,
      "persistent": true  // Keeps running
    }
  }
}

Common Patterns

Full-Stack Application

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 configs

Shared Component Library

packages/
├── ui/                    # Component library
│   ├── src/
│   ├── package.json
│   └── tsconfig.json
└── ui-docs/              # Storybook
    ├── .storybook/
    ├── stories/
    └── package.json

Microfrontends

apps/
├── shell/        # Container app
├── dashboard/    # Dashboard MFE
└── settings/     # Settings MFE

packages/
├── shared-ui/    # Shared components
└── router/       # Routing logic

Troubleshooting

Cache Issues

Problem: 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/turbo

Problem: Cache too large

# Limit cache size in turbo.json
{
  "cacheDir": ".turbo",
  "cacheSize": "50gb"
}

Dependency Issues

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'

Task Execution Issues

Problem: Tasks running in wrong order

  • Check dependsOn configuration
  • Use ^task for dependency tasks
  • Verify task names match package.json scripts

Problem: Dev server not starting

{
  "pipeline": {
    "dev": {
      "cache": false,
      "persistent": true  // Add this
    }
  }
}

Performance Issues

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-run

Problem: 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-only

Migration Guide

From Lerna

  1. Replace Lerna with Turborepo:
npm uninstall lerna
npm install turbo --save-dev
  1. Convert lerna.json to turbo.json:
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"]
    }
  }
}
  1. Update scripts:
{
  "scripts": {
    "build": "turbo run build",
    "test": "turbo run test"
  }
}

From Nx

  1. Install Turborepo:
npm install turbo --save-dev
  1. Convert nx.json to turbo.json:
  • Map targetDefaults to pipeline
  • Convert dependsOn syntax
  • Configure caching
  1. Update workspace configuration
  2. Migrate CI/CD scripts

Resources

Implementation Checklist

When setting up Turborepo:

  • Install Turborepo globally or per-project
  • Set up workspace structure (apps/, packages/)
  • Create turbo.json with pipeline configuration
  • Define task dependencies (build, test, lint)
  • Configure cache outputs for each task
  • Set up global dependencies and environment variables
  • Link to remote cache (Vercel or custom)
  • Configure CI/CD integration
  • Add filtering strategies for large repos
  • Document monorepo structure for team
  • Set up code generation (turbo gen)
  • Configure Docker builds with turbo prune
  • Test caching behavior locally
  • Verify remote cache in CI
  • Optimize concurrency settings