콘텐츠로 이동

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

CommandDescription
nx run my-app:buildRun a target on a project
nx build my-appShorthand for run
nx serve my-appServe a project
nx test my-appTest a project
nx lint my-appLint a project
nx run-many -t buildRun build across all projects
nx run-many -t build test lintRun multiple targets on all
nx run-many -t build --projects=app1,app2Run on specific projects
nx affected -t buildRun build only on affected projects
nx affected -t testRun tests on affected projects
nx affected -t lint --base=mainAffected since main branch
nx graphOpen project graph in browser
nx graph --file=graph.jsonExport graph as JSON
nx show projectsList all projects
nx show project my-appShow project details
nx generate @nx/react:appRun a code generator
nx g @nx/react:componentGenerate a component
nx listList installed plugins
nx list @nx/reactList generators in a plugin
nx resetClear local Nx cache
nx reportShow workspace health report
nx migrate latestUpdate Nx to latest version
nx connectConnect 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.