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
April 3, 2026
11 min read

Monorepo Tools Comparison: Turborepo vs Nx vs Lerna in 2026

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 100+ 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.


🚀 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.


Working 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.


See Also

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 100+ projects delivered across MT4/MT5 EAs, fintech platforms, and production AI systems, the team brings deep technical experience to every engagement. Based in India, serving clients globally.

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.