Skip to main content
Technology & EngineeringMonorepo246 lines

Nx

Monorepo development with Nx including project graph, generators, executors, and computation caching

Quick Summary18 lines
You are an expert in Nx for managing monorepo projects, including project graphs, code generation, task execution, and distributed caching.

## Key Points

1. **Use tags and module boundaries** — Enforce architectural constraints so feature libs don't import from app-specific code.
2. **Keep libraries small and focused** — Many small libs give Nx a finer-grained dependency graph and better cache hit rates.
3. **Use `namedInputs`** — Define reusable input sets to keep target definitions DRY and cache keys accurate.
4. **Prefer `affected` in CI** — Run `nx affected -t test,build,lint` to skip unchanged projects entirely.
5. **Enable Nx Cloud for teams** — Distributed caching and task execution prevent redundant CI work across branches.
6. **Use generators for consistency** — Create workspace generators for common patterns (new service, new feature lib) to enforce conventions.
7. **Visualize with `nx graph`** — Regularly inspect the project graph to catch unintended dependencies early.
- **Circular dependencies** — Nx will detect these, but they often signal poor library boundaries. Extract shared code into a new lib.
- **Over-coupling with barrel exports** — An `index.ts` that re-exports everything creates implicit dependencies. Export only the public API.
- **Ignoring cache misses** — If caching seems broken, run `nx build my-app --verbose` and check which inputs changed.
- **Not setting `defaultBase`** — Affected commands compare against `defaultBase` in `nx.json`. Set it to your trunk branch (`main` or `master`).
- **Massive projects with no library extraction** — A single large app can't benefit from Nx's parallelism or caching. Break it into composable libraries.
skilldb get monorepo-skills/NxFull skill: 246 lines
Paste into your CLAUDE.md or agent config

Nx — Monorepo Management

You are an expert in Nx for managing monorepo projects, including project graphs, code generation, task execution, and distributed caching.

Core Philosophy

Overview

Nx is a build system with first-class monorepo support. It provides project graph analysis, computation caching, distributed task execution, code generators, and rich plugin ecosystem. Nx works with any language or framework but has deep integrations for JavaScript/TypeScript ecosystems including React, Angular, Node.js, and more.

Setup & Configuration

Creating a New Nx Workspace

# Create an integrated monorepo
npx create-nx-workspace@latest myorg --preset=ts

# Create with a specific framework
npx create-nx-workspace@latest myorg --preset=react-monorepo

# Add Nx to an existing monorepo
npx nx@latest init

nx.json Configuration

{
  "$schema": "./node_modules/nx/schemas/nx-schema.json",
  "namedInputs": {
    "default": ["{projectRoot}/**/*", "sharedGlobals"],
    "sharedGlobals": ["{workspaceRoot}/.github/workflows/*"],
    "production": [
      "default",
      "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)",
      "!{projectRoot}/tsconfig.spec.json",
      "!{projectRoot}/.eslintrc.json"
    ]
  },
  "targetDefaults": {
    "build": {
      "dependsOn": ["^build"],
      "inputs": ["production", "^production"],
      "cache": true
    },
    "test": {
      "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"],
      "cache": true
    },
    "lint": {
      "inputs": ["default", "{workspaceRoot}/.eslintrc.json"],
      "cache": true
    }
  },
  "defaultBase": "main"
}

Project Configuration (project.json)

{
  "name": "my-app",
  "sourceRoot": "apps/my-app/src",
  "projectType": "application",
  "targets": {
    "build": {
      "executor": "@nx/webpack:webpack",
      "outputs": ["{options.outputPath}"],
      "options": {
        "outputPath": "dist/apps/my-app",
        "main": "apps/my-app/src/main.ts",
        "tsConfig": "apps/my-app/tsconfig.app.json"
      },
      "configurations": {
        "production": {
          "optimization": true,
          "sourceMap": false
        }
      }
    },
    "serve": {
      "executor": "@nx/webpack:dev-server",
      "options": {
        "buildTarget": "my-app:build"
      }
    }
  }
}

Core Patterns

Running Tasks

# Run a target for a single project
nx build my-app

# Run a target for all projects
nx run-many -t build

# Run affected projects only (changes since base branch)
nx affected -t build

# Run with specific configuration
nx build my-app --configuration=production

# Visualize the project graph
nx graph

Code Generation

# Generate a new library
nx g @nx/js:library shared-utils --directory=libs/shared-utils

# Generate a new application
nx g @nx/react:application dashboard --directory=apps/dashboard

# Generate a component
nx g @nx/react:component button --project=ui-components

# Remove a project
nx g @nx/workspace:remove old-lib

Affected Commands

# See what's affected by current changes
nx affected:graph

# Test only affected projects
nx affected -t test --base=main --head=HEAD

# Lint affected projects
nx affected -t lint

Module Boundary Rules

In .eslintrc.json at workspace root:

{
  "overrides": [
    {
      "files": ["*.ts", "*.tsx"],
      "rules": {
        "@nx/enforce-module-boundaries": [
          "error",
          {
            "enforceBuildableLibDependency": true,
            "allow": [],
            "depConstraints": [
              {
                "sourceTag": "scope:app",
                "onlyDependOnLibsWithTags": ["scope:shared", "scope:feature"]
              },
              {
                "sourceTag": "scope:feature",
                "onlyDependOnLibsWithTags": ["scope:shared"]
              },
              {
                "sourceTag": "scope:shared",
                "onlyDependOnLibsWithTags": ["scope:shared"]
              }
            ]
          }
        ]
      }
    }
  ]
}

Nx Cloud / Remote Caching

# Connect to Nx Cloud
npx nx connect

# Or configure manually in nx.json
{
  "nxCloudAccessToken": "your-token"
}

Custom Executors

// libs/my-plugin/src/executors/custom/executor.ts
import { ExecutorContext } from '@nx/devkit';

export interface CustomExecutorOptions {
  outputPath: string;
}

export default async function runExecutor(
  options: CustomExecutorOptions,
  context: ExecutorContext
) {
  const projectName = context.projectName;
  console.log(`Running custom executor for ${projectName}`);
  // Your build logic here
  return { success: true };
}

Best Practices

  1. Use tags and module boundaries — Enforce architectural constraints so feature libs don't import from app-specific code.
  2. Keep libraries small and focused — Many small libs give Nx a finer-grained dependency graph and better cache hit rates.
  3. Use namedInputs — Define reusable input sets to keep target definitions DRY and cache keys accurate.
  4. Prefer affected in CI — Run nx affected -t test,build,lint to skip unchanged projects entirely.
  5. Enable Nx Cloud for teams — Distributed caching and task execution prevent redundant CI work across branches.
  6. Use generators for consistency — Create workspace generators for common patterns (new service, new feature lib) to enforce conventions.
  7. Visualize with nx graph — Regularly inspect the project graph to catch unintended dependencies early.

Common Pitfalls

  • Circular dependencies — Nx will detect these, but they often signal poor library boundaries. Extract shared code into a new lib.
  • Over-coupling with barrel exports — An index.ts that re-exports everything creates implicit dependencies. Export only the public API.
  • Ignoring cache misses — If caching seems broken, run nx build my-app --verbose and check which inputs changed.
  • Not setting defaultBase — Affected commands compare against defaultBase in nx.json. Set it to your trunk branch (main or master).
  • Massive projects with no library extraction — A single large app can't benefit from Nx's parallelism or caching. Break it into composable libraries.

Anti-Patterns

Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.

Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.

Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.

Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.

Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.

Install this skill directly: skilldb add monorepo-skills

Get CLI access →