Nx vs Turborepo in 2026 comes down to one question: how much structure does your React monorepo need? Nx 21 keeps adding things: continuous tasks, a terminal UI, an MCP server that hands your workspace graph to AI agents, and self-healing CI through Nx Cloud. Turborepo has stayed small on purpose. It's a task runner with a cache and very little else. I set up the same React + Vite pnpm monorepo in both. Here is the config you'll actually write, plus the project count and team shape where Nx starts to pay for itself.
The test repo: a typical React + Vite pnpm monorepo
The repo is two Vite React apps (web and admin), a shared ui component library, a utils package, and Vitest everywhere. Most mid-sized product teams have something like this. If you haven't set up the workspace layer yet, start with our pnpm workspaces and Biome monorepo guide. Both tools here sit on top of that.
apps/
web/ # Vite + React 19
admin/ # Vite + React 19
packages/
ui/ # component library, built with Vite library mode
utils/ # plain TS
pnpm-workspace.yamlTurborepo: the default for most React monorepos
Verdict: Turborepo is the right pick for most React + Vite monorepos under about 15 packages, because you get caching and task ordering for one config file and almost no lock-in.
Adding it to an existing pnpm workspace takes about two minutes:
pnpm add -Dw turbo{
"$schema": "https://turborepo.com/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["^build"],
"outputs": ["coverage/**"]
},
"lint": {},
"dev": {
"cache": false,
"persistent": true
}
}
}Then run pnpm turbo run build test --affected in CI. Each package keeps its own package.json scripts. Turbo reads them, orders them by the dependency graph, and hashes their inputs. To delete Turbo later, you remove one file and one dev dependency. That exit cost is the whole selling point.
For remote caching, pnpm turbo login && pnpm turbo link connects you to Vercel's cache. It's free on Hobby and the obvious choice if you already deploy there. You can also self-host a cache server if your security team won't sign off on Vercel.
Where it hurts: Turborepo can't tell you that admin shouldn't import from web's internals. turbo boundaries exists but is still basic. There are no generators, so every new package is a copy-paste of the last one, including the tsconfig quirks somebody fixed in March and nobody wrote down.
Nx 21: the monorepo platform
Verdict: Nx 21 is the better tool once you have roughly 15+ projects or 3+ teams, because its project graph, module boundaries, generators and distributed CI fix problems Turborepo doesn't even try to solve.
Greenfield setup:
npx create-nx-workspace@latest acme --preset=react-monorepo --bundler=vite --unitTestRunner=vitest --e2eTestRunner=playwright --workspacesFor an existing pnpm repo, run pnpm dlx nx@latest init. Nx detects your Vite configs and infers targets through @nx/vite/plugin, so you don't write a project.json per package any more. The setup lives in one nx.json:
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"plugins": [
{
"plugin": "@nx/vite/plugin",
"options": {
"buildTargetName": "build",
"testTargetName": "test",
"serveTargetName": "dev"
}
}
],
"namedInputs": {
"production": ["default", "!{projectRoot}/**/*.test.tsx"]
},
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"inputs": ["production", "^production"],
"cache": true
}
}
}The namedInputs block matters more than it looks. Editing a test file no longer busts the production build cache. Turborepo can do the same with per-task inputs, but Nx makes it the default pattern.
The Nx 21 features I actually use:
- Continuous tasks: mark a target
"continuous": trueand other tasks can depend on it. Your Playwright e2e target can start thedevserver itself, so you can drop theconcurrently+wait-onhacks. - Terminal UI: parallel task output in separate panes, not interleaved soup. Turbo has a TUI too, but Nx's handles continuous tasks better.
- Nx MCP server: your editor's AI agent can query the project graph, ownership and generators. When Claude Code or Cursor knows
uihas 40 dependents, its suggestions stop being naive. - Generators:
nx g @nx/react:library packages/forms --bundler=vitegives every new package the same tsconfig, test setup and exports map.
The catch nobody mentions: @nx/enforce-module-boundaries is an ESLint rule. If you moved to Biome (and most of you should have), you have to keep a minimal ESLint config alive just for that one rule. It's annoying, and the docs bury it.
Plain pnpm workspaces: the option people skip
Verdict: Plain pnpm workspaces with no orchestrator are fine for two or three packages and one developer. Once CI runs longer than about four minutes, add Turborepo.
pnpm -r --filter ...[origin/main] run build already gives you topological ordering and changed-package filtering. What you lose is caching. On a small repo, a cold build often finishes faster than a team spends arguing about which orchestrator to adopt.
When does Nx beat Turborepo?
Nx beats Turborepo once you pass about 15 to 20 projects, three or more teams commit to the same repo, or CI regularly takes over 10 minutes. At that point the graph, the boundaries and distributed task execution save more time than Nx costs.
Here are the signals I'd act on:
- Project count above 15: generators pay back their learning curve after about the fifth package you'd otherwise copy-paste.
- Multiple teams: tag-based boundaries (
scope:checkoutcan't importscope:admin) catch the import that turns into a three-week untangling job later. - CI over 10 minutes: Nx Cloud's distributed execution spreads tasks across agents based on the graph, not just shards. Turborepo only caches. It doesn't distribute.
- Heavy AI-agent usage: the MCP server is the one place Nx has a lead in 2026 that Turborepo isn't really chasing.
Is Nx overkill for a small React monorepo?
Yes. For two apps and a shared UI library with fewer than about eight developers, Nx is overkill. You'll spend more time learning inferred targets, plugin versions and nx migrate than you'll ever save on builds.
The hidden cost is upgrades. Nx plugins are versioned in lockstep with Nx itself, so a major bump means running nx migrate latest, reviewing the generated migrations, and sometimes fixing a plugin that changed how it infers targets. The new migrate UI helps, but it's still a chore Turborepo simply doesn't have. I've lost more than one afternoon to a Vite plugin version mismatch hidden behind an Nx inference layer, staring at a cache miss that worked fine on my machine.
Nx also changes how people think about the repo. New hires have to learn "targets", "executors" and "project crystal" before they can tell why pnpm build and nx build behave differently. On a small team, that onboarding cost goes straight onto your velocity.
Nx vs Turborepo 2026: the decision table
Pick based on team shape, not benchmarks. Both cache well enough that raw speed rarely decides it.
- 1 to 3 packages, solo dev: plain pnpm workspaces.
- 3 to 15 packages, 1 to 2 teams, deploying to Vercel or Netlify: Turborepo.
- 15+ projects, 3+ teams, CI over 10 minutes, or strict ownership rules: Nx 21 with Nx Cloud.
- Starting at 8 packages and expecting to grow: Turborepo now. Moving to Nx later with
nx initis a day's work, and going the other way is harder.
My recommendation: start every React + Vite monorepo on Turborepo. It's the cheapest decision to undo. Move to Nx when you hit the first boundary violation that reaches production, or when CI passes 10 minutes and caching alone stops fixing it. If you're still choosing what runs inside those tasks, our Vite vs Turbopack vs Rspack comparison covers the bundler side.
FAQs
Can I migrate from Turborepo to Nx without rewriting my packages?
Yes. Run pnpm dlx nx@latest init in the repo root and Nx picks up your existing package.json scripts and Vite configs as targets. You can keep turbo.json while you test, then delete it once the CI pipeline runs through Nx.
Does Nx 21 work with Biome instead of ESLint?
Mostly. Biome handles linting and formatting fine in an Nx workspace through a normal lint script. The catch is module boundary enforcement: it ships as an ESLint rule, so you'll need a small ESLint config just for @nx/enforce-module-boundaries if you want that feature.
Is Turborepo remote caching free?
Vercel's remote cache is free on Hobby and Pro plans, and you connect it with turbo login and turbo link. You can also self-host a compatible cache server on any S3-style storage if you can't send build artefacts to Vercel.
Which is faster for a Vite React monorepo, Nx or Turborepo?
On a warm cache they're effectively the same, because both skip unchanged work. Nx pulls ahead on large repos through finer-grained inputs and distributed execution on Nx Cloud. On small repos, Turborepo often feels faster because there are no plugin inference steps.
Do I need Nx Cloud to get value from Nx?
No. Local caching, the project graph, nx affected, generators and boundaries all work without it. Nx Cloud adds remote caching, distributed task execution and self-healing CI, and those mostly matter once CI times climb past 10 minutes.