lerna, nx, and turbo are all tools designed to manage monorepos — codebases that contain multiple packages or applications in a single repository. They help with tasks like dependency management, task orchestration, caching, and cross-project development workflows. While they share the goal of improving developer productivity in complex codebases, they differ significantly in architecture, scope, and philosophy.
Managing multiple projects in one repository used to mean endless scripts and manual coordination. Today, lerna, nx, and turbo offer structured solutions — but they solve different problems in different ways. Let’s cut through the noise and compare how each handles real-world engineering challenges.
lerna started as a lightweight CLI to manage npm packages in a monorepo. It assumes you’re using npm workspaces (or its own linking system) and focuses on versioning, publishing, and running scripts across packages. It doesn’t care how you build your apps — just that they’re valid npm packages.
// lerna.json
{
"version": "independent",
"npmClient": "npm",
"useWorkspaces": true
}
nx is a full development platform. It includes a powerful task orchestrator, dependency graph analysis, code generation, linting rules, and deep editor integration. It treats your monorepo as a unified system where apps, libs, and tools are first-class citizens with enforced relationships.
// nx.json
{
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"inputs": ["production", "^production"]
}
}
}
turbo is a high-performance task runner focused on speed and caching. It doesn’t manage dependencies or enforce project structure — it just runs your existing npm scripts faster by skipping redundant work and sharing caches across machines.
// turbo.json
{
"pipeline": {
"build": {
"outputs": ["dist/**"],
"dependsOn": ["^build"]
},
"test": {
"outputs": [],
"inputs": ["src/**", "test/**"]
}
}
}
All three assume you’re using npm workspaces, Yarn workspaces, or pnpm workspaces for dependency hoisting and linking. None replace your package manager.
lerna: Historically managed symlinks itself, but now recommends useWorkspaces: true to delegate to npm/Yarn/pnpm.nx: Fully compatible with any workspace setup; uses it to resolve project relationships.turbo: Agnostic — only reads package.json scripts and workspace topology.✅ All three work best when you’ve already set up a modern workspace with your preferred package manager.
lerna runs npm scripts across packages using simple filtering:
# lerna: Run build in all packages that have a "build" script
lerna run build
# Only in packages that depend on "utils"
lerna run build --scope="*" --include-dependencies --since=main
nx uses a dependency-aware task graph. It knows which projects changed and which tasks depend on others:
# nx: Build only affected projects since main
nx affected:build
# Or build a specific project and its deps
nx build my-app
turbo runs tasks in topological order with content-aware caching:
# turbo: Run build across all packages with shared cache
turbo run build
# Only rebuild what changed based on file hashes
turbo run build --filter="./apps/*"
Assume this structure:
/packages
/shared-utils
/web-app
lerna requires you to ensure shared-utils is built before web-app, often via --include-dependencies:
lerna run build --scope=web-app --include-dependencies
nx automatically infers the dependency from imports or explicit tags and runs shared-utils:build first:
nx build web-app # automatically builds shared-utils if needed
turbo uses the dependsOn: ["^build"] rule in turbo.json to enforce order:
// turbo.json
{
"pipeline": {
"build": {
"outputs": ["dist/**"],
"dependsOn": ["^build"]
}
}
}
Then:
turbo run build --filter=web-app...
lerna has no built-in caching. Every lerna run executes scripts unless you add external tooling.
nx offers computation caching based on source files, environment variables, and task inputs. Supports local disk and remote distributed caching (via Nx Cloud or self-hosted):
# Enable remote caching in nx.json
{
"tasksRunnerOptions": {
"default": {
"runner": "nx/tasks-runners/default",
"options": {
"cacheableOperations": ["build", "test"],
"remoteCache": {
"url": "https://your-nx-cache.com"
}
}
}
}
}
turbo was built for remote caching from day one. It hashes inputs and uploads/downloads artifacts automatically when connected to Vercel or a custom endpoint:
# Login to enable remote caching
turbo login
# Now all runs share cache across team
turbo run build
lerna is CLI-only. No editor plugins, no graph visualization, no code generators.
nx ships with VS Code and WebStorm plugins, a graph visualizer (nx graph), and schematics for generating components, libs, and apps:
# Generate a new React lib with tests, storybook, etc.
nx g @nx/react:lib ui-components
turbo aims for zero-config adoption. Just add turbo.json and run turbo run. No generators, no IDE plugins — but integrates smoothly with existing tooling like Next.js or Vite.
lernalerna version and lerna publish handle independent or fixed versioning cleanly.# Standard OSS release flow with lerna
lerna version --conventional-commits
lerna publish from-git
nx// nx.json: Enforce that "feature-x" can't import from "admin-only"
{
"namedInputs": {
"default": ["{projectRoot}/**/*"]
},
"targetDefaults": {
"build": {
"inputs": ["default", "^default"]
}
}
}
turbo// Minimal turbo.json for a Next.js monorepo
{
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"outputs": [".next/**", "!build/**"]
},
"dev": {
"cache": false,
"persistent": true
}
}
}
lerna + nx: Nx can import an existing Lerna repo using nx init.turbo + anything: Turbo works alongside Lerna or Nx — just add turbo.json and replace lerna run or nx run with turbo run for cached tasks.lerna bootstrap) in new projects — stick to npm workspaces.| Feature | lerna | nx | turbo |
|---|---|---|---|
| Primary Focus | Package publishing | Full dev platform | Task speed & caching |
| Caching | ❌ None | ✅ Local + remote | ✅ Local + remote (built-in) |
| Task Intelligence | Basic filtering | Dependency graph aware | Input hashing + topological |
| IDE Integration | ❌ | ✅ VS Code, WebStorm | ❌ |
| Code Generation | ❌ | ✅ Schematics | ❌ |
| Remote Caching Setup | N/A | Requires config | ✅ turbo login |
| Best For | OSS npm packages | Large app suites | Speed-focused teams |
These tools aren’t mutually exclusive — and you might even combine them. But start by asking:
lernanxturboPick the tool that matches your team’s biggest pain point today — not the one with the most features.
Choose nx if you’re building a large-scale application suite (e.g., multiple frontend apps sharing backend services and libraries) and need deep tooling for task orchestration, computation caching, dependency graph visualization, and editor integration. Nx excels when you want a batteries-included experience with first-class support for frameworks like React, Angular, and NestJS, along with strong project boundaries and code quality enforcement.
Choose turbo if you prioritize speed and simplicity in task execution and caching, especially in CI environments. Turbo is ideal for teams already using modern toolchains (like Vite, Next.js, or Remix) who want minimal configuration, fast incremental builds, and seamless remote caching without adopting a full framework ecosystem.
Choose lerna if you're maintaining a traditional package-based monorepo (like those common in open-source libraries) and need straightforward versioning, publishing, and npm workspace integration. It’s ideal when your primary needs are managing interdependent packages with conventional changelog-driven releases and you don’t require advanced build pipelines or deep IDE integrations.
Get to green PRs in half the time. Nx optimizes your builds, scales your CI, and fixes failed PRs. Built for developers and AI agents.
Using npx
npx create-nx-workspace
Using npm init
npm init nx-workspace
Using yarn create
yarn create nx-workspace
Run:
npx nx@latest init