Files
2025-11-30 08:48:52 +08:00

9.5 KiB

Turborepo Setup & Configuration

Installation, workspace configuration, and project structure for monorepos.

Installation

Create New Monorepo

Using official starter:

npx create-turbo@latest my-monorepo
cd my-monorepo

Interactive prompts:

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

Manual Installation

Install in existing project:

# npm
npm install turbo --save-dev

# yarn
yarn add turbo --dev

# pnpm
pnpm add turbo --save-dev

# bun
bun add turbo --dev

Workspace Configuration

Package Manager Setup

pnpm (recommended):

# pnpm-workspace.yaml
packages:
  - 'apps/*'
  - 'packages/*'

npm/yarn:

// package.json (root)
{
  "name": "my-monorepo",
  "private": true,
  "workspaces": [
    "apps/*",
    "packages/*"
  ]
}

Root Package.json

{
  "name": "my-monorepo",
  "private": true,
  "workspaces": ["apps/*", "packages/*"],
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "lint": "turbo run lint",
    "test": "turbo run test",
    "clean": "turbo run clean"
  },
  "devDependencies": {
    "turbo": "latest",
    "typescript": "^5.0.0"
  },
  "packageManager": "pnpm@8.0.0"
}

Project Structure

my-monorepo/
├── apps/                    # Applications
│   ├── web/                # Next.js web app
│   │   ├── app/
│   │   ├── package.json
│   │   └── next.config.js
│   ├── docs/               # Documentation site
│   │   ├── app/
│   │   └── package.json
│   └── api/                # Backend API
│       ├── src/
│       └── package.json
├── packages/               # Shared packages
│   ├── ui/                 # UI component library
│   │   ├── src/
│   │   ├── package.json
│   │   └── tsconfig.json
│   ├── config/             # Shared configs
│   │   ├── eslint/
│   │   └── typescript/
│   ├── utils/              # Utility functions
│   │   ├── src/
│   │   └── package.json
│   └── types/              # Shared TypeScript types
│       ├── src/
│       └── package.json
├── turbo.json              # Turborepo config
├── package.json            # Root package.json
├── pnpm-workspace.yaml     # Workspace config (pnpm)
└── .gitignore

Application Package Setup

Next.js App

// apps/web/package.json
{
  "name": "web",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@repo/ui": "*",
    "@repo/utils": "*",
    "next": "latest",
    "react": "latest",
    "react-dom": "latest"
  },
  "devDependencies": {
    "@repo/typescript-config": "*",
    "@repo/eslint-config": "*",
    "typescript": "^5.0.0"
  }
}

Backend API App

// apps/api/package.json
{
  "name": "api",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "build": "tsup src/index.ts",
    "start": "node dist/index.js",
    "lint": "eslint src/"
  },
  "dependencies": {
    "@repo/utils": "*",
    "@repo/types": "*",
    "express": "^4.18.0"
  },
  "devDependencies": {
    "@repo/typescript-config": "*",
    "@types/express": "^4.17.0",
    "tsx": "^4.0.0",
    "tsup": "^8.0.0"
  }
}

Shared Package Setup

UI Component Library

// packages/ui/package.json
{
  "name": "@repo/ui",
  "version": "0.0.0",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    },
    "./button": {
      "types": "./dist/button.d.ts",
      "default": "./dist/button.js"
    }
  },
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch",
    "lint": "eslint src/",
    "clean": "rm -rf dist"
  },
  "dependencies": {
    "react": "latest"
  },
  "devDependencies": {
    "@repo/typescript-config": "*",
    "typescript": "^5.0.0"
  }
}
// packages/ui/tsconfig.json
{
  "extends": "@repo/typescript-config/react-library.json",
  "compilerOptions": {
    "outDir": "dist",
    "declarationDir": "dist"
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}

Utility Library

// packages/utils/package.json
{
  "name": "@repo/utils",
  "version": "0.0.0",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch",
    "test": "jest"
  },
  "devDependencies": {
    "@repo/typescript-config": "*",
    "jest": "^29.0.0",
    "typescript": "^5.0.0"
  }
}

Shared Configuration Packages

TypeScript Config Package

packages/typescript-config/
├── base.json
├── nextjs.json
├── react-library.json
└── package.json
// packages/typescript-config/package.json
{
  "name": "@repo/typescript-config",
  "version": "0.0.0",
  "main": "base.json",
  "files": [
    "base.json",
    "nextjs.json",
    "react-library.json"
  ]
}
// packages/typescript-config/base.json
{
  "compilerOptions": {
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "moduleResolution": "bundler",
    "target": "ES2020",
    "module": "ESNext"
  },
  "exclude": ["node_modules"]
}
// packages/typescript-config/nextjs.json
{
  "extends": "./base.json",
  "compilerOptions": {
    "jsx": "preserve",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "noEmit": true,
    "incremental": true,
    "plugins": [{ "name": "next" }]
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}

ESLint Config Package

packages/eslint-config/
├── library.js
├── next.js
└── package.json
// packages/eslint-config/package.json
{
  "name": "@repo/eslint-config",
  "version": "0.0.0",
  "main": "library.js",
  "files": ["library.js", "next.js"],
  "dependencies": {
    "eslint-config-next": "latest",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-react": "latest"
  }
}
// packages/eslint-config/library.js
module.exports = {
  extends: ['eslint:recommended', 'prettier'],
  env: {
    node: true,
    es2020: true,
  },
  parserOptions: {
    ecmaVersion: 2020,
    sourceType: 'module',
  },
  rules: {
    'no-console': 'warn',
  },
}
// packages/eslint-config/next.js
module.exports = {
  extends: ['next', 'prettier'],
  rules: {
    '@next/next/no-html-link-for-pages': 'off',
  },
}

Dependency Management

Internal Dependencies

Use workspace protocol:

pnpm:

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

npm/yarn:

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

Version Syncing

Keep dependencies in sync across packages:

// Root package.json
{
  "devDependencies": {
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "typescript": "5.0.0"
  }
}

Packages inherit from root or specify versions explicitly.

Turbo.json Configuration

Basic configuration file:

{
  "$schema": "https://turbo.build/schema.json",
  "globalDependencies": [
    "**/.env.*local",
    "tsconfig.json"
  ],
  "globalEnv": [
    "NODE_ENV"
  ],
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**", "dist/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    },
    "lint": {
      "dependsOn": ["^build"]
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"]
    },
    "clean": {
      "cache": false
    }
  }
}

Environment Variables

Global Environment Variables

// turbo.json
{
  "globalEnv": [
    "NODE_ENV",
    "CI"
  ]
}

Package-Specific Environment Variables

{
  "pipeline": {
    "build": {
      "env": ["NEXT_PUBLIC_API_URL", "DATABASE_URL"],
      "passThroughEnv": ["CUSTOM_VAR"]
    }
  }
}

.env Files

my-monorepo/
├── .env                    # Global env vars
├── .env.local             # Local overrides (gitignored)
├── apps/
│   └── web/
│       ├── .env           # App-specific
│       └── .env.local     # Local overrides

Gitignore Configuration

# Dependencies
node_modules/
.pnp
.pnp.js

# Turbo
.turbo

# Build outputs
dist/
.next/
out/
build/

# Environment
.env.local
.env.*.local

# Testing
coverage/

# Misc
.DS_Store
*.log

NPM Scripts

Common scripts in root package.json:

{
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "lint": "turbo run lint",
    "test": "turbo run test",
    "format": "prettier --write \"**/*.{ts,tsx,md}\"",
    "clean": "turbo run clean && rm -rf node_modules",
    "typecheck": "turbo run typecheck"
  }
}

Initialization Checklist

Setting up new Turborepo:

  • Install Turborepo (create-turbo or manual)
  • Configure workspace (pnpm-workspace.yaml or package.json)
  • Create directory structure (apps/, packages/)
  • Set up shared config packages (typescript-config, eslint-config)
  • Create turbo.json with pipeline
  • Configure gitignore
  • Set up environment variables
  • Define package dependencies
  • Add root scripts
  • Test build and dev commands