Nx
Nx is a smart monorepo toolkit: project graph analysis, task running with computation caching, code generators, and optional module boundary rules. Like Turborepo, it orchestrates builds — unlike Turborepo, it also shapes how teams create and constrain packages.
Use Nx when structure and scale matter as much as cache hits. Use Turborepo when you mainly need a fast pipeline on an existing workspace. Use pnpm workspaces alone when the repo is small and CI is already fast.
info
| Concept | What it is | Why it matters |
|---|---|---|
| Project graph | Nodes = projects; edges = imports/deps | Enables affected & ordering |
| Targets | Named tasks (build, test, lint) | Consistent CLI across packages |
| Executors / plugins | How a target runs | Integrates Vite/Jest/etc. |
| Generators | Scaffold apps/libs/files | Org-standard structure |
| Cache | Local + optional remote | Skip unchanged work |
| 1 | # New Nx workspace |
| 2 | npx create-nx-workspace@latest acme --preset=apps |
| 3 | |
| 4 | # Add Nx to an existing pnpm/npm monorepo |
| 5 | npx nx@latest init |
| 6 | |
| 7 | # Common plugins |
| 8 | npx nx add @nx/vite |
| 9 | npx nx add @nx/next |
| 10 | npx nx add @nx/js |
| 11 | npx nx add @nx/eslint |
| 1 | { |
| 2 | "$schema": "./node_modules/nx/schemas/nx-schema.json", |
| 3 | "targetDefaults": { |
| 4 | "build": { |
| 5 | "dependsOn": ["^build"], |
| 6 | "inputs": ["production", "^production"], |
| 7 | "cache": true |
| 8 | }, |
| 9 | "test": { |
| 10 | "inputs": ["default", "^production"], |
| 11 | "cache": true |
| 12 | }, |
| 13 | "lint": { |
| 14 | "cache": true |
| 15 | } |
| 16 | }, |
| 17 | "namedInputs": { |
| 18 | "default": ["{projectRoot}/**/*", "sharedGlobals"], |
| 19 | "production": [ |
| 20 | "default", |
| 21 | "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)", |
| 22 | "!{projectRoot}/tsconfig.spec.json" |
| 23 | ], |
| 24 | "sharedGlobals": ["{workspaceRoot}/tsconfig.base.json"] |
| 25 | } |
| 26 | } |
Nx analyzes TypeScript imports and package dependencies to build a graph. Visualize it when debugging unexpected rebuilds or illegal imports.
| 1 | # Interactive graph in the browser |
| 2 | npx nx graph |
| 3 | |
| 4 | # JSON for tooling |
| 5 | npx nx graph --file=graph.json |
| 6 | |
| 7 | # Focus on one project |
| 8 | npx nx graph --focus=web |
note
nx affected runs targets only for projects impacted by a git diff — the killer feature for large monorepo CI.
| 1 | # Compare against main |
| 2 | npx nx affected -t build --base=origin/main --head=HEAD |
| 3 | |
| 4 | # Multiple targets |
| 5 | npx nx affected -t lint,test,build --base=origin/main |
| 6 | |
| 7 | # Print projects without running |
| 8 | npx nx show projects --affected --base=origin/main |
| 9 | |
| 10 | # CI example: shallow clones need fetch-depth: 0 for accurate base |
| Change type | Typically affects | CI impact |
|---|---|---|
| Leaf app only | That app (+ maybe e2e) | Minimal |
| Shared UI lib | All dependent apps | Medium–large |
| Root tooling (eslint) | Many projects | Broad — expect it |
Nx hashes inputs defined by namedInputsand target configuration. Remote caching (Nx Cloud or self-hosted) mirrors Turborepo's remote cache value proposition.
| 1 | # Run with cache |
| 2 | npx nx run web:build |
| 3 | |
| 4 | # Skip cache |
| 5 | npx nx run web:build --skip-nx-cache |
| 6 | |
| 7 | # Reset local cache |
| 8 | npx nx reset |
warning
Generators scaffold consistent apps and libraries. Publish workspace generators so feature teams cannot invent one-off folder layouts.
| 1 | # Official examples |
| 2 | npx nx g @nx/react:library ui-button --directory=libs/ui/button |
| 3 | npx nx g @nx/js:lib utils --bundler=tsc |
| 4 | npx nx g @nx/vite:app web |
| 5 | |
| 6 | # List available generators |
| 7 | npx nx list |
| 8 | npx nx list @nx/react |
best practice
Tag projects (scope:shared, type:feature, type:util) and enforce legal dependency directions with @nx/enforce-module-boundaries.
| 1 | import nxPlugin from "@nx/eslint-plugin"; |
| 2 | |
| 3 | export default [ |
| 4 | { |
| 5 | plugins: { "@nx": nxPlugin }, |
| 6 | rules: { |
| 7 | "@nx/enforce-module-boundaries": [ |
| 8 | "error", |
| 9 | { |
| 10 | enforceBuildableLibDependency: true, |
| 11 | allow: [], |
| 12 | depConstraints: [ |
| 13 | { |
| 14 | sourceTag: "type:app", |
| 15 | onlyDependOnProjectsWithTags: ["type:feature", "type:ui", "type:util"], |
| 16 | }, |
| 17 | { |
| 18 | sourceTag: "type:feature", |
| 19 | onlyDependOnProjectsWithTags: ["type:ui", "type:util", "type:data"], |
| 20 | }, |
| 21 | { |
| 22 | sourceTag: "type:util", |
| 23 | onlyDependOnProjectsWithTags: ["type:util"], |
| 24 | }, |
| 25 | ], |
| 26 | }, |
| 27 | ], |
| 28 | }, |
| 29 | }, |
| 30 | ]; |
| Rule idea | Why | Violation smell |
|---|---|---|
| utils cannot import features | Keep low-level packages pure | Circular domain coupling |
| apps cannot import other apps | Force shared code into libs | Copy-paste across apps |
| scope:admin ≠ scope:public | Security / product isolation | Admin code in public bundle |
| Choose | When | You accept |
|---|---|---|
| pnpm workspaces only | Few packages (about 5 or fewer), simple scripts | Manual ordering, no remote task cache |
| Turborepo | Need cache/pipelines fast with low ceremony | DIY structure & boundaries |
| Nx | Need affected, generators, boundaries, plugins | More concepts & config |
pro tip
| 1 | name: CI |
| 2 | on: [pull_request] |
| 3 | jobs: |
| 4 | main: |
| 5 | runs-on: ubuntu-latest |
| 6 | steps: |
| 7 | - uses: actions/checkout@v4 |
| 8 | with: |
| 9 | fetch-depth: 0 |
| 10 | - uses: pnpm/action-setup@v4 |
| 11 | - uses: actions/setup-node@v4 |
| 12 | with: |
| 13 | node-version: 22 |
| 14 | cache: pnpm |
| 15 | - run: pnpm install --frozen-lockfile |
| 16 | - run: pnpm nx affected -t lint,test,build --base=origin/main --parallel=3 |
- Start with nx init on an existing repo before a full rewrite.
- Introduce tags + boundaries gradually — warn first, then error.
- Standardize generators after the third copy-pasted library.
- Measure CI with affected + remote cache before buying more runners.
note
Community
Get help on Slack, Discord or VIP
Stuck on a guide? Join the community and ask.