Back to Blog

Monorepo Tools Comparison: Turborepo vs Nx vs Lerna in 2026

Compare Turborepo, Nx, and Lerna for JavaScript monorepos — build caching, task pipelines, code generation, and team fit. Includes configuration examples and mi

Viprasol Tech Team
11 min read
Updated 2026

Monorepo Tools Comparison: Turborepo vs Nx vs Lerna in 2026

Quick answer. Choose Turborepo for fast, low-config build caching on JavaScript/TypeScript projects, Nx when you need richer code generation, dependency graphing, and plugin tooling at scale, and Lerna mainly for legacy package publishing. Adopt a monorepo only if you actually have shared types or coordinated cross-package releases to justify it.

A monorepo keeps multiple projects — a web app, an API, shared component libraries, utility packages — in one repository. The appeal is obvious: one PR touches the API and the frontend together, shared code doesn't require publishing packages to npm, and CI can run only what's affected by a change.

The tooling choice matters because the wrong one either underdelivers on caching (slow CI) or overdelivers on complexity (engineers spend time learning the tool instead of building features).


When a Monorepo Makes Sense

Monorepos solve specific problems. Before adopting one, confirm you have them:

ProblemMonorepo HelpsMonorepo Doesn't Help
Shared types between frontend and backend
Coordinated releases across packages
Consistent tooling (lint, test, format)
Team of 2 with one app❌ (overhead not worth it)
Completely independent services, different stacks❌ (use polyrepo)
Slow CI for a single-package repo❌ (fix CI, not repo structure)

Repository Structure (Common to All Tools)

All three tools work with a similar directory layout:

my-monorepo/
├── apps/
│   ├── web/                 # Next.js frontend
│   ├── api/                 # Fastify backend
│   └── mobile/              # React Native app
├── packages/
│   ├── ui/                  # Shared React components
│   ├── types/               # Shared TypeScript types
│   ├── utils/               # Shared utilities
│   └── config/
│       ├── eslint/          # Shared ESLint config
│       ├── typescript/      # Shared tsconfig
│       └── jest/            # Shared Jest config
├── package.json             # Root package.json (workspaces)
└── turbo.json / nx.json     # Tool-specific config

Root package.json (pnpm workspaces):

{
  "name": "my-monorepo",
  "private": true,
  "workspaces": ["apps/*", "packages/*"],
  "scripts": {
    "build": "turbo build",
    "dev": "turbo dev --parallel",
    "test": "turbo test",
    "lint": "turbo lint"
  },
  "devDependencies": {
    "turbo": "^2.0.0"
  }
}

🌐 Looking for a Dev Team That Actually Delivers?

Most agencies sell you a project manager and assign juniors. Viprasol is different — senior engineers only, direct Slack access, and a 5.0★ Upwork record across 1000+ projects.

  • React, Next.js, Node.js, TypeScript — production-grade stack
  • Fixed-price contracts — no surprise invoices
  • Full source code ownership from day one
  • 90-day post-launch support included

Turborepo

Turborepo (by Vercel) focuses on one thing: making build, test, and lint tasks fast through intelligent caching and parallelization. Its configuration is minimal, and it integrates with any build system.

turbo.json configuration:

{
  "$schema": "https://turbo.build/schema.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**", "!dist/cache/**"],
      "cache": true
    },
    "dev": {
      "dependsOn": ["^build"],
      "cache": false,
      "persistent": true
    },
    "test": {
      "dependsOn": ["build"],
      "outputs": ["coverage/**"],
      "cache": true
    },
    "lint": {
      "cache": true
    },
    "type-check": {
      "dependsOn": ["^type-check"],
      "cache": true
    }
  }
}

Key concepts:

  • "^build" means "build all dependencies first" — Turbo resolves the dependency graph automatically
  • outputs tells Turbo what to cache — on a cache hit, it restores the output files instead of re-running
  • persistent: true for dev tasks (never completes, so never cached)

Running with filtering:

# Build only affected packages (since last commit)
turbo build --filter=...[HEAD^1]

# Build a specific app and its dependencies
turbo build --filter=web...

# Run dev for the API only
turbo dev --filter=api

Remote caching with Vercel (free for open source, paid for teams):

# Link to Vercel remote cache
npx turbo login
npx turbo link

