Claude Code for Nx: Monorepo Build System with Caching and Generators — Claude Skills 360 Blog
Blog / DevOps / Claude Code for Nx: Monorepo Build System with Caching and Generators
DevOps

Claude Code for Nx: Monorepo Build System with Caching and Generators

Published: February 4, 2027
Read time: 9 min read
By: Claude Skills 360

Nx builds a project graph from your monorepo — understanding which packages depend on which — and uses this graph to determine the minimal set of projects affected by any change. nx affected --target=test runs tests only for changed projects and their dependents. Computation caching stores task outputs locally and in Nx Cloud, replaying them on cache hit without re-executing. Custom generators scaffold consistent code across the workspace with @nx/devkit. Task pipelines declare that build must complete before deploy, automatically ordering execution. Module Federation splits a Next.js or React app into independently deployed micro-frontends sharing modules at runtime. Claude Code generates Nx workspace configurations, project.json task definitions, custom generators, affected CI pipelines, and the module federation configurations for production Nx monorepos.

CLAUDE.md for Nx Projects

## Nx Stack
- Version: nx >= 20, @nx/js >= 20, @nx/react/@nx/next / @nx/node as needed
- Config: nx.json (workspace), project.json (per project) — not workspace.json
- Affected: nx affected --target=build --base=main — only changed + dependents
- Cache: nx.json targetDefaults inputs/outputs — invalidation fingerprinting
- Generators: nx generate @nx/react:library my-lib -- for scaffolding
- Plugins: @nx/eslint, @nx/jest, @nx/vite — integrated tooling
- CI: nx affected + Nx Cloud for distributed, remote caching

nx.json Configuration

{
  "$schema": "./node_modules/nx/schemas/nx-schema.json",
  "namedInputs": {
    "default": ["{projectRoot}/**/*", "sharedGlobals"],
    "production": [
      "default",
      "!{projectRoot}/**/*.spec.ts",
      "!{projectRoot}/**/*.stories.tsx",
      "!{projectRoot}/jest.config.ts"
    ],
    "sharedGlobals": ["{workspaceRoot}/tsconfig.base.json"]
  },
  "targetDefaults": {
    "build": {
      "inputs": ["production", "^production"],
      "outputs": ["{projectRoot}/dist"],
      "cache": true,
      "dependsOn": ["^build"]
    },
    "test": {
      "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"],
      "outputs": ["{projectRoot}/coverage"],
      "cache": true
    },
    "lint": {
      "inputs": ["default", "{workspaceRoot}/.eslintrc.json"],
      "cache": true
    }
  },
  "plugins": [
    { "plugin": "@nx/eslint/plugin" },
    { "plugin": "@nx/vite/plugin", "options": { "buildTargetName": "build" } }
  ],
  "nxCloudAccessToken": "${NX_CLOUD_ACCESS_TOKEN}"
}

Project.json Configuration

{
  "name": "orders-api",
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "apps/orders-api/src",
  "projectType": "application",
  "targets": {
    "build": {
      "executor": "@nx/webpack:webpack",
      "outputs": ["{options.outputPath}"],
      "defaultConfiguration": "production",
      "options": {
        "outputPath": "dist/apps/orders-api",
        "main": "apps/orders-api/src/main.ts",
        "tsConfig": "apps/orders-api/tsconfig.app.json",
        "target": "node",
        "compiler": "tsc"
      },
      "configurations": {
        "production": { "optimization": true, "sourceMap": false },
        "development": { "optimization": false, "sourceMap": true }
      }
    },
    "serve": {
      "executor": "@nx/js:node",
      "dependsOn": ["build"],
      "options": {
        "buildTarget": "orders-api:build:development",
        "watch": true
      }
    },
    "test": {
      "executor": "@nx/jest:jest",
      "outputs": ["{workspaceRoot}/coverage/apps/orders-api"],
      "options": {
        "jestConfig": "apps/orders-api/jest.config.ts"
      }
    },
    "deploy": {
      "executor": "nx:run-commands",
      "dependsOn": ["build"],
      "options": {
        "command": "fly deploy --config apps/orders-api/fly.toml",
        "cwd": "dist/apps/orders-api"
      }
    }
  },
  "tags": ["scope:backend", "type:app"]
}

Custom Generator

