Claude Code for Nx Monorepos: Generators, Affected Commands, and Module Boundaries — Claude Skills 360 Blog
Blog / Development / Claude Code for Nx Monorepos: Generators, Affected Commands, and Module Boundaries
Development

Claude Code for Nx Monorepos: Generators, Affected Commands, and Module Boundaries

Published: August 18, 2026
Read time: 8 min read
By: Claude Skills 360

Nx brings computation caching, affected command detection, and workspace generators to monorepos — dramatically reducing CI times as repositories grow. Claude Code works well with Nx: understanding project graph dependencies, generating new projects from custom generators, and respecting module boundary tags when writing cross-project imports.

This guide covers Nx monorepos with Claude Code: project structure, affected commands, custom generators, module boundaries, and CI optimization.

CLAUDE.md for Nx Workspaces

## Nx Monorepo Structure
- Apps: apps/{web,api,mobile} — deployable units, thin entry points
- Feature libs: libs/features/{orders,users,auth} — business logic + UI
- Shared libs: libs/shared/{ui,utils,api-client,types}

## Module boundary tags (nx.json enforce tags)
- app: can import from feature, shared, util
- feature: can import from shared, util — NOT from other features directly
- shared: can import from util only
- util: no imports from this workspace

## Commands
- nx affected:build — only rebuild changed projects + dependents
- nx affected:test -- --parallel=4 — test only affected
- nx graph — visualize dependency graph
- nx generate @nx/react:library libs/features/new-feature --tags=feature

Nx Project Setup

Show me the workspace structure for a full-stack Nx monorepo
with React frontend, Node API, and shared libraries.
apps/
  web/                          # React SPA
    src/app/App.tsx
    project.json
  api/                          # Express API
    src/main.ts
    project.json

libs/
  features/
    orders/                     # Orders feature (UI + business logic)
      src/
        lib/
          OrderList/
            OrderList.tsx
            OrderList.spec.tsx
          orders.store.ts
          orders.service.ts
        index.ts                # Public API — controls what's exported
      project.json
    auth/
      src/...
      project.json

  shared/
    ui/                         # Design system components
      src/lib/Button/...
      project.json
    types/                      # Shared TypeScript types
      src/lib/api.types.ts
      project.json
    api-client/                 # Generated API client
      src/...
      project.json

  util/
    testing/                    # Test utilities and fixtures
    formatting/                 # Date, currency, string formatters
// nx.json — enforce module boundaries
{
  "affected": {
    "defaultBase": "main"
  },
  "targetDefaults": {
    "build": {
      "cache": true,
      "dependsOn": ["^build"]
    },
    "test": {
      "cache": true
    },
    "lint": {
      "cache": true
    }
  },
  "namedInputs": {
    "production": [
      "default",
      "!{projectRoot}/**/*.spec.ts",
      "!{projectRoot}/src/test-setup.ts"
    ]
  }
}
// .eslintrc.json — module boundary enforcement
{
  "rules": {
    "@nx/enforce-module-boundaries": [
      "error",
      {
        "enforceBuildableLibDependency": true,
        "allow": [],
        "depConstraints": [
          {
            "sourceTag": "app",
            "onlyDependOnLibsWithTags": ["feature", "shared", "util"]
          },
          {
            "sourceTag": "feature",
            "onlyDependOnLibsWithTags": ["shared", "util"]
          },
          {
            "sourceTag": "shared",
            "onlyDependOnLibsWithTags": ["util"]
          },
          {
            "sourceTag": "util",
            "onlyDependOnLibsWithTags": ["util"]
          }
        ]
      }
    ]
  }
}

Custom Generators

Create a custom Nx generator for our feature libraries.
It should scaffold the standard structure we use:
store, service, component, and barrel exports.
// tools/generators/feature-lib/index.ts
import {
  Tree,
  formatFiles,
  generateFiles,
  names,
  installPackagesTask,
  addProjectConfiguration,
  getWorkspaceLayout,
  joinPathFragments,
} from '@nx/devkit';

interface FeatureLibSchema {
  name: string;
  directory?: string;
  withState?: boolean; // Include zustand store
}

