Nx
Nx is a powerful monorepo toolkit that provides a project graph, intelligent caching, code generators (workspace schematics), and distributed task execution. It supports React, Angular, Next.js, Node, and many other frameworks through a plugin system, making it suitable for large enterprise monorepos.
Installation
# Create a new Nx workspace
npx create-nx-workspace@latest my-workspace
pnpm dlx create-nx-workspace@latest my-workspace
# Choose preset interactively, or pass via flag
npx create-nx-workspace@latest my-workspace --preset=ts
npx create-nx-workspace@latest my-workspace --preset=react
npx create-nx-workspace@latest my-workspace --preset=next
npx create-nx-workspace@latest my-workspace --preset=node
# Add Nx to an existing monorepo
npx nx@latest init
# Install Nx CLI globally
npm install -g nx
pnpm add -g nx
# Check version
nx --version
# Upgrade Nx and all plugins
npx nx migrate latest
npx nx migrate --run-migrations # apply pending migrations
Adding Plugins
# Add a plugin (installs package + configures workspace)
nx add @nx/react
nx add @nx/next
nx add @nx/node
nx add @nx/angular
nx add @nx/fastify
nx add @nx/vite
nx add @nx/storybook
# Or install manually
pnpm add -D @nx/react
Configuration
nx.json — Workspace 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": ["{workspaceRoot}/babel.config.json"]
},
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"inputs": ["production", "^production"],
"outputs": ["{options.outputPath}"],
"cache": true
},
"test": {
"inputs": ["default", "^production"],
"cache": true
},
"lint": {
"inputs": ["default", "{workspaceRoot}/.eslintrc.json"],
"cache": true
}
},
"nxCloudAccessToken": "your-token-here",
"parallel": 3,
"cacheDirectory": ".nx/cache"
}
project.json — Per-Project Configuration
{
"name": "my-app",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/my-app/src",
"projectType": "application",
"tags": ["scope:app", "type:ui"],
"targets": {
"build": {
"executor": "@nx/vite:build",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/my-app"
},
"configurations": {
"production": {
"mode": "production"
},
"development": {
"mode": "development"
}
}
},
"serve": {
"executor": "@nx/vite:dev-server",
"options": {
"buildTarget": "my-app:build"
}
},
"test": {
"executor": "@nx/vite:test",
"options": {
"coverage": false
}
},
"lint": {
"executor": "@nx/eslint:lint",
"options": {
"lintFilePatterns": ["apps/my-app/**/*.ts"]
}
}
}
}
Core Commands
| Command | Description |
|---|---|
nx run my-app:build | Run a target on a project |
nx build my-app | Shorthand for run |
nx serve my-app | Serve a project |
nx test my-app | Test a project |
nx lint my-app | Lint a project |
nx run-many -t build | Run build across all projects |
nx run-many -t build test lint | Run multiple targets on all |
nx run-many -t build --projects=app1,app2 | Run on specific projects |
nx affected -t build | Run build only on affected projects |
nx affected -t test | Run tests on affected projects |
nx affected -t lint --base=main | Affected since main branch |
nx graph | Open project graph in browser |
nx graph --file=graph.json | Export graph as JSON |
nx show projects | List all projects |
nx show project my-app | Show project details |
nx generate @nx/react:app | Run a code generator |
nx g @nx/react:component | Generate a component |
nx list | List installed plugins |
nx list @nx/react | List generators in a plugin |
nx reset | Clear local Nx cache |
nx report | Show workspace health report |
nx migrate latest | Update Nx to latest version |
nx connect | Connect to Nx Cloud |
Advanced Usage
Project Graph and Tags
// Enforce module boundaries via tags in project.json:
// "tags": ["scope:shared", "type:util"]
// .eslintrc.json — enforce boundaries
{
"rules": {
"@nx/enforce-module-boundaries": [
"error",
{
"enforceBuildableLibDependency": true,
"depConstraints": [
{
"sourceTag": "scope:app",
"onlyDependOnLibsWithTags": ["scope:shared", "scope:app"]
},
{
"sourceTag": "type:ui",
"onlyDependOnLibsWithTags": ["type:ui", "type:util", "type:data"]
},
{
"sourceTag": "type:data",
"notDependOnLibsWithTags": ["type:ui"]
}
]
}
]
}
}
Affected Commands
# Run on affected projects vs base branch (default: main)
nx affected -t build
nx affected -t test --base=origin/main --head=HEAD
# Affected in the last commit
nx affected -t lint --base=HEAD~1
# Show which projects are affected (dry run)
nx affected:apps # affected applications
nx affected:libs # affected libraries
nx show projects --affected
# In CI — compare PR head vs base
nx affected -t build --base=${{ github.base_ref }} --head=HEAD
Nx Cloud (Remote Caching & CI Distribution)
# Connect to Nx Cloud
nx connect
# Or add access token to nx.json:
# "nxCloudAccessToken": "your-token"
# Distributed Task Execution (DTE) in CI
# Splits tasks across multiple agents automatically
# Configure in CI with nx-cloud agents
Workspace Generators
# List available generators
nx list @nx/react
# Generate a new React app
nx g @nx/react:app my-app --directory=apps/my-app --style=css --bundler=vite
# Generate a shared library
nx g @nx/react:lib my-ui --directory=packages/my-ui --publishable --importPath=@myorg/my-ui
# Generate a component in a library
nx g @nx/react:component Button --project=my-ui --export
# Generate a Node.js library
nx g @nx/node:lib data-access --directory=packages/data-access
# Custom workspace generators
nx g @nx/workspace:move --project=my-app --destination=apps/renamed-app
nx g @nx/workspace:remove --project=old-lib
# Dry run to preview
nx g @nx/react:app my-app --dry-run
CI with GitHub Actions
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # needed for affected
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
# Set the base for affected commands on PRs
- name: Derive affected base
uses: nrwl/nx-set-shas@v4
# Run affected tasks (uses remote cache via Nx Cloud)
- run: pnpm nx affected -t lint typecheck build test --parallel=3
Nx Agents (Distributed CI)
# Split tasks across multiple CI machines
- name: Start Nx Agents
run: npx nx-cloud start-ci-run --distribute-on="3 linux-medium-js"
- run: pnpm nx affected -t build test lint
- name: Stop Agents
if: always()
run: npx nx-cloud stop-all-agents
Buildable Libraries
# Generate a publishable library (produces dist/ with package.json)
nx g @nx/js:lib my-lib --publishable --importPath=@myorg/my-lib
# Build the library (required before apps that depend on it)
nx build my-lib
# Run typecheck across workspace
nx run-many -t typecheck --all
Common Workflows
Adding a New App to an Existing Workspace
# Add the plugin if not installed
nx add @nx/next
# Generate the app
nx g @nx/next:app dashboard --directory=apps/dashboard
# Add a shared library
nx g @nx/js:lib shared-utils --directory=libs/shared-utils
# Import the library in the app
# import { helper } from '@myorg/shared-utils'
Visualizing the Project Graph
# Open interactive graph in browser
nx graph
# Focus on one project and its dependencies
nx graph --focus=my-app
# Show only affected projects
nx graph --affected
# Export as JSON for custom tooling
nx graph --file=project-graph.json
Resetting and Debugging Cache
# Clear local cache
nx reset
# Skip cache for a single run
nx run my-app:build --skip-nx-cache
# See what's cached
ls .nx/cache/
# Verbose output
NX_VERBOSE_LOGGING=true nx run my-app:build
Tips and Best Practices
Use tags to enforce boundaries — Define scope: and type: tags on every project and configure @nx/enforce-module-boundaries in ESLint. This prevents architectural violations from creeping in over time.
Prefer libraries over large apps — Extract logic into small, focused libraries. Apps should be thin shells that compose libraries. This maximizes cache hits since most builds will be library builds.
Use namedInputs in nx.json — Define production inputs that exclude test files. This way, changing a spec file doesn’t bust the build cache.
Enable Nx Cloud from day one — Remote caching is free for open source and small teams. CI runs become dramatically faster once the first build is cached. Connect with nx connect.
Use affected in CI, not run-many — Running nx affected -t build instead of nx run-many -t build ensures you only build what changed in a PR. On the main branch, run everything.
Generate with --dry-run first — Always preview generators before running them. nx g @nx/react:app my-app --dry-run shows every file that will be created or modified.
Use Nx Agents for large monorepos — If CI takes more than 10 minutes, Nx Agents can distribute tasks across multiple machines. It’s the fastest way to scale CI without manual matrix configuration.
Keep project.json executors explicit — Rather than relying on inferred targets, declare targets explicitly in project.json. This makes the build configuration discoverable and easier to override.