// tools/generators/feature/index.ts — custom Nx generator
import {
  Tree,
  formatFiles,
  generateFiles,
  joinPathFragments,
  names,
  readProjectConfiguration,
  updateProjectConfiguration,
} from "@nx/devkit"
import * as path from "path"

interface FeatureGeneratorSchema {
  name: string
  project: string
  directory?: string
}

export default async function (tree: Tree, options: FeatureGeneratorSchema) {
  const { name, project, directory } = options
  const projectConfig = readProjectConfiguration(tree, project)
  const projectRoot = projectConfig.sourceRoot || `${projectConfig.root}/src`

  const normalizedNames = names(name)
  const featureDir = joinPathFragments(
    projectRoot,
    directory ?? "features",
    normalizedNames.fileName
  )

  // Generate files from templates
  generateFiles(
    tree,
    path.join(__dirname, "files"),
    featureDir,
    {
      ...normalizedNames,
      projectName: project,
      tmpl: "",  // Strips .tmpl extension from template files
    }
  )

  // Add feature to project's test configuration
  updateProjectConfiguration(tree, project, {
    ...projectConfig,
    targets: {
      ...projectConfig.targets,
    },
  })

  await formatFiles(tree)

  return () => {
    console.log(`Feature ${name} generated in ${featureDir}`)
  }
}
// tools/generators/feature/files/__name__.component.tsx__tmpl__
import { useState, useCallback } from "react"

interface <%= className %>Props {
  // Define props
}

export function <%= className %>({ }: <%= className %>Props) {
  return (
    <div>
      <h2><%= className %></h2>
    </div>
  )
}

export default <%= className %>

Affected in CI

# .github/workflows/ci.yml — Nx affected pipeline
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  affected:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Full history for affected calculation

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm

      - run: npm ci

      - name: Set NX_BASE for main branch
        if: github.ref == 'refs/heads/main'
        run: echo "NX_BASE=HEAD~1" >> $GITHUB_ENV

      - name: Set NX_BASE for PR
        if: github.event_name == 'pull_request'
        run: echo "NX_BASE=origin/main" >> $GITHUB_ENV

      - name: Lint affected projects
        run: npx nx affected --target=lint --base=$NX_BASE --parallel=5
        env:
          NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}

      - name: Test affected projects
        run: npx nx affected --target=test --base=$NX_BASE --parallel=3 --coverage
        env:
          NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}

      - name: Build affected projects
        run: npx nx affected --target=build --base=$NX_BASE --parallel=3
        env:
          NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}

      - name: Show project graph
        run: npx nx graph --affected --base=$NX_BASE --file=dist/graph.html
        if: always()

Publishable Library Configuration

// libs/ui-components/project.json — publishable library
{
  "name": "ui-components",
  "projectType": "library",
  "sourceRoot": "libs/ui-components/src",
  "targets": {
    "build": {
      "executor": "@nx/vite:build",
      "outputs": ["{workspaceRoot}/dist/libs/ui-components"],
      "options": {
        "outputPath": "dist/libs/ui-components",
        "configFile": "libs/ui-components/vite.config.ts"
      }
    },
    "publish": {
      "executor": "nx:run-commands",
      "dependsOn": ["build"],
      "options": {
        "command": "npm publish --access public",
        "cwd": "dist/libs/ui-components"
      }
    }
  },
  "tags": ["scope:shared", "type:ui"]
}
// libs/ui-components/vite.config.ts — library Vite build
import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
import dts from "vite-plugin-dts"
import { resolve } from "path"

export default defineConfig({
  plugins: [
    react(),
    dts({ include: ["src"], rollupTypes: true }),
  ],
  build: {
    lib: {
      entry: resolve(__dirname, "src/index.ts"),
      formats: ["es", "cjs"],
      fileName: (format) => `index.${format === "es" ? "mjs" : "cjs"}`,
    },
    rollupOptions: {
      external: ["react", "react/jsx-runtime"],
    },
  },
})

For the Turborepo alternative monorepo build system with similar caching and affected-task primitives but a lighter configuration surface optimized for npm workspaces, see the monorepo guide for Turborepo pipeline configuration. For the Bazel build system that provides hermetic, reproducible builds with fine-grained dependency control for very large polyglot codebases, the advanced build guide covers Bazel workspace and rule configuration. The Claude Skills 360 bundle includes Nx skill sets covering workspace configuration, custom generators, and affected CI pipelines. Start with the free tier to try Nx monorepo configuration generation.

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free