shared-monorepo-nx · git:20260316.063411a · 2026-03-16 · sha256 d82f3b92fc6ad7e9
shared-monorepo-nx git:20260316.063411aA
Immutable. This exact content is served forever at /api/v1/blob/d82f3b92fc6ad7e9.
---
name: shared-monorepo-nx
description: Nx monorepo build system — workspace configuration, project graph, task pipelines, caching, generators, plugins, and release management
---
# Monorepo Orchestration with Nx
> **Quick Guide:** Nx 22 for monorepo orchestration and build intelligence. Project graph for dependency analysis. Task pipelines with topological ordering and `dependsOn`. Local computation caching + Nx Cloud remote caching for massive speed gains. Inferred tasks (Project Crystal) auto-detect targets from tool config files. `nx affected` runs only what changed. `nx release` for versioning, changelogs, and publishing. Generators scaffold code, executors run tasks.
---
<critical_requirements>
## CRITICAL: Before Using This Skill
> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)
**(You MUST enable caching with `"cache": true` on cacheable targets — builds, tests, linting — and set `"cache": false` or omit for side-effect tasks like `serve`)**
**(You MUST define `dependsOn: ["^build"]` in targetDefaults for build tasks to ensure topological ordering across the project graph)**
**(You MUST declare `inputs` and `outputs` for cached targets so Nx knows what to hash and what to restore)**
**(You MUST use inferred tasks (Project Crystal) as the default — only add `project.json` targets when overriding inferred configuration)**
**(You MUST use `nx affected -t <target>` in CI to only run tasks for changed projects and their dependents)**
</critical_requirements>
---
**Auto-detection:** Nx workspace, nx.json, project.json, nx generate, nx affected, nx graph, nx release, @nx/ plugins, Nx Cloud, inferred tasks, Project Crystal, nx migrate, targetDefaults, namedInputs, nx run-many, nx serve
**When to use:**
- Setting up a new Nx monorepo or adding Nx to an existing repo
- Configuring task pipelines, caching, and dependency ordering in nx.json
- Generating projects, libraries, and components with Nx generators
- Running affected commands to optimize CI builds
- Configuring Nx Cloud for remote caching and distributed task execution
- Managing releases with `nx release` (versioning, changelogs, publishing)
- Setting up module federation for micro-frontend architectures
- Migrating between Nx versions with `nx migrate`
**When NOT to use:**
- Single application with no shared libraries (standard build tools suffice)
- Projects already using Turborepo (do not mix monorepo orchestrators)
- Very small projects where Nx setup overhead exceeds benefits
- When all you need is `npm workspaces` without task orchestration
**Key patterns covered:**
- Workspace setup and nx.json configuration
- Task pipelines with `targetDefaults` and `dependsOn`
- Local + remote caching strategies
- Inferred tasks (Project Crystal) and plugin system
- Affected commands and project graph
- Generators and executors
- Release management (`nx release`)
- Module federation for micro-frontends
**Detailed Resources:**
- For code examples, see [examples/nx.md](examples/nx.md) (always start here)
- For CLI reference and decision frameworks, see [reference.md](reference.md)
---
<philosophy>
## Philosophy
Nx is a build intelligence platform for monorepos. Unlike simple task runners, Nx understands the structure of your codebase through the **project graph** — a directed acyclic graph of projects and their dependencies. This graph enables intelligent task scheduling, fine-grained caching, and affected analysis.
Nx's core value proposition: **never run a task that has already been computed, and never run more tasks than necessary.**
**Key principles:**
- **Project graph first** — Nx analyzes imports, configuration, and dependency relationships to build a graph of your workspace. Every feature (caching, affected, task pipelines) builds on this graph.
- **Inferred configuration** — Since Project Crystal (Nx 18+), plugins auto-detect tasks from tool configs (vite.config.ts, jest.config.ts, etc.), dramatically reducing boilerplate.
- **Computation caching** — Every task result is cached by default. Cache keys are computed from file inputs, environment, and dependency graph position.
- **Affected analysis** — `nx affected` uses git diff + project graph to determine the minimum set of projects impacted by a change.
**When to use Nx:**
- Monorepos with multiple apps sharing libraries
- Teams needing remote cache sharing across developers and CI
- Large codebases where build/test times are a bottleneck
- Projects with complex task dependency chains requiring topological ordering
- Organizations wanting enforced module boundaries between teams
**When NOT to use Nx:**
- Single-app projects with no shared code (Vite/esbuild directly)
- Polyrepo setups where repos are intentionally independent
- Projects already using Turborepo (pick one orchestrator)
- Prototypes or very small projects where setup cost exceeds benefit
</philosophy>
---
<patterns>
## Core Patterns
### Pattern 1: Workspace Setup and nx.json Configuration
The `nx.json` file is the central configuration for task behavior, caching, plugins, and workspace-wide defaults.
#### Minimal nx.json
```json
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"defaultBase": "main",
"namedInputs": {
"default": ["{projectRoot}/**/*", "sharedGlobals"],
"production": [
"default",
"!{projectRoot}/**/*.spec.ts",
"!{projectRoot}/**/*.test.ts",
"!{projectRoot}/tsconfig.spec.json",
"!{projectRoot}/.eslintrc.json"
],
"sharedGlobals": ["{workspaceRoot}/.github/workflows/*"]
},
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"inputs": ["production", "^production"],
"outputs": ["{projectRoot}/dist"],
"cache": true
},
"test": {
"inputs": ["default", "^production"],
"cache": true
},
"lint": {
"inputs": [
"default",
"{workspaceRoot}/.eslintrc.json",
"{workspaceRoot}/eslint.config.js"
],
"cache": true
}
},
"plugins": [
{
"plugin": "@nx/vite/plugin",
"options": { "buildTargetName": "build", "testTargetName": "test" }
},
{
"plugin": "@nx/eslint/plugin",
"options": { "targetName": "lint" }
}
]
}
```
**Why good:** `namedInputs` define reusable file sets so test files do not invalidate build caches, `targetDefaults` set global task behavior (caching, ordering) without repeating per project, `dependsOn: ["^build"]` enforces topological build ordering, plugins with inferred tasks eliminate per-project `project.json` boilerplate
```json
{
"targetDefaults": {
"build": {
"outputs": ["dist/**"]
}
}
}
```
**Why bad:** Missing `dependsOn` breaks topological ordering (packages may build before their dependencies), missing `inputs` means Nx cannot properly detect which changes invalidate the cache, missing `cache: true` disables caching entirely for this target, no `namedInputs` to exclude test files from build cache keys
---
### Pattern 2: Task Pipelines and Dependency Ordering
Task pipelines define execution order using the `dependsOn` property. The `^` prefix means "run this target on dependencies first" (topological ordering).
#### dependsOn Syntax
```json
{
"targetDefaults": {
"build": {
"dependsOn": ["^build"]
},
"test": {
"dependsOn": ["build"]
},
"e2e": {
"dependsOn": [
{
"target": "serve",
"params": "ignore"
}
]
},
"serve": {
"continuous": true,
"cache": false
}
}
}
```
**Why good:** `^build` runs dependency builds first (topological), `"dependsOn": ["build"]` runs same-project build before test, `continuous: true` marks long-running tasks (Nx 21+) so dependents do not wait for exit, `params: "ignore"` prevents parameter forwarding to dependencies
```json
{
"targetDefaults": {
"build": {},
"test": {},
"e2e": {
"dependsOn": ["serve"]
},
"serve": {}
}
}
```
**Why bad:** Build has no `dependsOn: ["^build"]` so dependency packages may not build first, test has no dependency on build so it may run against stale artifacts, serve is not marked `continuous: true` so e2e waits forever for it to exit, no caching configuration
---
### Pattern 3: Computation Caching (Local + Remote)
Nx caches task results locally by default. When inputs have not changed, cached outputs are restored instantly. Nx Cloud extends this with remote caching shared across the team.
#### Cache Configuration
```json
{
"namedInputs": {
"default": ["{projectRoot}/**/*", "sharedGlobals"],
"production": [
"default",
"!{projectRoot}/**/*.spec.ts",
"!{projectRoot}/**/*.test.ts"
],
"sharedGlobals": ["{workspaceRoot}/tsconfig.base.json"]
},
"targetDefaults": {
"build": {
"inputs": ["production", "^production"],
"outputs": [
"{projectRoot}/dist",
"{projectRoot}/.next/**",
"!{projectRoot}/.next/cache/**"
],
"cache": true
},
"test": {
"inputs": [
"default",
"^production",
{ "externalDependencies": ["jest", "vitest"] }
],
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"cache": true
},
"serve": {
"cache": false,
"continuous": true
}
},
"maxCacheSize": "10GB"
}
```
**Why good:** `production` input excludes test files so test changes do not invalidate build cache, `outputs` include build artifacts and exclude framework caches, `externalDependencies` ensures cache invalidates when test runner version changes, `cache: false` on serve prevents caching long-running dev servers, `maxCacheSize` prevents disk bloat
#### Nx Cloud Remote Caching
```json
{
"nxCloudId": "your-cloud-id"
}
```
```bash
# Connect workspace to Nx Cloud
npx nx connect
# Verify remote cache is working
npx nx build my-app --verbose
# Second run should show "remote cache hit"
```
**Why good:** One-line setup, entire team shares cached results, CI builds reuse developer cache hits and vice versa
See [examples/nx.md](examples/nx.md) for cache configuration examples and CI integration.
---
### Pattern 4: Inferred Tasks (Project Crystal)
Since Nx 18, plugins automatically infer tasks from tool configuration files. For example, `@nx/vite/plugin` detects `vite.config.ts` and creates `build`, `serve`, and `test` targets without any `project.json` configuration.
#### Plugin Configuration
```json
{
"plugins": [
{
"plugin": "@nx/vite/plugin",
"options": {
"buildTargetName": "build",
"serveTargetName": "serve",
"testTargetName": "test"
}
},
{
"plugin": "@nx/jest/plugin",
"include": ["packages/**/*"],
"exclude": ["**/*-e2e/**/*"],
"options": {
"targetName": "test"
}
},
{
"plugin": "@nx/eslint/plugin",
"options": {
"targetName": "lint"
}
}
]
}
```
**Why good:** Zero-config task detection from existing tool configs, `include`/`exclude` scope plugins to specific projects, inferred caching/inputs/outputs are accurate because plugins understand the tool, consistent target naming across all projects
#### Overriding Inferred Tasks
When you need to customize an inferred target, add a `project.json` with only the overrides:
```json
{
"name": "my-app",
"targets": {
"build": {
"outputs": ["{projectRoot}/custom-dist"]
}
}
}
```
**Why good:** Only overrides specified, all other inferred properties preserved. Nx merges `project.json` targets with inferred targets (project-level takes precedence).
#### Configuration Precedence
```
1. Plugin inferred config (lowest priority)
2. targetDefaults in nx.json
3. project.json or package.json targets (highest priority)
```
**When to use:** Always prefer inferred tasks as default. Only add `project.json` targets when a project needs configuration that differs from the inferred defaults.
---
### Pattern 5: Affected Commands and Project Graph
`nx affected` uses git diff combined with the project graph to determine which projects need to be rebuilt/tested. This is the primary CI optimization.
#### Affected Commands
```bash
# Run tests only for affected projects
npx nx affected -t test
# Build only affected projects
npx nx affected -t build
# Run multiple targets on affected projects
npx nx affected -t build test lint
# Compare against specific base branch
npx nx affected -t test --base=origin/main --head=HEAD
# Visualize affected project graph
npx nx affected --graph
```
**Why good:** Only runs tasks for changed projects and their dependents, uses project graph for accurate dependency analysis, `--graph` flag visualizes impact for debugging
```bash
# BAD: Run all tests every time
npx nx run-many -t test
```
**Why bad:** Runs tests for every project regardless of changes, wastes CI time and compute on unchanged projects
**When to use:** Always in CI pipelines. Use `nx run-many` only for local development when you want to run everything.
---
### Pattern 6: Generators (Code Scaffolding)
Generators create and modify code from templates. Official plugins provide generators for apps, libraries, components, and more. Custom generators enforce organizational standards.
#### Using Built-in Generators
```bash
# Create a new React library
npx nx generate @nx/react:library my-lib --directory=packages/my-lib
# Create a new Next.js application
npx nx generate @nx/next:application my-app --directory=apps/my-app
# Create a new Node library
npx nx generate @nx/node:library my-api --directory=packages/my-api
# Move a project to a new location
npx nx generate @nx/workspace:move --project=my-lib --destination=packages/shared/my-lib
# Remove a project
npx nx generate @nx/workspace:remove my-lib
```
#### Generator Defaults in nx.json
```json
{
"generators": {
"@nx/react:library": {
"bundler": "vite",
"unitTestRunner": "vitest",
"style": "scss"
},
"@nx/react:component": {
"style": "scss"
},
"@nx/js:library": {
"buildable": true,
"publishable": false
}
}
}
```
**Why good:** Consistent defaults for all generated code, no need to pass flags every time, enforces organizational standards
See [examples/nx.md](examples/nx.md) for custom generator examples.
---
### Pattern 7: Release Management (nx release)
`nx release` orchestrates three phases: versioning, changelog generation, and publishing. Supports fixed (all packages same version) and independent (per-package versioning) strategies.
#### nx.json Release Configuration
```json
{
"release": {
"projects": ["packages/*"],
"projectsRelationship": "independent",
"version": {
"conventionalCommits": true,
"preserveMatchingDependencyRanges": true,
"updateDependents": "always"
},
"changelog": {
"workspaceChangelog": {
"createRelease": "github",
"file": "{workspaceRoot}/CHANGELOG.md"
},
"projectChangelogs": {
"file": "{projectRoot}/CHANGELOG.md"
}
},
"releaseTag": {
"pattern": "{projectName}-v{version}"
},
"git": {
"commit": true,
"tag": true
}
}
}
```
**Why good:** Conventional commits automate version bumps from commit messages, independent releases allow per-package versioning, GitHub releases created automatically, changelogs at both workspace and project level, git tags follow clear naming pattern
#### Release Commands
```bash
# Full release: version + changelog + publish
npx nx release
# Dry run to preview changes
npx nx release --dry-run
# First release (skip changelog diff)
npx nx release --first-release
# Individual phases
npx nx release version
npx nx release changelog
npx nx release publish
# Version plans (file-based versioning)
npx nx release plan minor -m "Add new API endpoints"
```
#### Version Plans (Alternative to Conventional Commits)
```json
{
"release": {
"version": {
"conventionalCommits": false
},
"versionPlans": true
}
}
```
```bash
# Create a version plan file
npx nx release plan patch -m "Fix button hover state"
# Creates .nx/version-plans/plan-123.md
# Apply when ready
npx nx release
```
**When to use:** Use conventional commits for automated CI releases. Use version plans when teams want to decouple "what changed" from "what version bump."
---
### Pattern 8: Module Federation (Micro-Frontends)
Nx provides first-class module federation support for React and Angular, enabling micro-frontend architectures where independent teams deploy separately.
#### Generating Module Federation Setup
```bash
# Create host application
npx nx generate @nx/react:host shell --directory=apps/shell
# Create remote application
npx nx generate @nx/react:remote shop --directory=apps/shop --host=shell
# Add another remote
npx nx generate @nx/react:remote cart --directory=apps/cart --host=shell
```
#### Host Configuration (module-federation.config.ts)
```typescript
// apps/shell/module-federation.config.ts
import type { ModuleFederationConfig } from "@nx/module-federation";
const config: ModuleFederationConfig = {
name: "shell",
remotes: ["shop", "cart"],
};
export default config;
```
#### Dynamic Module Federation
```typescript
// apps/shell/module-federation.manifest.json
{
"shop": "http://localhost:4201",
"cart": "http://localhost:4202"
}
```
**Why good:** Remotes resolved at runtime (not hardcoded at build time), enables independent deployment, host does not need to rebuild when remotes change
**When to use:** Large teams with independent deployment cadences. When to avoid: small teams where a single app suffices.
</patterns>
---
<performance>
## Performance Optimization
**Cache Hit Metrics (typical monorepo with 20+ projects):**
- First build: ~60s (no cache, full workspace)
- Cached build: ~1s (local cache hit, 98% faster)
- Affected build: ~15s (only changed projects, 75% faster)
- Remote cache hit: ~5s (download + restore from Nx Cloud)
- Team savings: 10-40 hours/week with Nx Cloud enabled
**Optimization Strategies:**
- **Use `namedInputs`** to exclude test/spec files from build cache keys. A test file change should not invalidate the build cache.
- **Set `outputs` precisely** to only cache what is needed. Exclude framework caches (e.g., `!{projectRoot}/.next/cache/**`).
- **Enable Nx Cloud** for remote caching. One developer's cache hit benefits the entire team.
- **Use `nx affected`** in CI to skip unchanged projects entirely.
- **Configure `parallel`** in nx.json to control concurrency (default is 3, increase for powerful CI machines).
- **Use `maxCacheSize`** to prevent unbounded cache growth (default: 10% of disk, max 10GB).
**Force Cache Bypass:**
```bash
# Skip cache for a specific run
npx nx build my-app --skip-nx-cache
# Clear all cached artifacts
npx nx reset
```
</performance>
---
<decision_framework>
## Decision Framework
### When to Use Nx
```
Is this a monorepo with shared code?
├─ NO → Standard build tools (Vite, esbuild, tsc)
└─ YES → Do you need task orchestration and caching?
├─ NO → npm/pnpm/bun workspaces alone may suffice
└─ YES → Do you need a project graph and affected analysis?
├─ YES → Nx
└─ NO → Turborepo may be simpler
```
### Nx vs Turborepo
```
Which monorepo tool?
├─ Need project graph analysis → Nx
├─ Need generators and code scaffolding → Nx
├─ Need module federation support → Nx
├─ Need distributed task execution (Nx Agents) → Nx
├─ Need simplest possible config → Turborepo
├─ Already using Vercel ecosystem → Turborepo
└─ Need polyglot support (.NET, Java, Gradle) → Nx
```
### Where to Put New Code
```
New code to write?
├─ Deployable application → apps/
├─ Shared across 2+ apps → libs/ or packages/
├─ App-specific code → Feature folder within the app
├─ Build tooling or generators → tools/
└─ Shared configuration → packages/ (e.g., eslint-config, tsconfig)
```
### Fixed vs Independent Releases
```
How to version packages?
├─ All packages always release together → "fixed" (default)
├─ Packages have different consumers → "independent"
├─ Internal-only packages → Fixed (simpler)
└─ Published to npm with different audiences → Independent
```
For comprehensive decision trees and anti-patterns, see [reference.md](reference.md).
</decision_framework>
---
<integration>
## Integration Guide
**Works with:**
- **Package managers (npm, pnpm, Bun, Yarn)**: Nx works with any package manager's workspace feature for dependency linking
- **Vite**: `@nx/vite/plugin` infers build/serve/test targets from vite.config.ts
- **Jest / Vitest**: `@nx/jest/plugin` and `@nx/vite/plugin` infer test targets
- **ESLint**: `@nx/eslint/plugin` infers lint targets from eslint.config.js
- **Next.js**: `@nx/next` provides generators, executors, and module federation support
- **React**: `@nx/react` provides generators for apps, libraries, components, hooks
- **Angular**: `@nx/angular` provides full Angular CLI parity within Nx
- **Storybook**: `@nx/storybook` infers build-storybook and storybook targets
- **Playwright / Cypress**: `@nx/playwright` and `@nx/cypress` with test atomizer for distributed testing
- **Nx Cloud**: Remote caching (Nx Replay) and distributed task execution (Nx Agents)
- **Module Federation**: `@module-federation/enhanced` for micro-frontend architectures
**Replaces / Conflicts with:**
- **Turborepo**: Similar monorepo tool — choose one, not both
- **Lerna**: Nx subsumes Lerna's functionality (Nx team maintains Lerna since v6)
- **Rush**: Microsoft's monorepo tool — Nx provides broader plugin ecosystem
</integration>
---
<red_flags>
## RED FLAGS
**High Priority Issues:**
- Missing `dependsOn: ["^build"]` for build targets — dependencies may not build first, causing import errors
- Missing `cache: true` on cacheable targets — every run recomputes from scratch, negating Nx's primary value
- Caching long-running tasks (dev servers, watch mode) — `serve` and `dev` must have `cache: false`
- Running `nx run-many -t test` in CI instead of `nx affected -t test` — wastes compute on unchanged projects
- Missing `inputs` on cached targets — Nx cannot determine when cache is stale, leading to incorrect cache hits
**Medium Priority Issues:**
- Not using inferred tasks — manually defining every target in `project.json` when plugins can auto-detect
- Missing `namedInputs` for production — test file changes invalidate build caches unnecessarily
- Not connecting to Nx Cloud — every developer rebuilds everything locally instead of sharing cache
- Overly broad `outputs` — caching framework cache directories (`.next/cache/`) bloats cache storage
**Common Mistakes:**
- Using `dependsOn: ["build"]` (same project) when `dependsOn: ["^build"]` (dependency projects) was intended
- Forgetting to set `continuous: true` on serve tasks — dependent e2e tasks wait forever for serve to "complete"
- Running `nx migrate` without `--run-migrations` — migrations are generated but not applied
- Not setting `defaultBase` in nx.json — affected analysis defaults to `main` which may not be your branch
**Gotchas & Edge Cases:**
- `dependsOn: ["^task"]` runs the target on **dependency** projects; `dependsOn: ["task"]` runs it on the **same** project. Mixing these up causes subtle ordering bugs.
- `nx affected` requires git history — in CI, ensure `fetch-depth: 0` (full history) or at least `fetch-depth: 2` for shallow comparison.
- Plugin order in `nx.json` matters — when multiple plugins create the same target name, the last plugin wins.
- `maxCacheSize: "0"` means unlimited, not zero. To disable caching, use `cache: false` on targets.
- Nx merges `project.json` and `package.json` scripts. If both define the same target, `project.json` takes precedence for configuration but `package.json` scripts are still registered as targets.
- `nx reset` clears the local cache AND shuts down the Nx Daemon. Use `nx reset --only-cache` to preserve the daemon.
</red_flags>
---
<critical_reminders>
## CRITICAL REMINDERS
> **All code must follow project conventions in CLAUDE.md**
**(You MUST enable caching with `"cache": true` on cacheable targets — builds, tests, linting — and set `"cache": false` or omit for side-effect tasks like `serve`)**
**(You MUST define `dependsOn: ["^build"]` in targetDefaults for build tasks to ensure topological ordering across the project graph)**
**(You MUST declare `inputs` and `outputs` for cached targets so Nx knows what to hash and what to restore)**
**(You MUST use inferred tasks (Project Crystal) as the default — only add `project.json` targets when overriding inferred configuration)**
**(You MUST use `nx affected -t <target>` in CI to only run tasks for changed projects and their dependents)**
**Failure to follow these rules will cause incorrect builds, stale caches, wasted CI compute, and broken task ordering.**
</critical_reminders>