# Or self-host with Turborepo remote cache API
# Set TURBO_TEAM and TURBO_TOKEN env vars in CI

With remote cache, a CI job that's already been run on the same code takes seconds — Turbo downloads cached outputs instead of re-executing.

Turborepo strengths: Minimal config, fast by default, excellent Vercel integration, zero lock-in (still uses your existing build tools).

Turborepo limitations: No code generation, no project templates, no understanding of your code's structure — it just runs tasks. For teams that want generators and structural enforcement, Nx is more capable.


Nx

Nx is a full-featured monorepo platform. Beyond caching, it understands your project graph (who imports whom), can generate code, enforces module boundaries, and has plugins for every major framework.

nx.json configuration:

{
  "$schema": "./node_modules/nx/schemas/nx-schema.json",
  "defaultBase": "main",
  "namedInputs": {
    "default": ["{projectRoot}/**/*", "sharedGlobals"],
    "production": [
      "default",
      "!{projectRoot}/**/*.spec.ts",
      "!{projectRoot}/jest.config.ts"
    ],
    "sharedGlobals": []
  },
  "targetDefaults": {
    "build": {
      "dependsOn": ["^build"],
      "inputs": ["production", "^production"],
      "cache": true
    },
    "test": {
      "inputs": ["default", "^production"],
      "cache": true
    },
    "lint": {
      "inputs": ["default"],
      "cache": true
    }
  },
  "plugins": [
    "@nx/next/plugin",
    "@nx/eslint/plugin"
  ]
}

Nx project configuration (apps/web/project.json):

{
  "name": "web",
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "apps/web",
  "projectType": "application",
  "tags": ["type:app", "scope:web"],
  "targets": {
    "build": {
      "executor": "@nx/next:build",
      "options": {
        "outputPath": "dist/apps/web"
      }
    },
    "dev": {
      "executor": "@nx/next:dev",
      "options": { "port": 3000 }
    }
  }
}

