Nx Reference: Workspace Setup, Caching, Project Graph, Generators & Nx Cloud CI
Nx is a monorepo build system. The core value is computation caching — Nx hashes inputs (source files, env vars, dependencies) for each task and replays the cached output if nothing changed. With remote cache (Nx Cloud or self-hosted), that cache is shared across your CI and your team’s machines. The Nx vs Turborepo question: Nx has better first-class support for Angular/React/Next.js/Node via plugins, a visual project graph, and more built-in code generators. Turborepo is simpler and pipeline-focused. For large multi-framework repos, Nx wins.
1. Setup & Workspace
Create workspace, add apps/libs, project.json, and nx.json config
# Create new Nx workspace:
npx create-nx-workspace@latest my-org --preset=ts # TypeScript monorepo
# Other presets: react, next, angular, node, express, nest, empty
# Add to existing monorepo:
npx nx@latest init # detects npm workspaces, adds nx.json
# Add apps and libraries:
nx g @nx/react:app my-app # React app
nx g @nx/next:app web # Next.js app
nx g @nx/node:app api # Node.js app
nx g @nx/js:lib utils # Shared TypeScript library
nx g @nx/react:lib ui # React component library
# Workspace structure:
# apps/my-app/ — application (deployable)
# apps/api/ — application
# libs/utils/ — library (shared code)
# libs/ui/ — library (shared components)
# nx.json — workspace configuration
# project.json — per-project tasks
# project.json (apps/my-app/project.json):
{
"name": "my-app",
"targets": {
"build": {
"executor": "@nx/vite:build",
"options": { "outputPath": "dist/apps/my-app" }
},
"serve": { "executor": "@nx/vite:dev-server" },
"test": { "executor": "@nx/vitest:vitest" },
"lint": { "executor": "@nx/eslint:lint" }
}
}
2. Running Tasks & Caching
nx run, nx run-many, –affected, cache configuration, and what gets hashed
# Run a task for one project:
nx run my-app:build
nx run my-app:test
nx run my-app:lint
# Shorthand (project:target):
nx build my-app
nx test my-app
nx serve my-app # starts dev server
# Run a task across all projects:
nx run-many -t build
nx run-many -t build test lint # multiple targets in parallel
# Run only affected projects (changed vs main branch):
nx affected -t build
nx affected -t test --base=main --head=HEAD
# View what's cached vs what will run:
nx show project my-app --web # opens project detail in browser
nx graph # visual dependency graph in browser
# Cache is stored in .nx/cache/ by default.
# Cache keys include: source files, package.json, tsconfig, env vars
# nx.json — configure caching:
{
"targetDefaults": {
"build": {
"cache": true,
"inputs": ["default", "^default"], // this project + its deps
"outputs": ["{projectRoot}/dist"] // what to cache
},
"test": {
"cache": true,
"inputs": ["default", "^default", "{workspaceRoot}/jest.preset.js"]
}
}
}
# Clear cache:
nx reset # clears .nx/cache and Nx daemon state
3. Project Graph & Boundaries
Dependency graph, nx graph, module boundary rules, and tags
# View the project graph:
nx graph # browser UI showing all deps
nx graph --focus=my-app # zoom in to one project
nx graph --exclude=e2e # hide e2e projects
# Nx understands imports:
// apps/my-app/src/app.tsx imports from libs/ui → adds edge to graph
import { Button } from "@my-org/ui";
// libs/ui/src/index.ts exports Button
# Module boundary rules (.eslintrc.json):
# Prevent circular deps, enforce feature/lib/app layers
{
"plugins": ["@nx/eslint-plugin"],
"rules": {
"@nx/enforce-module-boundaries": ["error", {
"enforceBuildableLibDependency": true,
"allow": [],
"depConstraints": [
{ "sourceTag": "type:app", "onlyDependOnLibsWithTags": ["type:feature", "type:ui", "type:util"] },
{ "sourceTag": "type:feature", "onlyDependOnLibsWithTags": ["type:ui", "type:util"] },
{ "sourceTag": "type:ui", "onlyDependOnLibsWithTags": ["type:util"] }
]
}]
}
}
# Set tags in project.json:
{ "tags": ["type:feature", "scope:cart"] }
# nx affected works from the graph:
# Changed libs/utils → affected detects all apps + libs that import it
nx affected --graph # visualise what's affected by current changes
4. Generators & Executors
Code generators (nx g), custom generators, executors, and plugin ecosystem
# Built-in generators from plugins:
nx g @nx/react:component Button --project=ui --export
nx g @nx/react:hook useAuth --project=auth
nx g @nx/js:lib data-access --directory=libs/shared
# Dry run first (see what files will be created):
nx g @nx/react:app dashboard --dry-run
# Generate with prompts:
nx g @nx/react:app # interactive prompts for name, style, etc.
# Custom generator (in tools/generators/my-gen/):
# generators.json:
{
"generators": {
"my-gen": { "factory": "./tools/generators/my-gen/index.ts", "schema": "./schema.json" }
}
}
# tools/generators/my-gen/index.ts:
import { Tree, formatFiles, generateFiles } from "@nx/devkit";
export default async function(tree: Tree, options: { name: string }) {
generateFiles(tree, path.join(__dirname, "files"), `libs/${options.name}`, options);
await formatFiles(tree);
}
# Run custom generator:
nx g my-gen --name=auth
# Popular plugins:
# @nx/react, @nx/next, @nx/angular — frontend frameworks
# @nx/node, @nx/nest, @nx/express — backend frameworks
# @nx/js, @nx/eslint, @nx/jest, @nx/vitest, @nx/playwright, @nx/storybook
# @nx/docker — Docker build targets
# @nx/gradle, @nx/go — polyglot support
5. Nx Cloud & CI
Remote caching, distributed task execution, and CI pipeline setup
# Nx Cloud — remote cache + distributed task execution:
# nx.json:
{
"nxCloudAccessToken": "your-token", # free tier: 500 hours/month
"tasksRunnerOptions": {
"default": {
"runner": "nx-cloud",
"options": { "accessToken": "your-token", "cacheableOperations": ["build","test","lint"] }
}
}
}
# Self-hosted remote cache (without Nx Cloud):
# Use @nx/powerpack-s3-cache or Verdaccio + custom remote cache
# GitHub Actions — affected builds only:
# .github/workflows/ci.yml:
# - name: Install NX SHAs
# uses: nrwl/nx-set-shas@v4 # sets NX_BASE and NX_HEAD from last successful CI run
# - run: nx affected -t build --parallel=3
# - run: nx affected -t test --parallel=3
# - run: nx affected -t lint --parallel=3
# Distribute tasks across agents (DTE):
# Instead of running all tasks on one machine, split across workers:
# - run: npx nx-cloud start-ci-run --distribute-on="3 linux-medium-js"
# - run: nx affected -t build test lint
# - run: npx nx-cloud stop-all-agents
# Tag-based deployment:
nx run-many -t deploy --projects=tag:type:app # deploy only app projects
# View CI history on nx.cloud — see cache hit rate, task duration, failures
# 80%+ cache hit rate on CI is a healthy target
Track Node.js and monorepo tooling releases at ReleaseRun. Related: Turborepo Reference | TypeScript Reference | Vite Reference
🔍 Free tool: npm Package Health Checker — check @nx/core and Nx plugin packages for known CVEs and active maintenance.
Founded
2023 in London, UK
Contact
hello@releaserun.com