Skip to content

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