export default async function featureLibGenerator(tree: Tree, schema: FeatureLibSchema) {
  const { name, directory, withState = true } = schema;
  const { libsDir } = getWorkspaceLayout(tree);
  const featureNames = names(name); // Generates camelCase, PascalCase, etc.

  const projectRoot = joinPathFragments(
    libsDir,
    'features',
    directory ?? name,
  );

  // Register project in workspace
  addProjectConfiguration(tree, `feature-${name}`, {
    root: projectRoot,
    sourceRoot: `${projectRoot}/src`,
    projectType: 'library',
    tags: ['feature'],
    targets: {
      build: { executor: '@nx/js:tsc' },
      test: { executor: '@nx/jest:jest' },
      lint: { executor: '@nx/eslint:lint' },
    },
  });

  // Generate files from templates
  generateFiles(
    tree,
    joinPathFragments(__dirname, 'files'), // Template directory
    projectRoot,
    {
      ...featureNames,
      withState,
      tmpl: '', // Removes .tmpl extension from template files
    },
  );

  await formatFiles(tree);
  return () => installPackagesTask(tree);
}
// tools/generators/feature-lib/files/src/lib/__name__.service.ts__tmpl__
import { apiClient } from '@myworkspace/shared/api-client';
import type { <%= PascalCase %>, Create<%= PascalCase %>Request } from '@myworkspace/shared/types';

export async function getAll(): Promise<<%= PascalCase %>[]> {
  return apiClient.get('/<%= name %>s');
}

export async function create(data: Create<%= PascalCase %>Request): Promise<<%= PascalCase %>> {
  return apiClient.post('/<%= name %>s', data);
}
// tools/generators/feature-lib/files/src/index.ts__tmpl__
// Public API — controls what the outside world can import
export { <%= PascalCase %>List } from './lib/<%= PascalCase %>List/<%= PascalCase %>List';
<% if (withState) { %>
export { use<%= PascalCase %>Store } from './lib/<%= name %>.store';
<% } %>
export * from './lib/<%= name %>.service';

Generate a new feature:

nx generate @myworkspace/tools:feature-lib orders --withState=true

This creates the entire feature scaffold in seconds with correct barrel exports, type imports from shared, and project registration.

Affected Commands for CI Optimization

Our CI runs all tests on every commit and takes 45 minutes.
How do we use nx affected to only test what changed?
# .github/workflows/ci.yml
name: CI

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

jobs:
  affected:
    runs-on: ubuntu-latest
    outputs:
      # Compute which projects are affected ONCE — share with jobs
      affected-apps: ${{ steps.nx.outputs.affected-apps }}
      affected-libs: ${{ steps.nx.outputs.affected-libs }}

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Need full history for base comparison

      - uses: nrwl/nx-set-shas@v4 # Sets NX_BASE and NX_HEAD
        with:
          main-branch-name: main

      - name: Get affected projects
        id: nx
        run: |
          echo "affected-apps=$(nx print-affected --type=app --select=projects 2>/dev/null | tr '\n' ',')" >> $GITHUB_OUTPUT

  test:
    needs: affected
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: nrwl/nx-set-shas@v4

      # Only test affected projects — skips unchanged code
      - run: npx nx affected -t test --parallel=4

      # Only lint affected
      - run: npx nx affected -t lint --parallel=4

  build:
    needs: affected
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: nrwl/nx-set-shas@v4

      # Build with cache — Nx remote cache hits from previous runs
      - run: npx nx affected -t build
        env:
          NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}

With Nx remote caching, builds that haven’t changed restore from cache rather than running. A full 45-minute CI run typically drops to under 5 minutes for a change that touches one feature library.

For Turborepo monorepos (alternative to Nx, simpler for frontend-only workspaces), see the Turborepo guide. For micro-frontend patterns where each team owns an independently deployed Nx app, see the microservices guide. The Claude Skills 360 bundle includes monorepo skill sets for Nx and Turborepo covering generators, caching, and large-scale refactoring. Start with the free tier to try monorepo code 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