Guide for implementing Turborepo - a high-performance build system for JavaScript and TypeScript monorepos...
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.
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 --dev
Using official examples:
npx create-turbo@latest
Interactive 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.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"]
}
}
}
{
"$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=full
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:
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
Link local repo to remote cache:
# Link to Vercel
turbo link
# Unlink
turbo unlink
Authenticate with Vercel:
turbo login
List 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 run
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:
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 test
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
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
env:
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 configs
packages/
āāā ui/ # Component library
ā āāā src/
ā āāā package.json
ā āāā tsconfig.json
āāā ui-docs/ # Storybook
āāā .storybook/
āāā stories/
āāā package.json
apps/
āāā shell/ # Container app
āāā dashboard/ # Dashboard MFE
āāā settings/ # Settings MFE
packages/
āāā shared-ui/ # Shared components
āāā router/ # Routing logic
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"
}
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-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
npm uninstall lerna
npm install turbo --save-dev
{
"pipeline": {
"build": {
"dependsOn": ["^build"]
}
}
}
{
"scripts": {
"build": "turbo run build",
"test": "turbo run test"
}
}
npm install turbo --save-dev
When setting up Turborepo: