
Turborepo
- 67 installs
- 14 repo stars
- Updated March 2, 2026
- oakoss/agent-skills
Helps with ai & agent building tasks during AI-assisted development.
About
turborepo is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- turborepo
- AI & Agent Building
- AI-coding skill
Turborepo by the numbers
- 67 all-time installs (skills.sh)
- +2 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #5,935 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/oakoss/agent-skills --skill turborepoAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 67 |
|---|---|
| repo stars | ★ 14 |
| Last updated | March 2, 2026 |
| Repository | oakoss/agent-skills ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Turborepo
Overview
Build system for JavaScript/TypeScript monorepos. Caches task outputs and runs tasks in parallel based on dependency graph. Always create package tasks (not root tasks), use turbo run in scripts, and let dependsOn manage execution order. Configuration uses turbo.json (or turbo.jsonc for comments).
When to use: Monorepo task orchestration, build caching, CI optimization, workspace dependency management, package boundary enforcement.
When NOT to use: Single-package projects, non-JavaScript monorepos, projects without build steps.
Quick Reference
| Pattern | Syntax | Key Points |
|---|---|---|
| Schema | "$schema": "https://turborepo.dev/schema.json" | Always include in turbo.json |
| Dependency build | "dependsOn": ["^build"] | Build dependencies first |
| Same-package task | "dependsOn": ["codegen"] | Run in same package first |
| Specific package | "dependsOn": ["pkg#task"] | Named package's task |
| Parallel lint/typecheck | Transit Nodes pattern | Cache invalidation without sequential execution |
| Dev server | "persistent": true, "cache": false | Long-running, non-cacheable |
| Sidecar tasks | "with": ["api#dev"] | Run tasks concurrently alongside |
| Watch mode | turbo watch dev | Re-run on file changes |
| Filter by package | --filter=web | Single package |
| Filter with deps | --filter=web... | Package + dependencies |
| Changed packages | --affected | Changed + dependents |
| Debug cache | --summarize or --dry | See hash inputs |
| Package config | turbo.json with "extends": ["//"] | Per-package overrides |
| Composable config | "extends": ["@repo/config"] | Extend from any workspace package |
| Extend arrays | "$TURBO_EXTENDS$" in arrays | Append to inherited config instead of replacing |
| Env vars in hash | "env": ["API_URL"] | Cache invalidation on change |
| Boundaries | turbo boundaries | Enforce package isolation and import rules |
| Query graph | turbo query | GraphQL interface to package/task graphs |
| Code generation | turbo generate | Scaffold new packages and components |
| List packages | turbo ls | List all packages in monorepo |
| Devtools | turbo devtools | Visual Package Graph and Task Graph explorer |
| Docker pruned workspace | turbo prune <pkg> --docker | Minimal monorepo slice for container builds |
Decision Trees
Configure a Task
Configure a task?
+-- Define task dependencies -> dependsOn in turbo.json
+-- Lint/check-types (parallel) -> Transit Nodes pattern
+-- Specify build outputs -> outputs key
+-- Handle environment variables -> env key or globalEnv
+-- Dev/watch tasks -> persistent: true, cache: false
+-- Sidecar tasks (run alongside) -> with key
+-- Package-specific config -> Package turbo.json with extends: ["//"]
+-- Composable config -> extends from any workspace package
+-- Global settings -> globalEnv, globalDependencies, cacheDirCache Problems
Cache problems?
+-- Outputs not restored -> Missing outputs key
+-- Unexpected cache misses -> Use --summarize or --dry to debug
+-- Skip cache entirely -> --force or cache: false
+-- Remote cache not working -> Check turbo login/link
+-- Environment causing misses -> Var not in env keyFilter Packages
Filter packages?
+-- By package name -> --filter=web
+-- By directory -> --filter=./apps/*
+-- Package + dependencies -> --filter=web...
+-- Package + dependents -> --filter=...web
+-- Changed + dependents -> --affectedExplore Repository
Explore repository?
+-- List all packages -> turbo ls
+-- Query dependency graph -> turbo query
+-- Visualize graphs -> turbo devtools
+-- Check boundary violations -> turbo boundaries
+-- Generate new package -> turbo generate workspace
+-- Run custom generator -> turbo generate run [name]Common Mistakes
| Mistake | Correct Pattern |
|---|---|
Putting build logic in root package.json scripts instead of per-package | Define scripts in each package and use turbo run in root to delegate |
Using ^build without declaring workspace:* dependency | Add the dependency in package.json first; ^build only triggers for declared dependencies |
Chaining turbo tasks with && in package scripts | Use dependsOn in turbo.json to declare task ordering |
Not adding environment variables to the env key in turbo.json | Declare all build-affecting env vars in env so cache hashes correctly |
Using --parallel flag to bypass dependency ordering | Configure dependsOn correctly or use transit nodes for parallel tasks with proper cache invalidation |
| Using outdated schema URL | Use https://turborepo.dev/schema.json in $schema field |
| Overriding inherited arrays in package configs | Use $TURBO_EXTENDS$ in arrays to append instead of replace |
Defining tasks in root turbo.json that belong to a specific package | Define tasks inside the package's own turbo.json with extends: ["//"] |
Using turbo <task> shorthand in scripts or CI | Use turbo run <task> in package.json scripts and CI pipelines; shorthand is for interactive use only |
Delegation
- Monorepo structure exploration: Use
Exploreagent to discover packages, workspace layout, and dependency relationships - Pipeline configuration and optimization: Use
Taskagent to set up turbo.json tasks, configure caching, and debug cache misses - Monorepo architecture planning: Use
Planagent to design package boundaries, shared libraries, and CI optimization strategy
If thepnpm-workspaceskill is available, delegate workspace setup, dependency linking, catalogs, andpnpm deployto it.
If the changesets skill is available, delegate versioning, changelog generation, and npm publishing to it.References
- Task configuration and dependsOn patterns
- Caching, outputs, and debugging cache issues
- Filtering, affected packages, and CI patterns
- Workspace structure and package management
- Environment variables and modes
- Watch mode, dev tasks, and anti-patterns
- Boundaries, query, and code generation
Boundaries, Query, and Code Generation
Boundaries
Enforce package isolation rules to catch import violations and undeclared dependencies.
Enable Boundaries
Add to root turbo.json:
{
"boundaries": {}
}Run the check:
turbo boundariesCatches two types of violations:
- Importing a file outside the package's directory
- Importing a package not declared in the package's
package.json
Boundary Tags
Tags allow custom rules for package dependency relationships. Define tags in each package's turbo.json:
{
"tags": ["internal"],
"extends": ["//"],
"tasks": {}
}Define tag rules in root turbo.json:
{
"boundaries": {
"tags": {
"public": {
"dependencies": {
"allow": ["public"]
}
},
"internal": {
"dependents": {
"deny": ["public"]
}
}
}
}
}Tag Rule Options
| Rule | Description |
|---|---|
dependencies.allow | Tags/packages this tagged package can depend on |
dependencies.deny | Tags/packages this tagged package cannot depend on |
dependents.allow | Tags/packages allowed to depend on this tagged package |
dependents.deny | Tags/packages denied from depending on this tagged package |
Rules apply transitively -- violations are caught even through intermediate dependencies.
Common Boundary Patterns
Separate public and internal packages:
{
"boundaries": {
"tags": {
"public": {
"dependencies": {
"deny": ["internal"]
}
}
}
}
}Isolate feature domains:
{
"boundaries": {
"tags": {
"auth": {
"dependencies": {
"allow": ["auth", "shared"]
}
},
"billing": {
"dependencies": {
"allow": ["billing", "shared"]
}
}
}
}
}turbo query
Run GraphQL queries against the repository's package and task graphs:
turbo queryOpens a GraphiQL playground when run without arguments.
Query Examples
Find all packages that depend on a specific package:
turbo query "{ packages(filter: { dependents: { name: { equal: \"@repo/ui\" } } }) { items { name } } }"Query from a file:
turbo query path/to/query.gqlMachine-readable output:
turbo query --output=json "{ packages { items { name path } } }"Use Cases
- Discover dependency relationships between packages
- Find changed packages and their dependents
- Audit task configurations across the monorepo
- Build custom CI pipelines based on graph data
turbo generate
Scaffold new packages and run custom generators.
Create a New Package
turbo generate workspace
turbo generate workspace --name @repo/new-lib --copy @repo/uiOptions:
| Flag | Description |
|---|---|
--name | Name for the new package |
--copy | Source package to copy from |
--empty | Create an empty workspace |
--type | Package type (app or package) |
Custom Generators
Define generators in turbo/generators/config.ts:
import type { PlopTypes } from '@turbo/gen';
export default function generator(plop: PlopTypes.NodePlopAPI): void {
plop.setGenerator('component', {
description: 'Create a new React component',
prompts: [
{
type: 'input',
name: 'name',
message: 'Component name?',
},
],
actions: [
{
type: 'add',
path: 'packages/ui/src/{{kebabCase name}}.tsx',
templateFile: 'turbo/generators/templates/component.hbs',
},
],
});
}Run a custom generator:
turbo generate run component
turbo gen component --args "Button"Install the generator dependency:
pnpm add -D @turbo/genturbo ls
List all packages in the monorepo:
turbo lsShows package names and their directories.
Filter Package List
turbo ls --filter=./packages/*
turbo ls --affectedturbo devtools
Visual explorer for Package Graph and Task Graph:
turbo devtoolsHot-reloads as you make changes to turbo.json or package structure. Useful for debugging dependency relationships and task ordering.
Caching and Debugging
Diagnostic Tools
--summarize
Generates a JSON file with all hash inputs. Compare two runs to find differences.
turbo build --summarize
# Creates .turbo/runs/<run-id>.json
# Compare runs
diff .turbo/runs/<first-run>.json .turbo/runs/<second-run>.json--dry / --dry=json
See what would run without executing:
turbo build --dry
turbo build --dry=json # machine-readable output--force
Skip reading cache, re-execute all tasks:
turbo build --forceUnexpected Cache Misses
Symptom: Task runs when you expected a cache hit.
Checklist:
1. Run with --summarize, compare with previous run 2. Check env vars with --dry=json 3. Look for lockfile/config changes in git 4. Check if environment variables are in env key 5. Check if .env files are in inputs 6. Verify outputs includes all produced files
Common Causes
- Environment variable changed: Different
API_URLbetween runs - .env file changed: Not tracked by default, add to
inputs - Lockfile changed: Installing/updating packages changes global hash
- turbo.json changed: Config changes invalidate global hash
Incorrect Cache Hits
Symptom: Cached output is stale/wrong.
- Missing env var: Task uses a var not listed in
env - Missing file in inputs: Task reads a file outside default inputs
{
"tasks": {
"build": {
"env": ["API_URL"],
"inputs": ["$TURBO_DEFAULT$", ".env", ".env.*"]
}
}
}Remote Caching
Configure remote cache (Vercel or custom S3-based) for fast CI:
- Use
persistent: trueandcache: falsefor dev servers to prevent cache poisoning - Use
--summarizeor--dryto debug cache behavior
GitHub Actions Setup
jobs:
build:
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}Alternative: actions/cache
If you can't use remote cache:
- uses: actions/cache@v5
with:
path: .turbo
key: turbo-${{ runner.os }}-${{ hashFiles('**/turbo.json', '**/package-lock.json') }}
restore-keys: |
turbo-${{ runner.os }}-Useful Flags
# Only show output for cache misses
turbo build --output-logs=new-only
# Show output for everything
turbo build --output-logs=full
# See why tasks are running
turbo build --verbosity=2Watch Mode and Anti-Patterns
Watch Mode
Re-run tasks on file changes:
turbo watch devDev Task Configuration
{
"tasks": {
"dev": {
"dependsOn": ["^dev"],
"cache": false,
"persistent": false
}
}
}Apps override with persistent:
{
"extends": ["//"],
"tasks": {
"dev": { "persistent": true }
}
}with Key for Runtime Dependencies
Run tasks alongside (concurrent, not sequential):
{
"tasks": {
"dev": {
"with": ["api#dev"],
"persistent": true,
"cache": false
}
}
}interruptible Tasks
Allow turbo watch to restart tasks on dependency changes:
{
"tasks": {
"dev": {
"persistent": true,
"interruptible": true,
"cache": false
}
}
}Troubleshooting
Tasks Running Sequentially
1. Check dependsOn -- ^build forces sequential execution 2. For lint/typecheck, use Transit Nodes pattern instead 3. Remove unnecessary dependsOn entries
Build Order Wrong
1. Verify workspace:* dependency is declared in package.json 2. Confirm dependsOn: ["^build"] is set in turbo.json 3. ^build only triggers for declared dependencies
Dev Server Not Picking Up Changes
1. Use turbo watch dev instead of turbo run dev 2. Set interruptible: true on dev tasks that should restart 3. Ensure dependency packages have a dev script
Environment Variables Missing
1. Check if strict mode is filtering them (default behavior) 2. Add to env, globalEnv, or globalPassThroughEnv 3. Framework-specific vars are auto-included via inference
Monorepo Structure Validation
- Use
turbo boundariesto enforce package isolation - Run
turbo run build --dryto verify task graph - Run
turbo queryto explore package and task graphs via GraphQL - Run
turbo lsto list all packages in the monorepo - Use
--filterto test specific packages in isolation
Anti-Patterns
Using --parallel Flag
Bypasses dependency graph. Configure dependsOn correctly or use transit nodes instead.
../ in inputs
// Bad
{ "inputs": ["$TURBO_DEFAULT$", "../shared-config.json"] }
// Good
{ "inputs": ["$TURBO_DEFAULT$", "$TURBO_ROOT$/shared-config.json"] }Too Many Root Dependencies
Root package.json should only have repo tooling (turbo, prettier, etc.). App dependencies belong in each package.
Environment Variables
env Key
Variables listed in env affect cache hits -- changing the value invalidates cache.
{
"tasks": {
"build": {
"env": ["API_URL", "NEXT_PUBLIC_*", "!DEBUG"]
}
}
}Use wildcards (*) for framework-prefixed vars. Use ! to exclude from hash.
globalEnv
Variables that affect all tasks globally:
{
"globalEnv": ["NODE_ENV"],
"globalDependencies": [".env"]
}.env Files in Inputs
Turbo does NOT load .env files (your framework does), but Turbo needs to know about changes:
{
"tasks": {
"build": {
"env": ["API_URL"],
"inputs": ["$TURBO_DEFAULT$", ".env", ".env.*"]
}
}
}Strict Mode (Default)
Only explicitly configured variables are available to tasks. Unlisted vars are filtered out.
Benefits: Guarantees cache correctness, prevents accidental dependencies, reproducible builds.
turbo run build --env-mode=strictLoose Mode
All system environment variables are available but only env/globalEnv vars affect the hash:
turbo run build --env-mode=looseRisks: Cache may restore incorrect results if unhashed vars changed. Use for migrating legacy projects.
Framework Inference
Turborepo auto-detects frameworks and includes their conventional env vars:
| Framework | Pattern |
|---|---|
| Next.js | NEXT_PUBLIC_* |
| Vite | VITE_* |
| Create React App | REACT_APP_* |
| Gatsby | GATSBY_* |
| Nuxt | NUXT_*, NITRO_* |
| Expo | EXPO_PUBLIC_* |
| Astro | PUBLIC_* |
| SvelteKit | PUBLIC_* |
Disabling Framework Inference
turbo run build --framework-inference=falseOr exclude specific patterns:
{
"tasks": {
"build": {
"env": ["!NEXT_PUBLIC_*"]
}
}
}passThroughEnv
Variables available at runtime but NOT included in cache hash:
{
"tasks": {
"build": {
"passThroughEnv": ["AWS_SECRET_KEY", "GITHUB_TOKEN"]
}
}
}Changes to these vars won't cause cache misses.
globalPassThroughEnv
For CI variables that need to be available but shouldn't affect cache:
{
"globalPassThroughEnv": ["GITHUB_TOKEN", "CI"]
}Anti-Patterns
Environment Variables Not Hashed
// Bad: API_URL changes won't rebuild
{ "tasks": { "build": { "outputs": ["dist/**"] } } }
// Good: API_URL in hash
{ "tasks": { "build": { "outputs": ["dist/**"], "env": ["API_URL"] } } }Overly Broad globalDependencies
// Bad: affects all hashes
{ "globalDependencies": ["**/.env.*local"] }
// Better: task-level inputs
{
"globalDependencies": [".env"],
"tasks": {
"build": { "inputs": ["$TURBO_DEFAULT$", ".env*"] }
}
}Root .env File
Package .env files in each app, not a single root .env. Root .env creates implicit coupling, coarse cache invalidation, and security risks.
NOT an Anti-Pattern
A large env array (50+ variables) is fine. It means thorough environment declaration.
Checking Environment Mode
turbo run build --dry=json | jq '.tasks[].environmentVariables'Filtering and CI Patterns
Filter Syntax
Single Package
turbo run build --filter=web
turbo run test --filter=@acme/apiPackage with Dependencies
Build a package and everything it depends on:
turbo run build --filter=web...Package Dependents
Run in all packages that depend on a library:
turbo run test --filter=...uiDependents Only (Exclude Target)
turbo run test --filter=...^uiChanged Packages
turbo run lint --filter=[HEAD^1]
turbo run lint --filter=[main...HEAD]Changed + Dependents
turbo run build test --filter=...[HEAD^1]
# Or shortcut:
turbo run build test --affectedDirectory-Based
turbo run build --filter=./apps/*
turbo run build --filter=./apps/web --filter=./apps/apiScope-Based
turbo run build --filter=@acme/*Exclusions
turbo run build --filter=./apps/* --filter=!admin
turbo run lint --filter=!legacy-app --filter=!deprecated-pkgCustom Affected Base
turbo run build --affected --affected-base=origin/developDebugging Filters
turbo run build --filter=web... --dry
turbo run build --filter=...[HEAD^1] --dry=jsonGitHub Actions CI
Complete Example
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 2
- uses: pnpm/action-setup@v3
with:
version: 9
- uses: actions/setup-node@v6
with:
node-version: 20
cache: 'pnpm'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: turbo run build --affected
- name: Test
run: turbo run test --affected
- name: Lint
run: turbo run lint --affectedPackage Manager Setup
# pnpm
- uses: pnpm/action-setup@v3
with:
version: 9
- run: pnpm install --frozen-lockfile
# Yarn
- run: yarn install --frozen-lockfile
# Bun
- uses: oven-sh/setup-bun@v1
- run: bun install --frozen-lockfileRemote Cache Setup
1. Create Vercel access token at Vercel Dashboard 2. Add TURBO_TOKEN as repository secret 3. Add TURBO_TEAM as repository variable 4. Reference in workflow env block
CI Patterns Summary
| Scenario | Command |
|---|---|
| PR validation | turbo run build test lint --affected |
| Deploy changed apps | turbo run deploy --filter=./apps/* --filter=[main...HEAD] |
| Full rebuild of app | turbo run build --filter=production-app... |
Task Configuration
Standard Build Pipeline
{
"$schema": "https://turborepo.dev/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}dependsOn Syntax
{
"tasks": {
"build": { "dependsOn": ["^build"] },
"test": { "dependsOn": ["build"] },
"deploy": { "dependsOn": ["web#build"] }
}
}| Syntax | Meaning |
|---|---|
^build | Run build in DEPENDENCIES first (upstream packages) |
build (no ^) | Run build in SAME PACKAGE first |
pkg#task | Specific package's task |
The ^ prefix is crucial -- without it, you're referencing the same package.
Transit Nodes for Parallel Tasks
Tasks like lint and typecheck can run in parallel but need dependency-aware caching:
{
"tasks": {
"transit": { "dependsOn": ["^transit"] },
"lint": { "dependsOn": ["transit"] },
"check-types": { "dependsOn": ["transit"] }
}
}DO NOT use `dependsOn: ["^lint"]` -- this forces sequential execution. DO NOT use `dependsOn: []` -- this breaks cache invalidation.
The transit task creates dependency relationships without running anything (no matching script).
outputs
Glob patterns for files to cache. If omitted, nothing is cached.
{
"tasks": {
"build": {
"outputs": ["dist/**", "build/**"]
}
}
}Framework examples:
| Framework | outputs |
|---|---|
| Next.js | [".next/**", "!.next/cache/**"] |
| Vite/Rollup | ["dist/**"] |
| tsc | ["dist/**"] or custom outDir |
| tsc --noEmit | .tsbuildinfo file location |
| Lint/typecheck | [] (no file outputs) |
inputs
Files considered when calculating task hash. Defaults to all tracked files.
{
"tasks": {
"build": {
"inputs": [
"$TURBO_DEFAULT$",
"!README.md",
"$TURBO_ROOT$/tsconfig.base.json"
]
}
}
}| Value | Meaning |
|---|---|
$TURBO_DEFAULT$ | Include default inputs, then add/remove |
$TURBO_ROOT$/<path> | Reference files from repo root |
Task Options Reference
| Option | Default | Description |
|---|---|---|
cache | true | Enable/disable caching |
persistent | false | Long-running tasks that don't exit |
interactive | false | Allow stdin input |
interruptible | false | Allow turbo watch to restart |
outputLogs | full | full, hash-only, new-only, errors-only, none |
with | - | Sidecar tasks that run alongside (concurrent) |
description | - | Human-readable task description |
passThroughEnv | - | Available at runtime but NOT in cache hash |
extends | - | Inherit from another package's task (composable config) |
Package Configurations
Use per-package turbo.json instead of cluttering root with package#task overrides:
{
"extends": ["//"],
"tasks": {
"test": { "outputs": ["coverage/**"] }
}
}Dev Task with ^dev Pattern (for turbo watch)
{
"tasks": {
"dev": {
"dependsOn": ["^dev"],
"cache": false,
"persistent": false
}
}
}{
"extends": ["//"],
"tasks": {
"dev": { "persistent": true }
}
}Packages run one-shot dev scripts; apps override with persistent: true. Use with turbo watch dev.
Composable Configuration (Turborepo 2.7+)
Package configurations can extend from any workspace package, not just root:
{
"extends": ["@repo/turbo-config"],
"tasks": {
"build": {
"dependsOn": ["$TURBO_EXTENDS$", "codegen"]
}
}
}$TURBO_EXTENDS$ appends to inherited arrays instead of replacing them. Without it, the local dependsOn completely overrides the inherited value.
Workspace Structure
Workspace Configuration
# pnpm-workspace.yaml
packages:
- 'apps/*'
- 'packages/*'Root package.json
{
"name": "my-monorepo",
"private": true,
"packageManager": "pnpm@9.0.0",
"scripts": {
"build": "turbo run build",
"dev": "turbo run dev",
"lint": "turbo run lint",
"test": "turbo run test"
},
"devDependencies": {
"turbo": "latest"
}
}Key points:
private: trueprevents accidental publishing- Scripts only delegate to
turbo run-- no actual build logic - Minimal devDependencies (just turbo and repo tools)
- App dependencies belong in each package, not root
Package Tasks, Not Root Tasks
// apps/web/package.json
{ "scripts": { "build": "next build", "lint": "eslint .", "test": "vitest" } }
// packages/ui/package.json
{ "scripts": { "build": "tsc", "lint": "eslint .", "test": "vitest" } }// Root package.json ONLY delegates
{
"scripts": {
"build": "turbo run build",
"lint": "turbo run lint",
"test": "turbo run test"
}
}Root Tasks (//#taskname) are only for tasks that truly cannot exist in packages.
Workspace Dependencies
Use workspace:* for internal dependencies:
{
"dependencies": {
"@repo/ui": "workspace:*",
"@repo/utils": "workspace:*"
}
}Shared UI Package
{
"name": "@repo/ui",
"version": "0.0.0",
"private": true,
"exports": {
".": "./src/index.ts",
"./styles.css": "./src/styles.css"
},
"peerDependencies": {
"react": "^19.0.0"
}
}Shared TypeScript Config
// packages/typescript-config/base.json
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"moduleResolution": "bundler",
"module": "ESNext",
"target": "ES2022"
}
}
// packages/ui/tsconfig.json
{
"extends": "@repo/typescript-config/library.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}Shared ESLint Config
// packages/eslint-config/package.json
{
"name": "@repo/eslint-config",
"exports": {
"./base": "./base.js",
"./next": "./next.js",
"./library": "./library.js"
}
}Anti-Patterns
Relative Imports Across Packages
// Bad
import { Button } from '../../packages/ui/src/button';
// Good
import { Button } from '@repo/ui/button';Shared Code Inside Apps
Extract shared code to packages/, not apps/web/shared/.
prebuild Scripts
// Bad - bypasses dependency graph
{
"scripts": {
"prebuild": "cd ../../packages/types && bun run build",
"build": "next build"
}
}
// Good - declare dependency, let turbo handle order
{
"dependencies": { "@repo/types": "workspace:*" },
"scripts": { "build": "next build" }
}Chaining Turbo Tasks with &&
// Bad
{ "scripts": { "changeset:publish": "bun build && changeset publish" } }
// Good
{ "scripts": { "changeset:publish": "turbo run build && changeset publish" } }Root Scripts Bypassing Turbo
// Bad
{ "scripts": { "build": "bun build" } }
// Good
{ "scripts": { "build": "turbo run build" } }turbo run vs turbo
Always use turbo run in code (package.json, CI, scripts). The shorthand turbo <task> is only for one-off terminal commands.
JSONC Support
Use turbo.jsonc instead of turbo.json to add comments to configuration:
{
"$schema": "https://turborepo.dev/schema.json",
// Build all dependencies first
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"],
},
},
}Lockfile
A lockfile is required for reproducible builds, dependency understanding, and cache correctness.