Module boundary enforcement (prevents packages from importing things they shouldn't):

// .eslintrc.json
{
  "rules": {
    "@nx/enforce-module-boundaries": [
      "error",
      {
        "enforceBuildableLibDependency": true,
        "allow": [],
        "depConstraints": [
          {
            "sourceTag": "scope:web",
            "onlyDependOnLibsWithTags": ["scope:web", "scope:shared"]
          },
          {
            "sourceTag": "scope:api",
            "onlyDependOnLibsWithTags": ["scope:api", "scope:shared"]
          },
          {
            "sourceTag": "type:app",
            "onlyDependOnLibsWithTags": ["type:lib", "type:util"]
          }
        ]
      }
    ]
  }
}

Code generation (Nx generators):

# Generate a new Next.js app
nx g @nx/next:app dashboard

# Generate a new shared React library
nx g @nx/react:library ui-components --publishable

# Generate a component inside the ui library
nx g @nx/react:component Button --project=ui-components --export

Nx strengths: Full project graph analysis, code generation, module boundary enforcement, excellent for large teams with complex dependency rules.

Nx limitations: Steeper learning curve, more configuration, the plugin system can feel heavy for simple monorepos.


monorepo - Monorepo Tools Comparison: Turborepo vs Nx vs Lerna in 2026

🚀 Senior Engineers. No Junior Handoffs. Ever.

You get the senior developer, not a project manager who relays your requirements to someone you never meet. Every Viprasol project has a senior lead from kickoff to launch.

  • MVPs in 4–8 weeks, full platforms in 3–5 months
  • Lighthouse 90+ performance scores standard
  • Works across US, UK, AU timezones
  • Free 30-min architecture review, no commitment

Lerna

Lerna was the original JavaScript monorepo tool, primarily focused on package versioning and publishing. It's been revitalized by Nx Inc. (Lerna 6+) and now uses Nx for task running under the hood.

lerna.json:

{
  "$schema": "node_modules/lerna/schemas/lerna-schema.json",
  "version": "independent",
  "npmClient": "pnpm",
  "command": {
    "publish": {
      "conventionalCommits": true,
      "message": "chore(release): publish"
    }
  }
}

Publishing packages:

# Bump versions and publish all changed packages
lerna publish --conventional-commits

# Publish specific package
lerna publish --scope=@myorg/ui

Lerna's publishing workflow — detecting which packages changed, bumping versions according to conventional commits, updating changelogs, and publishing to npm — is its primary differentiator. Turborepo and Nx don't do this natively.

Lerna strengths: Best npm publish workflow, conventional commit versioning, changelog generation.

Lerna limitations: Primarily for publishable packages — if you're not publishing to npm, Turbo or Nx are better choices.


Decision Guide

Are you publishing packages to npm?
├── YES → Lerna (+ Nx for task running)
└── NO → Do you have 20+ developers with complex dependency rules?
    ├── YES → Nx (module boundaries, generators, project graph)
    └── NO → Turborepo (minimal config, fast, get started in 30 min)

Team size heuristics:

  • 1–10 engineers: Turborepo — minimal overhead, fast to learn
  • 10–50 engineers: Either Turborepo or Nx depending on complexity
  • 50+ engineers: Nx — the generator and boundary enforcement pays off at scale

Shared Package Setup (Works with All Tools)

// packages/types/src/index.ts
export interface User {
  id: string;
  email: string;
  role: 'admin' | 'editor' | 'viewer';
}

export interface ApiResponse<T> {
  data: T;
  error?: string;
  meta?: Record<string, unknown>;
}
// packages/types/package.json
{
  "name": "@myorg/types",
  "version": "0.0.1",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "scripts": {
    "build": "tsc",
    "type-check": "tsc --noEmit"
  }
}
// apps/web/package.json — consuming the shared package
{
  "dependencies": {
    "@myorg/types": "workspace:*"
  }
}

With workspace protocol (workspace:*), pnpm links to the local package instead of fetching from npm. Changes to @myorg/types are immediately visible to apps/web — no publish step needed.


CI Configuration (Turborepo Example)

# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  build-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Full history for Turbo's affected calculation

      - uses: pnpm/action-setup@v3
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'pnpm'

      - run: pnpm install --frozen-lockfile

      - name: Build and test affected
        run: pnpm turbo build test lint --filter=...[origin/main]
        env:
          TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
          TURBO_TEAM: ${{ secrets.TURBO_TEAM }}

With --filter=...[origin/main], Turbo only runs tasks for packages changed since the last merge to main. On a 20-package monorepo where you changed 2 packages, this runs 2 builds instead of 20.


Partnering With Viprasol

We set up and maintain monorepo infrastructure for product teams moving from spaghetti multi-repo setups to structured workspaces. The typical outcome: CI times drop 60–80%, shared code is actually shared instead of copy-pasted, and cross-team PRs become routine rather than painful.

Talk to our team about your repository architecture.


Related Reading

Related: Next.js Monorepo + Turborepo: pnpm Workspaces & CI — structuring a Next.js monorepo with Turborepo and pnpm.

How to Approach a Turborepo Nx Monorepo Tools Comparison in 2026

When teams run a turborepo nx monorepo tools comparison, the deciding factor is rarely raw speed alone. Turborepo focuses on fast, cache-driven task pipelines and stays deliberately minimal, making it a natural fit for JavaScript and TypeScript projects that want low configuration overhead. Nx goes further with code generators, dependency graph visualization, and plugins for multiple languages and frameworks, which suits larger or polyglot codebases. Lerna remains useful mainly for package versioning and publishing, often paired with one of the others.

At Viprasol, our senior engineers weigh remote caching, build orchestration, and CI integration against your actual repository size and team workflow. The right monorepo build tool is the one your developers can own end to end, not the one with the longest feature list.

monorepoturboreponxdevopsfrontend
Share this article:

About the Author

V

Viprasol Tech Team

Custom Software Development Specialists

The Viprasol Tech team specialises in algorithmic trading software, AI agent systems, and SaaS development. With 1000+ projects delivered across MT4/MT5 EAs, fintech platforms, and production AI systems, the team brings deep technical experience to every engagement.

MT4/MT5 EA DevelopmentAI Agent SystemsSaaS DevelopmentAlgorithmic Trading

Need a Modern Web Application?

From landing pages to complex SaaS platforms — we build it all with Next.js and React.

Free consultation • No commitment • Response within 24 hours

Viprasol · Web Development

Need a custom web application built?

We build React and Next.js web applications with Lighthouse ≥90 scores, mobile-first design, and full source code ownership. Senior engineers only — from architecture through deployment.