.agents/skills/turborepo/references/best-practices/dependencies.md
Best practices for managing dependencies in a Turborepo monorepo.
Dependencies belong in the package that uses them, not the root.
# Good: Install in specific package
pnpm add react --filter=@repo/ui
pnpm add next --filter=web
# Avoid: Installing in root
pnpm add react -w # Only for repo-level tools!
Each package's package.json lists exactly what it needs:
// packages/ui/package.json
{
"dependencies": {
"react": "^18.0.0",
"class-variance-authority": "^0.7.0"
}
}
Different packages can use different versions when needed:
// packages/legacy-ui/package.json
{ "dependencies": { "react": "^17.0.0" } }
// packages/ui/package.json
{ "dependencies": { "react": "^18.0.0" } }
Installing in root changes workspace lockfile, invalidating all caches.
turbo prune can remove unused dependencies for Docker images.
Only repository-level tools:
// Root package.json
{
"devDependencies": {
"turbo": "latest",
"husky": "^8.0.0",
"lint-staged": "^15.0.0"
}
}
NOT application dependencies:
# pnpm
pnpm add lodash --filter=@repo/utils
# npm
npm install lodash --workspace=@repo/utils
# yarn
yarn workspace @repo/utils add lodash
# bun
cd packages/utils && bun add lodash
# pnpm
pnpm add vitest --save-dev --filter=web --filter=@repo/ui
# npm
npm install vitest --save-dev --workspace=web --workspace=@repo/ui
# yarn (v2+)
yarn workspaces foreach -R --from '{web,@repo/ui}' add vitest --dev
# pnpm
pnpm add @repo/ui --filter=web
# This updates package.json:
{
"dependencies": {
"@repo/ui": "workspace:*"
}
}
# syncpack - Check and fix version mismatches
npx syncpack list-mismatches
npx syncpack fix-mismatches
# manypkg - Similar functionality
npx @manypkg/cli check
npx @manypkg/cli fix
# sherif - Rust-based, very fast
npx sherif
# pnpm - Update everywhere
pnpm up --recursive typescript@latest
# npm - Update in all workspaces
npm install typescript@latest --workspaces
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
catalog:
react: ^18.2.0
typescript: ^5.3.0
// Any package.json
{
"dependencies": {
"react": "catalog:" // Uses version from catalog
}
}
// pnpm/bun
{ "@repo/ui": "workspace:*" }
// npm/yarn
{ "@repo/ui": "*" }
Turborepo understands these relationships and orders builds accordingly.
{ "lodash": "^4.17.21" }
Standard semver versioning from npm.
For library packages that expect the consumer to provide dependencies:
// packages/ui/package.json
{
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"devDependencies": {
"react": "^18.0.0", // For development/testing
"react-dom": "^18.0.0"
}
}
pnpm install / npm install to update lockfilePackages can use different versions - this is a feature, not a bug. But if you need consistency:
Some tools expect dependencies in specific locations. Use package manager config:
# .npmrc (pnpm)
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*
Required for:
# Commit your lockfile!
git add pnpm-lock.yaml # or package-lock.json, yarn.lock