TL;DR:
- Nx adds computation caching, task orchestration, and code generators to TypeScript monorepos — build and test only what changed, not the whole repo
nx affectedis the biggest immediate CI win: run lint, test, and build only for projects that have been touched by a PR- Nx Agents distributes CI task execution across multiple machines without any infrastructure setup on your part
If your team has outgrown a single-package repository, or you’re consolidating multiple repos that share code, a monorepo with a proper build system makes the difference between “our CI takes 20 minutes” and “our CI takes 4 minutes”. Nx is the most fully featured TypeScript monorepo tool available — more opinionated than Turborepo, with more built-in generators and a broader plugin ecosystem, and with better Angular support specifically.
Setting Up Nx
You can add Nx to an existing project or start fresh:
# New monorepo
npx create-nx-workspace@latest my-workspace
# Add Nx to an existing repo
npx nx@latest init
The init command inspects your project structure and configures Nx without requiring you to rewrite your existing setup. It adds nx.json for workspace configuration and project.json files (or reads from package.json) for each project.
The workspace structure Nx works best with:
my-workspace/
├── apps/
│ ├── web-app/ # Next.js or React app
│ └── api/ # Node.js API
├── libs/
│ ├── ui/ # Shared component library
│ ├── utils/ # Shared utilities
│ └── data-access/ # API clients, data fetching
├── nx.json
└── package.json
Separating apps/ (deployable units) from libs/ (shared code) is the Nx convention. Projects in libs/ never deploy directly; they’re imported by apps.
Computation Caching
The most impactful Nx feature for CI time. When you run nx build web-app, Nx hashes the inputs: source files, configuration, environment variables, and the task definition itself. If those hashes match a previous run, Nx replays the cached output instead of executing the build.
# First run: builds normally
nx build web-app
# > nx run web-app:build 3.2s
# Second run with no changes: replays cache
nx build web-app
# > nx run web-app:build [local cache] 0.1s
Local cache (the default) helps individual developers. Nx Replay extends this to a shared remote cache that all CI runs share — one CI run builds and caches; subsequent runs on branches with similar unchanged code don’t rebuild. Remote cache is available on Nx Cloud’s free tier.
Configuring remote caching:
nx connect # Links to Nx Cloud, adds cache configuration
That’s the entire setup. Nx Cloud handles the cache storage and distribution.
nx affected: CI That Scales with Repo Size
The second major feature for CI. In a monorepo with 40 projects, you don’t want to test all 40 projects when a PR touches only 2 of them. nx affected computes the project dependency graph and runs tasks only on projects that the changed files affect — including all downstream dependents.
# In CI, compare against the base branch
nx affected -t test --base=origin/main
nx affected -t build --base=origin/main
nx affected -t lint --base=origin/main
If libs/utils changes, nx affected will include libs/utils AND any app or library that imports from it. If apps/web-app changes but nothing else, only web-app is included.
This scales well. As your monorepo grows, CI time stays relatively constant because you’re always testing only the code relevant to the PR, not the accumulated total.
Visualise the project graph at any time:
nx graph
This opens an interactive dependency graph showing how your projects relate. Useful for understanding the blast radius of changes.
Code Generators
Nx includes generators for creating new projects and components that follow your team’s conventions. Instead of copy-pasting a project structure, you run:
# Generate a new React library
nx g @nx/react:library ui-components
# Generate a Next.js app
nx g @nx/next:app marketing-site
# Generate a component inside a library
nx g @nx/react:component Button --project=ui-components
The generators create the files, update tsconfig.json references, add the project to the graph, and set up the default tasks (build, test, lint). Your team gets consistent structure without a wiki page explaining how to set up a new package.
Custom generators let you encode your own conventions: a generator that creates a new feature module with your specific folder structure, test setup, and boilerplate.
Task Orchestration and Dependencies
Nx lets you express dependencies between tasks so they run in the right order. In nx.json:
{
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"cache": true
},
"test": {
"dependsOn": ["build"],
"cache": true
}
}
}
The ^build syntax means “build all dependencies first”. So if web-app depends on ui-components, nx build web-app automatically builds ui-components first. You don’t need to manage this order manually.
Nx Agents for Distributed CI
Once you have a large enough repo, even nx affected on a single machine can be slow. Nx Agents distributes task execution across multiple CI machines automatically.
In your CI configuration (GitHub Actions, GitLab CI, etc.):
# GitHub Actions example
- uses: nrwl/cx-branch-e2e-tests@v1
with:
number-of-agents: 5 # Spin up 5 parallel machines
- run: nx affected -t test build lint --parallel=3
Nx automatically distributes the affected tasks across the 5 agents, managing dependencies correctly so tasks only run after their prerequisites are complete. You get the equivalent of 5x parallelism without managing job orchestration yourself.
Nx Agents is part of Nx Cloud and free for a meaningful amount of usage.
Nx vs Turborepo
Both tools address the same core problem. The key differences: Nx has broader plugin support (especially Angular, which Turborepo doesn’t support at all), built-in code generators, and more sophisticated project graph analysis. Turborepo has a simpler configuration model and slightly faster cold starts for smaller repos, and is a better fit for repos that only use standard package.json scripts without wanting Nx’s conventions.
For teams with Angular projects, Nx is the clear choice — it was built by the team that maintains Angular tooling. For pure React or Next.js monorepos, either works well; pick based on whether you want generators (Nx) or minimal configuration (Turborepo).
If you’re on Turborepo today and want to migrate, nx init can add Nx to a Turborepo-structured repo incrementally.
Where to Start
The fastest path to value: add Nx to an existing repo, connect remote caching, and update your CI to use nx affected. That alone will visibly reduce CI time for a team with more than three or four packages. Code generators and the plugin ecosystem are features you grow into as the monorepo matures.
npx nx@latest init # Add Nx
nx connect # Connect remote cache
nx graph # Understand your project structure
The project graph visualisation alone is worth the 10 minutes of setup — it’s often the first time teams can see how their monorepo’s dependencies actually flow.