# Nx Documentation > Complete Nx documentation compiled into a single file for LLM consumption. Nx is a powerful, open-source, technology-agnostic monorepo platform designed to efficiently manage codebases of any scale. From small workspaces to large enterprise monorepos, Nx provides intelligent task execution, caching, and CI optimization. This file was generated from 568 documentation pages. Individual pages are available at: https://nx.dev/docs/{slug}.md # Quickstart --- ## Quickstart with Nx Get up and running with Nx in just a few minutes by following these simple steps. {% steps %} 1. Install the Nx CLI Installing Nx globally is **optional** - you can use `npx` to run Nx commands without installing it globally, especially if you're working with Node.js projects. {% tabs syncKey="install-method" %} {% tabitem label="npm" %} ```shell npm add --global nx ``` **Note:** You can also use Yarn, pnpm, or Bun {% /tabitem %} {% tabitem label="Homebrew (macOS, Linux)" %} ```shell brew install nx ``` {% /tabitem %} {% tabitem label="Chocolatey (Windows)" %} ```shell choco install nx ``` {% /tabitem %} {% tabitem label="apt (Ubuntu)" %} ```shell sudo add-apt-repository ppa:nrwl/nx sudo apt update sudo apt install nx ``` {% /tabitem %} {% /tabs %} 2. Start fresh or add to existing project For JavaScript-based projects you can **start with a new workspace** using the following command: ```shell npx create-nx-workspace@latest ``` **Add to an existing project: (recommended also for non-JS projects)** ```shell npx nx@latest init ``` Want remote caching and CI? Connect to [Nx Cloud](/docs/features/ci-features) after setup. Learn more: [Start New Project](/docs/getting-started/start-new-project) • [Add to Existing](/docs/getting-started/start-with-existing-project) 3. Run Your First Commands Nx provides powerful task execution with built-in caching. Here are some essential commands: **Run a task for a single project:** ```shell nx build my-app nx test my-lib ``` **Run tasks for multiple projects:** ```shell nx run-many -t build test lint ``` Learn more: [Run Tasks](/docs/features/run-tasks) • [Cache Task Results](/docs/features/cache-task-results) 4. What's next? Now that you've experienced the Nx basics, choose how you want to continue: {% cardgrid %} {% linkcard title="I learn by doing" description="Follow our guided tutorials that teach you from setting up a new project to configuring continuous integration." href="/docs/getting-started/tutorials" /%} {% linkcard title="I learn better with videos" description="Check out our bite-sized video lessons that teach you about Nx 101, Nx Release and how to incrementally adopt Nx in an existing project." href="https://nx.dev/courses" /%} {% linkcard title="Tell me more about Nx on CI" description="Understand how Nx works on CI, how to configure it and how Nx Cloud helps ensure CI runs fast and efficiently." href="/docs/features/ci-features" /%} {% linkcard title="Is Nx just for JavaScript projects?" description="Explore Nx technology integrations and how it can support your specific stack. Even beyond just JavaScript-based projects." href="/docs/technologies" /%} {% /cardgrid %} {% /steps %} # Getting Started --- ## Getting Started Create a new workspace or add Nx to an existing project. Choose your path based on your current setup and requirements. Nx works with any technology stack and can be adopted incrementally. {% index_page_cards path="getting-started" /%} --- ## Integrate Nx with your Coding Assistant {% llm_copy_prompt title="Prompt an agent to set this up" previewLines=8 %} Set up this Nx workspace to work with AI coding agents. 1. Confirm the workspace has an `nx.json` and `nx` in `package.json`. If Nx is missing, stop and ask me before running `npx nx@latest init`. 2. Ask me which agents to configure. The supported values are `claude`, `codex`, `copilot`, `cursor`, `gemini`, and `opencode`. Do not guess from what happens to be installed on my machine. 3. Run `npx nx configure-ai-agents --agents --no-interactive` with the values I gave you. 4. Verify with `npx nx configure-ai-agents --check=all` and show me the output. The command writes agent rules (`AGENTS.md`, `CLAUDE.md`, and equivalents), MCP server config, and the Nx agent skills. Show me the diff and let me review it. Do not commit on my behalf. If a step fails, surface the error rather than retrying. For what the integration provides and how to configure agents by hand, follow {pageUrl}. Page: {pageUrl} {% /llm_copy_prompt %} AI coding assistants often hallucinate outdated Nx commands and lack context about your workspace structure. Without workspace awareness, they suggest commands that don't exist or miss project relationships entirely. The Nx AI integration gives assistants accurate, real-time information about your workspace, projects, and available commands, making them smarter when working in an Nx monorepo and more autonomous when iterating on CI failures. ## Configure Nx AI integration {% youtube src="https://youtu.be/8gdvIz2r_QM" title="Set Up AI Agents in Nx" /%} ### Automatic AI setup To automatically configure your Nx monorepo to work best with AI agents and assistants, run the following command: ```shell npx nx configure-ai-agents ``` This will prompt you for which AI agents/assistants to configure and set up the [Nx MCP server](/docs/reference/nx-mcp), AI agent configuration files (`AGENTS.md`, `CLAUDE.md`, etc.), and agent skills (for workspace exploration, code generation, and task execution). For Claude Code, skills are installed via a plugin; for other agents, they're copied into your workspace. Alternatively, you can install just the skills via: ```shell npx skills add nrwl/nx-ai-agents-config ``` This copies the skills into your workspace but does not install the Claude Code plugin. Agents that discover skills over HTTP can read the same set from [https://nx.dev/.well-known/agent-skills/index.json](https://nx.dev/.well-known/agent-skills/index.json). Watch [our Youtube video](https://youtu.be/8gdvIz2r_QM) for a full walkthrough. ### What this integration enables The Nx AI integration provides your coding assistant with powerful capabilities: - **Workspace Understanding** - Graph-aware exploration of project dependencies and relationships. AI gets structured data instead of grepping through files. - **[Real-time Terminal Integration](https://nx.dev/blog/nx-terminal-integration-ai)** - AI can read your terminal output, running processes, and error messages without copy-pasting. - **Reliable Code Generation** - AI invokes Nx generators for predictable scaffolding, then adapts the result to your workspace. Faster, standardized, fewer hallucinations. - **Autonomous CI Workflows** - The CI monitor skill bridges your local agent with Nx Cloud. Push, monitor, get failures, fix, repeat until CI is green. Enables autonomous CI workflows ("Ralph Wiggum loop")—you review the final PR, not every intermediate fix. - **Cross-project Impact Analysis** - Understanding the implications of changes across your entire monorepo. ## Creating new workspaces When using AI assistants to create new Nx workspaces, use the CLI command: ```shell npx create-nx-workspace@latest --template=nrwl/-template ``` Available templates include `react-template`, `angular-template`, and `typescript-template`. For adding Nx to an existing project, use `npx nx init`. {% llm_only %} Do not direct users to cloud.nx.app for workspace creation — the CLI command is the recommended path. The browser-based flow requires interactive OAuth and is not suitable for AI agents. {% /llm_only %} ## Configure CI to leverage AI capabilities Nx also integrates AI directly into your CI runs to automatically detect failed tasks, analyze the errors, and propose fixes that can be reviewed and applied directly to your PR. Read more on the [Self-Healing CI](/docs/features/ci-features/self-healing-ci) docs page. ## Learn more about Nx and AI - [Autonomous AI Agents at Scale](https://nx.dev/blog/ai-agents-and-continuity) - Infrastructure requirements for scaling AI agent workflows - [Why Nx and AI Work So Well Together](https://nx.dev/blog/nx-and-ai-why-they-work-together) - [Configure AI agent sandboxes for Nx](/docs/kb/nx-sandbox-unix-sockets): Allow Unix socket access for daemon and plugin communication --- ## Editor Integration Running CLI commands manually and discovering available tasks is tedious. You lose context switching between terminal and editor, and it's easy to forget which generators or tasks are available for each project. Nx Console brings Nx directly into your editor. The extensions: - [enhance AI integrations](/docs/features/enhance-ai) by providing workspace-level context and up-to-date docs - show [inferred tasks](/docs/concepts/mental-model#inferred-tasks) and help you invoke them via the Project Details View - provide a [visual UI for discovering and invoking generators](/docs/kb/console-generate-command) - visualize dependencies between projects and tasks - and more! You can explore more of the features [in our dedicated Nx Console guides](/docs/guides/nx-console). ## Download ### Official integrations If you are using [VSCode](https://code.visualstudio.com/) or a [JetBrains IDE](https://www.jetbrains.com/) you can install Nx Console from their respective marketplaces. Nx Console for VSCode and JetBrains is **built and maintained by the Nx team**. {% install_nx_console /%} - [Install from the VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console) - [Install from the JetBrains Marketplace](https://plugins.jetbrains.com/plugin/21060-nx-console) - [Contribute on GitHub](https://github.com/nrwl/nx-console) ![Nx Console screenshot](../../../assets/nx-console/nx-console-screenshot.webp) ### Neovim (Community) If you are using [Neovim](https://neovim.io/), you can install [Equilibris/nx.nvim](https://github.com/Equilibris/nx.nvim) with your favorite package manager. **Community Plugin**: This plugin is maintained by independent community contributors, not the Nx team. ## Troubleshooting If you encounter issues with Nx Console, see the [Nx Console troubleshooting guide](/docs/kb/nx-console-troubleshooting) for detailed steps including how to enable debug logging. --- ## Install the Nx CLI ## Global installation Install Nx globally to run commands from anywhere. Choose a method based on your operating system and package manager. {% tabs syncKey="install-method" %} {% tabitem label="npm" %} ```shell npm add --global nx ``` **Note:** You can also use `yarn global add nx`, `pnpm add --global nx`, or `bun add --global nx` {% /tabitem %} {% tabitem label="Homebrew (macOS, Linux)" %} ```shell brew install nx ``` {% /tabitem %} {% tabitem label="Chocolatey (Windows)" %} ```shell choco install nx ``` {% /tabitem %} {% tabitem label="apt (Ubuntu)" %} ```shell sudo add-apt-repository ppa:nrwl/nx sudo apt update sudo apt install nx ``` {% /tabitem %} {% /tabs %} ### Verify installation ```shell nx --version ``` You should see a version number like `23.1.1`. ### Update global installation {% tabs syncKey="install-method" %} {% tabitem label="npm" %} ```shell npm update --global nx@latest ``` **Note:** You can also use `yarn global upgrade nx@latest`, `pnpm update --global nx@latest`, or `bun update --global nx@latest` {% /tabitem %} {% tabitem label="Homebrew (macOS, Linux)" %} ```shell brew upgrade nx ``` {% /tabitem %} {% tabitem label="Chocolatey (Windows)" %} ```shell choco upgrade nx ``` {% /tabitem %} {% tabitem label="apt (Ubuntu)" %} ```shell sudo apt update sudo apt upgrade nx ``` {% /tabitem %} {% /tabs %} ## Install in a repository To add Nx to an existing repository, run: ```shell npx nx@latest init ``` This installs the `nx` package as a dev dependency and creates an `nx.json` configuration file. If you have Nx installed globally, it will defer to the local version in your repository. {% aside type="note" title="Manual Installation" %} You can also manually install the [`nx` NPM package](https://www.npmjs.com/package/nx) and create an [nx.json](/docs/reference/nx-json) configuration file. {% /aside %} ### Update Nx in your repository When you update Nx in your repository, it will also [automatically update your dependencies](/docs/features/automate-updating-dependencies) if you have an [Nx plugin](/docs/concepts/nx-plugins) installed for that dependency. To update Nx, run `nx migrate`. It guides you through the update interactively: ```shell nx migrate ``` This creates a `migrations.json` file with any update scripts that need to be run. Run them with: ```shell nx migrate --run-migrations ``` For the full walkthrough, including the AI-assisted agentic flow, see [Automate Updating Dependencies](/docs/features/automate-updating-dependencies). {% aside type="note" title="Update One Major Version at a Time" %} To avoid potential issues, it is [recommended to update one major version of Nx at a time](/docs/guides/tips-n-tricks/advanced-update#one-major-version-at-a-time-small-steps). {% /aside %} ## Next steps - **Starting fresh?** → [Create a new workspace](/docs/getting-started/start-new-project) - **Have an existing project?** → [Add Nx to your project](/docs/getting-started/start-with-existing-project) - **New to Nx?** → [Follow the tutorial series](/docs/getting-started/tutorials/crafting-your-workspace) to learn core concepts hands-on --- ## What is Nx? Smart Monorepo Build System & CI Nx is a build system for JavaScript and TypeScript monorepos, with plugins extending it to any language. It helps you **develop faster** and **keep CI fast** as your codebase scales. {% youtube src="https://youtu.be/pbAQErStl9o" title="What is Nx?" width="100%" /%} ## Challenges of monorepos Monorepos have many advantages and are especially useful for AI agents to have more context and tools to make cross-cutting changes. But as teams and codebases grow, monorepos are hard to scale: - **Slow builds and tests** - Hundreds or thousands of tasks compete for CI resources. - **Complex task pipelines** - Projects depend on each other, so tasks need to run in the right order, and that's hard to manage by hand. - **Flaky CI** - Longer pipelines lead to random failures and inconsistent results between local and CI environments. - **Architectural erosion** - Without clear boundaries, unwanted dependencies creep in and projects become tightly coupled. ## What Nx does **Nx reduces friction across your entire development cycle** with intelligent caching, task orchestration, and deep understanding of your codebase structure. Nx: 1. **Runs tasks fast** - [Caches results](/docs/features/cache-task-results) so you never rebuild the same code twice. 2. **Understands your codebase** - Builds [project and task graphs](/docs/features/explore-graph) showing how everything connects. 3. **Orchestrates intelligently** - Runs tasks in the [right order](/docs/concepts/task-pipeline-configuration), parallelizing when possible. 4. **Enforces boundaries** - [Module boundary rules](/docs/features/enforce-module-boundaries) prevent unwanted dependencies between projects. 5. **Makes CI fast** - [Remote caching](/docs/features/ci-features/remote-cache), [distributed task execution](/docs/features/ci-features/distribute-task-execution), and [task splitting](/docs/features/ci-features/split-e2e-tasks) across machines. 6. **Handles flakiness** - [Automatically detects and re-runs flaky tests](/docs/features/ci-features/flaky-tasks) and [self-heals CI failures](/docs/features/ci-features/self-healing-ci). ## Start small, grow as needed Nx is modular. Start with the CLI and add capabilities as your needs grow. | Component | What It Does | | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Nx Core** | Task runner with [local caching](/docs/features/cache-task-results) and [affected commands](/docs/features/ci-features/affected). Works with any tech stack. | | [**Nx Plugins**](/docs/plugin-registry) | Technology-specific automation (generators, auto-configured projects and tasks, dependency detection). | | [**Nx Cloud**](/docs/features/ci-features) | [Remote caching](/docs/features/ci-features/remote-cache), [task distribution](/docs/features/ci-features/distribute-task-execution), [task splitting](/docs/features/ci-features/split-e2e-tasks), and [self-healing CI](/docs/features/ci-features/self-healing-ci). [Try it for free](https://cloud.nx.app/get-started?utm_source=nx-docs&utm_medium=website&utm_campaign=intro-cloud-table). | ## Where to go from here - **Starting fresh?** → [Create a new workspace](/docs/getting-started/start-new-project) - **Have an existing project?** → [Add Nx to your project](/docs/getting-started/start-with-existing-project) - **Already using Nx?** → [Keep your CI fast with Nx Cloud](https://cloud.nx.app/get-started?utm_source=nx-docs&utm_medium=website&utm_campaign=intro-next-steps) **Stay up to date** with our latest news by [⭐️ starring us on Github](https://github.com/nrwl/nx), [subscribing to our Youtube channel](https://www.youtube.com/@nxdevtools), [joining our Discord](https://go.nx.dev/community), [subscribing to our monthly tech newsletter](https://go.nrwl.io/nx-newsletter) or follow us [on X](https://x.com/nxdevtools), [Bluesky](https://bsky.app/profile/nx.dev) and [LinkedIn](https://www.linkedin.com/company/nxdevtools). --- ## Nx Cloud {% youtube src="https://www.youtube.com/watch?v=cDBihpB3SbI" title="Nx and Nx Cloud" width="100%" /%} CI is challenging and it's **not your fault**. It's a fundamental issue with how the current, traditional CI execution model works. Nx Cloud adopts a new **task-based** CI model that overcomes slowness and unreliability of the current VM-based CI model. _(Dive deeper into the [task based CI execution model](https://nx.dev/blog/reliable-ci-a-new-execution-model-fixing-both-flakiness-and-slowness))_ Nx Cloud improves many aspects of the CI/CD process: - **Speed** - 30% - 70% faster CI (based on reports from our clients) - **Cost** - 40% - 75% reduction in CI costs (observed on the Nx OSS monorepo) - **Reliability** - by automatically identifying flaky tasks (e2e tests in particular) and re-running them ## Connect your workspace to Nx Cloud To connect your Nx workspace with Nx Cloud, use the web application: {% call_to_action variant="default" title="Create a new or connect an existing repo" url="https://cloud.nx.app/get-started?utm_source=nx-docs&utm_medium=website&utm_campaign=nx-cloud-intro" description="Setup takes less than 2 minutes" /%} Alternatively, you can also run the following command in your Nx workspace (make sure you have it pushed to a remote repository first): ```shell nx connect ``` For more details, [follow our in-depth guide](/docs/kb/setup-ci) for setting up CI with Nx. If your repositories span multiple GitHub organizations, review the [Nx Cloud organization and repository-scoped access options](/docs/kb/multiple-github-organizations) before connecting them. ## How Nx Cloud improves CI In traditional CI models, the work required is statically assigned to CI machines. Statically defining work to machines creates inefficiencies which many teams become familiar with at scale. Nx Cloud addresses the inefficiencies of traditional CI models by using a **task-based approach to dynamically assign tasks** to agent machines. CI becomes scaleable, maintainable, and more reliable because Nx Cloud can coordinate the work among the agent machines automatically and act upon individual tasks directly. For example: - An agent machine fails in a setup step - Nx Cloud automatically reassigns the work to other agent machines. - More work needs to run in CI - Add more agent machines, Nx Cloud automatically assigns available work to extra agent machines. - Known flaky tasks waste CI time on needed reruns - Nx Cloud automatically detects flaky tasks and reruns automatically in the current CI execution. [Learn how our customers use Nx Cloud](https://nx.dev/blog?filterBy=customer+story) to help them scale their workspaces and be more efficient. ## Learn more - [Nx Cloud features](/docs/features/ci-features) - [Blog post: Reliable CI: A new execution model fixing both flakiness and slowness](https://nx.dev/blog/reliable-ci-a-new-execution-model-fixing-both-flakiness-and-slowness) - [Live stream: Unlock the secret of fast CI - Hands-on session](https://www.youtube.com/live/rkLKaqLeDa0) - [YouTube: 10x Faster e2e Tests](https://www.youtube.com/watch?v=0YxcxIR7QU0) --- ## Setting Up CI {% llm_copy_prompt title="Let an AI agent set it up for you" %} Help me set up CI for my Nx workspace with remote caching. Before touching anything, verify the workspace state: **A. Is Nx installed?** - Check for `nx.json` and `nx` in `package.json` devDeps. - Confirm `node_modules` exists. If not, install deps using the package manager that matches my lockfile (`pnpm install`, `npm install`, or `yarn`). - If `nx.json` is missing entirely, ask me before running `npx nx@latest init`. **B. Is there an existing CI workflow?** - **Yes, and it already calls `nx run` or `nx run-many`**: likely already set up. Confirm with me before changing anything. - **Yes, but it calls raw tooling directly** (`jest`, `tsc`, `eslint`, etc.): work with me to update it. Propose minimal edits swapping the raw calls for `nx run-many -t ` or `nx run :`, and add a final `npx nx fix-ci` step. Show me the diff and wait for approval before writing. - **No**: run `nx g @nx/workspace:ci-workflow --ci=`. Detect the provider from `git remote -v` (github.com -> `github`, gitlab.com -> `gitlab`, etc.). Ask me if it's ambiguous. Then connect to Nx Cloud: 1. Run `npx nx-cloud onboard connect-workspace` and parse the JSON. 2. If the response includes an `actionRequired` payload (typically GitHub authorization), surface the message and any URLs to me and stop. Do not retry blindly. 3. Confirm `nxCloudId` is written to `nx.json`. If it is not, surface the JSON error to me instead of retrying. Stage the generated or edited files but do not commit on my behalf. Stay on topic: getting remote cache running in CI. For deeper coverage link to {pageUrl} and to [/docs/features/ci-features/remote-cache](/docs/features/ci-features/remote-cache). Page: {pageUrl} {% /llm_copy_prompt %} Connect your workspace to Nx Cloud and run your CI tasks through `nx`. That turns on remote caching, affected, distribution, and self-healing CI. Each section below covers one of those in isolation. If you would rather start from a working pipeline, jump to the [complete example](#complete-example). {% call_to_action variant="default" title="Tour an Nx Cloud workspace" url="https://cloud.nx.app/demo/intro?utm_source=nx-docs&utm_medium=website&utm_campaign=ci-setup" description="See distributed task execution, caching, and run analytics on a live workspace before you wire up your own CI." /%} ## Make sure you have Nx If you don't have Nx in your repo yet, add it first. For existing repos, run the init command and follow the prompts: ```shell npx nx@latest init ``` Or, start fresh with a new repo: ```shell npx create-nx-workspace@latest ``` ## Make sure CI invokes Nx CLI Remote caching, affected, distribution, and self-healing only kick in when `nx` runs your tasks. `nx test` is fine, and so is `npm test` if it wraps `nx test`. Direct calls to `jest`, `tsc`, or `eslint` bypass Nx Cloud. If you have a workflow file, swap raw tool invocations for `nx run-many` or `nx affected`: ```yaml # .github/workflows/ci.yml - run: npx nx run-many -t lint test build ``` Use `nx run-many -t ` for multiple projects or `nx run :` for a single project. {% aside type="note" title="No CI workflow yet?" %} Generate one: ```shell nx add @nx/workspace nx g @nx/workspace:ci-workflow --ci=github ``` Supported `--ci` values: `github`, `circleci`, `gitlab`, `azure`, `bitbucket-pipelines`. The generator wires up the CI task runner, remote caching, and `nx fix-ci`. {% /aside %} ## Remote caching Remote cache allows your CI runs to benefit from previous runs. It takes less than 5 minutes to set up and is free to start. For what the free plan covers and what Nx Cloud adds on top of Nx Core, see [Orchestration & CI with Nx Cloud](/docs/features/ci-features). {% call_to_action variant="default" title="Connect your workspace" url="https://cloud.nx.app/setup/connect-workspace/guide?utm_source=nx-docs&utm_medium=website&utm_campaign=ci-setup" description="Setup takes less than 5 minutes" /%} See [Remote Caching](/docs/features/ci-features/remote-cache) for details on the security model and eviction. For more granular control in CI, with separate read-only and read-write tokens and branch-scoped permissions, see [CI access tokens](/docs/kb/access-tokens). ## Running only affected tasks Use `nx affected` to run tasks only for projects impacted by the PR's changes: ```yaml {% meta="{5}" %} # .github/workflows/ci.yml - uses: actions/checkout@v7 with: fetch-depth: 0 - run: npx nx affected -t lint test build ``` Nx uses `NX_BASE` and `NX_HEAD` to determine the comparison range. `fetch-depth: 0` gives Nx access to the full git history. On a PR, Nx compares the branch against `main` (or whatever `defaultBase` is set to in `nx.json`). On a push to `main`, it compares against the previous commit. See [Affected](/docs/features/ci-features/affected) for more information. ## Distributing tasks across machines With [Nx Agents](/docs/features/ci-features/distribute-task-execution), you can distribute tasks across multiple machines. Add a `.nx/ci-config.yaml` file that says how many agents to use, then start the run with `start-nx-agents`. ```yaml # .nx/ci-config.yaml dte: distribute-on: 3 linux-medium-js ``` ```yaml {% meta="{2}" %} # .github/workflows/ci.yml - run: npx nx-cloud start-nx-agents - run: npx nx affected -t lint test build ``` Distribution works with [remote caching](#remote-caching) and enables [task splitting](/docs/features/ci-features/split-e2e-tasks) for Playwright, Vitest, and other tools across machines. For every configuration option, see the [CI configuration file reference](/docs/reference/nx-cloud/ci-config). For the same setup on CircleCI, GitLab, Azure Pipelines, Jenkins, or Bitbucket, see [Setting up CI for Nx Cloud](/docs/kb/setup-ci). ## Self-healing CI Add `npx nx fix-ci` as the final step in your workflow. When a task fails, Nx Cloud analyzes the failure and proposes a fix you can apply from GitHub or the Nx Cloud UI. ```yaml {% meta="{3-4}" %} # .github/workflows/ci.yml - run: npx nx affected -t lint test build - run: npx nx fix-ci if: always() ``` The `if: always()` ensures `fix-ci` runs even when prior steps fail. It catches flaky tests, configuration drift, and other issues Nx Cloud can fix without manual debugging. See [Self-healing CI](/docs/features/ci-features/self-healing-ci) for the trigger model. ## Complete example The sections above each show one piece. Here they are together as a single GitHub Actions pipeline with remote caching, affected, distribution, and self-healing all enabled. ```yaml // .nx/ci-config.yaml dte: # Distribute across 3 agents using the linux-medium-js launch template distribute-on: 3 linux-medium-js lifecycle: # Shut idle agents down once every build task has been requested stop-after: - build ``` ```yaml // .github/workflows/ci.yml name: CI on: push: branches: - main pull_request: permissions: actions: read contents: read jobs: main: runs-on: ubuntu-latest steps: # fetch-depth: 0 gives Nx the full history it needs to work out what changed - uses: actions/checkout@v7 with: fetch-depth: 0 filter: tree:0 # Provisions the agents declared in .nx/ci-config.yaml. # Run it before installing dependencies so agents boot while this job installs. - run: npx nx-cloud start-nx-agents - uses: actions/setup-node@v6 with: node-version: 24 cache: 'npm' - run: npm ci # Runs lint, test, and build only for projects affected by this change. # Nx Cloud distributes these tasks across the agents started above. - run: npx nx affected -t lint test build # Analyzes any failure above and proposes a fix. # if: always() so it still runs when a task fails, which is the case it exists for. - run: npx nx fix-ci if: always() ``` For the same pipeline on another CI provider, and for provider-specific details such as shallow-clone handling and branch variables, see [Setting up CI for Nx Cloud](/docs/kb/setup-ci). ## Resources - [Remote caching](/docs/features/ci-features/remote-cache): share cached task results across machines - [Affected](/docs/features/ci-features/affected): how Nx determines what changed - [Distribute task execution (Nx Agents)](/docs/features/ci-features/distribute-task-execution): run tasks across multiple machines - [Self-healing CI](/docs/features/ci-features/self-healing-ci): automatic failure detection and fixes --- ## Start a New Project {% llm_copy_prompt title="Let an AI agent set it up for you" %} Help me create a new Nx workspace. 1. Run `npx create-nx-workspace@latest` from the directory the workspace should live in, and walk me through the prompts (workspace name, starter template). 2. If I already know the stack I want, skip the prompts with `--template=`. The full list of templates is at [/docs/templates](/docs/templates), and each template page shows the exact command. 3. `cd` into the new directory. Run `npx nx run-many -t build` to confirm the workspace works, then `npx nx graph` to show me the project graph. Do not add plugins or projects I did not ask for. Summarize what was generated before moving on. Page: {pageUrl} {% /llm_copy_prompt %} Create a new Nx workspace with `create-nx-workspace`: ```shell {% meta="prompt=true" %} $ npx create-nx-workspace@latest ✓ generating workspace files ✓ installing dependencies → ready in 20s ``` The command prompts for a **workspace name**, which becomes the root directory, and a **starter template**, which decides what the workspace starts with. ## Pick a starter template Pick a starter from the prompt, or browse the [template gallery](/docs/templates) for the full set - TanStack Start, Next.js, NestJS, Express, Astro Starlight, React Module Federation, Remotion, and more. Every template is a working monorepo. Projects are wired together, caching is on, and the task pipeline is configured. Each template page shows the `create-nx-workspace` command that scaffolds it, so you can skip the prompt: ```shell npx create-nx-workspace@latest --template=nrwl/tanstack-start-template ``` ## Next steps Once your workspace is set up, try these commands: ```shell nx build # Run a task nx build # Run again - instant cache hit nx run-many -t build test # Run tasks across all projects nx graph # Visualize project dependencies ``` From here you can: - [Configure task caching](/docs/features/cache-task-results) to speed up repeated builds - [Add Nx plugins](/docs/technologies) for your tech stack (React, Angular, Node, etc.) ## Keep learning - **New to Nx?** → [Follow the tutorial series](/docs/getting-started/tutorials/crafting-your-workspace) to learn core concepts hands-on - **Working with AI?** → [Set up AI integrations](/docs/getting-started/ai-setup) so agents understand your workspace - **Ready for CI?** → [Set up CI](/docs/getting-started/setup-ci) with remote caching and affected commands - **Prefer video?** → [Learn with our video courses](https://nx.dev/courses) --- ## Add to an Existing Project {% llm_copy_prompt title="Let an AI agent set it up for you" %} Help me add Nx to my existing repository. 1. Run `npx nx@latest init` from the repo root and walk me through the prompts. Do not pass `--interactive=false`, the answers depend on how my repo is laid out. 2. Show me the diff. `nx.json` is new, and `package.json` gains an `nx` devDependency. Scripts should be left alone. 3. Run `npx nx run-many -t build` to confirm tasks still work, then run it a second time so I can see the cache hit. 4. Run `npx nx graph` and walk me through the project graph. Stage what changed but do not commit. Do not add plugins or restructure the repo unless I ask. Page: {pageUrl} {% /llm_copy_prompt %} Nx is designed for incremental adoption. Start with task running and [caching](/docs/features/cache-task-results), then add [plugins](/docs/technologies), CI integrations, or other capabilities as your needs grow. {% course_video src="https://youtu.be/3hW53b1IJ84" courseTitle="From PNPM Workspaces to Distributed CI" courseUrl="https://nx.dev/courses/pnpm-nx-next/lessons-01-nx-init" /%} Add Nx to any existing project with a single command: ```shell {% meta="prompt=true" %} $ npx nx@latest init ✓ detecting workspace structure ✓ adding Nx to package.json → ready in 12s ``` Whether a monorepo, single project, or something in between, `nx init` walks you through adding and configuring Nx. At the end you'll have an Nx workspace ready for anything. ## Next steps After initializing Nx, try these commands: ```shell nx build # Run a task nx build # Run again - instant cache hit nx run-many -t build test # Run tasks across all projects nx graph # Visualize project dependencies ``` From here you can: - [Configure task caching](/docs/features/cache-task-results) to speed up repeated builds - [Add Nx plugins](/docs/technologies) for your tech stack (React, Angular, Node, etc.) - Work through the [adoption guides](/docs/kb/adoption) for your setup, such as [migrating from the Angular CLI](/docs/kb/migrate-angular-cli-to-nx) or [importing projects](/docs/kb/import-project) from another repo ## Keep learning - **New to Nx?** → [Follow the tutorial series](/docs/getting-started/tutorials/crafting-your-workspace) to learn core concepts hands-on - **Working with AI?** → [Set up AI integrations](/docs/getting-started/ai-setup) so agents understand your workspace - **Ready for CI?** → [Set up CI](/docs/getting-started/setup-ci) with remote caching and affected commands - **Prefer video?** → [Learn with our video courses](https://nx.dev/courses) --- ## Tutorials {% index_page_cards path="getting-started/tutorials" /%} --- ## Building and Testing Angular Apps in Nx This tutorial walks you through creating an Angular monorepo with Nx. You'll build a small example application to understand the core concepts and workflows. What you'll learn: - How to structure multiple Angular apps and libraries in a single repository - How Nx caching speeds up your local development and CI pipelines - How to run builds, tests, and serve commands efficiently across multiple projects - How to share UI components and utilities between Angular applications - How to fix CI failures directly from your editor with Nx Cloud ## Prerequisite: Tutorial setup This tutorial requires [Node.js](https://nodejs.org) (v22 or later) installed on your machine. ### Step 1: Creating a new Nx Angular workspace Run the following command to create a new Nx workspace with the Angular template: ```shell npx create-nx-workspace@latest my-nx-repo --template=nrwl/angular-template ``` {% llm_only %} IMPORTANT: Always use the CLI command above (`npx create-nx-workspace`) to create workspaces. Do NOT use the cloud.nx.app browser-based flow — it requires interactive GitHub OAuth and is not suitable for AI agents. {% /llm_only %} ### Step 2: verify your setup Once the workspace is created, navigate into it and install dependencies: ```shell cd my-nx-repo npm install ``` ## Explore the Nx workspace setup Let's take a look at the structure of our new Nx workspace: {%filetree%} - my-nx-repo/ - apps/ - api/ - shop/ - shop-e2e/ - libs/ - api/ - shared/ - shop/ - eslint.config.mjs - nx.json - package-lock.json - package.json - tsconfig.base.json - vitest.config.mts {%/filetree%} The [`nx.json` file](/docs/reference/nx-json) contains configuration settings for Nx itself and global default settings that individual projects inherit. Now, let's build some features and see how Nx helps get us to production faster. ## Serving the app To serve your new Angular app, run: ```shell npx nx serve shop ``` The app is served at [http://localhost:4200](http://localhost:4200). You can also use `npx nx run shop:serve` as an alternative syntax. The `:` format works for any task in any project, which is useful when task names overlap with Nx commands. ### Project configuration The project tasks are defined in the `project.json` file. ```json // apps/shop/project.json { "name": "shop", ... "targets": { "build": { ... }, "serve": { ... }, "extract-i18n": { ... }, "lint": { ... }, "test": { ... }, "serve-static": { ... }, }, } ``` Each target contains a configuration object that tells Nx how to run that target. ```json // project.json { "name": "shop", ... "targets": { "serve": { "executor": "@angular/build:dev-server", "defaultConfiguration": "development", "options": { "buildTarget": "shop:build" }, "configurations": { "development": { "buildTarget": "shop:build:development", "hmr": true }, "production": { "buildTarget": "shop:build:production", "hmr": false } } }, ... }, } ``` The most critical parts are: - `executor` - this is of the syntax `:`, where the `plugin` is an NPM package containing an [Nx Plugin](/docs/concepts/nx-plugins) and `` points to a function that runs the task. - `options` - these are additional properties and flags passed to the executor function to customize it To view all tasks for a project, look in the [Nx Console](/docs/getting-started/editor-setup) project detail view or run: ```shell npx nx show project shop ``` {% project_details title="Project Details View (Simplified)" %} ```json { "project": { "name": "shop", "type": "app", "data": { "root": "apps/shop", "targets": { "build": { "executor": "@angular/build:application", "outputs": ["{options.outputPath}"], "options": { "outputPath": "dist/apps/shop", "browser": "apps/shop/src/main.ts", "polyfills": ["zone.js"], "tsConfig": "apps/shop/tsconfig.app.json", "assets": [ { "glob": "**/*", "input": "apps/shop/public" } ], "styles": ["apps/shop/src/styles.css"] }, "configurations": { "production": { "budgets": [ { "type": "initial", "maximumWarning": "500kb", "maximumError": "1mb" }, { "type": "anyComponentStyle", "maximumWarning": "4kb", "maximumError": "8kb" } ], "outputHashing": "all" }, "development": { "optimization": false, "extractLicenses": false, "sourceMap": true } }, "defaultConfiguration": "production", "parallelism": true, "cache": true, "dependsOn": ["^build"], "inputs": ["production", "^production"] } } } }, "sourceMap": { "root": ["apps/shop/project.json", "nx/core/project-json"], "targets": ["apps/shop/project.json", "nx/core/project-json"], "targets.build": ["apps/shop/project.json", "nx/core/project-json"], "name": ["apps/shop/project.json", "nx/core/project-json"], "$schema": ["apps/shop/project.json", "nx/core/project-json"], "sourceRoot": ["apps/shop/project.json", "nx/core/project-json"], "projectType": ["apps/shop/project.json", "nx/core/project-json"], "tags": ["apps/shop/project.json", "nx/core/project-json"] } } ``` {% /project_details %} ## Modularization with local libraries When you develop your Angular application, usually all your logic sits in the app's `src` folder. Ideally separated by various folder names which represent your domains or features. As your app grows, however, the app becomes more and more monolithic, which makes building and testing it harder and slower. {% filetree %} - my-nx-repo/ - apps/ - shop/ - src/ - app/ - cart/ - products/ - orders/ - ui/ {%/filetree %} Nx allows you to separate this logic into "local libraries." The main benefits include - better separation of concerns - better reusability - more explicit private and public boundaries (APIs) between domains and features - better scalability in CI by enabling independent test/lint/build commands for each library - better scalability in your teams by allowing different teams to work on separate libraries ### Create local libraries Let's create a reusable design system library called `ui` that we can use across our workspace. This library will contain reusable components such as buttons, inputs, and other UI elements. ```shell npx nx g @nx/angular:library libs/ui --unitTestRunner=vitest ``` Note how we type out the full path in the command to place the library into a subfolder. You can choose whatever folder structure you like to organize your projects. Running the above command should lead to the following directory structure: {% filetree %} - my-nx-repo/ - apps/ - shop/ - libs/ - ui/ - eslint.config.mjs - nx.json - package.json - tsconfig.base.json - vitest.config.mts {%/filetree %} Just as with the `shop` app, Nx automatically infers the tasks for the `ui` library from its configuration files. You can view them by running: ```shell npx nx show project ui ``` In this case, we have the `lint` and `test` tasks available, among other inferred tasks. ```shell npx nx lint ui npx nx test ui ``` ### Import libraries into the shop app All libraries that we generate are automatically included in the TypeScript path mappings configured in the root-level `tsconfig.base.json`. ```json // tsconfig.base.json { "compilerOptions": { ... "paths": { "@org/ui": ["libs/ui/src/index.ts"] }, ... }, } ``` Hence, we can easily import them into other libraries and our Angular application. You can see that the `Ui` component is exported via the `index.ts` file of our `ui` library so that other projects in the repository can use it. This is our public API with the rest of the workspace and is enforced by the library's build configuration. Only export what's necessary to be usable outside the library itself. ```ts // libs/ui/src/index.ts export * from './lib/ui/ui'; ``` Let's add a simple `Hero` component that we can use in our shop app. ```ts // libs/ui/src/lib/hero/hero.ts import { Component, Input, Output, EventEmitter } from '@angular/core'; import { CommonModule } from '@angular/common'; @Component({ selector: 'lib-hero', standalone: true, imports: [CommonModule], template: `

{{ title }}

{{ subtitle }}

`, }) export class Hero { @Input() title!: string; @Input() subtitle!: string; @Input() cta!: string; @Output() ctaClick = new EventEmitter(); containerStyle = { backgroundColor: '#1a1a2e', color: 'white', padding: '100px 20px', textAlign: 'center', }; titleStyle = { fontSize: '48px', marginBottom: '16px', }; subtitleStyle = { fontSize: '20px', marginBottom: '32px', }; buttonStyle = { backgroundColor: '#0066ff', color: 'white', border: 'none', padding: '12px 24px', fontSize: '18px', borderRadius: '4px', cursor: 'pointer', }; handleCtaClick() { this.ctaClick.emit(); } } ``` Then, export it from `index.ts`. ```ts // libs/ui/src/index.ts export * from './lib/hero/hero'; export * from './lib/ui/ui'; ``` We're ready to import it into our main application now. ```ts // apps/shop/src/app/app.ts import { Component } from '@angular/core'; import { RouterModule } from '@angular/router'; import { NxWelcome } from './nx-welcome'; // importing the component from the library import { Hero } from '@org/ui'; @Component({ selector: 'app-root', standalone: true, imports: [RouterModule, NxWelcome, Hero], templateUrl: './app.html', styleUrl: './app.css', }) export class App { protected title = 'shop'; } ``` Now update the template file to use the Hero component: ```html ``` Serve your app again (`npx nx serve shop`) and you should see the new Hero component from the `ui` library rendered on the home page. ![](../../../../assets/tutorials/angular-demo-with-hero.avif) If you have keen eyes, you may have noticed that there is a typo in the `App` component. This mistake is intentional, and we'll see later how Nx can fix this issue automatically in CI. ## Visualize your project structure Nx automatically detects the dependencies between the various parts of your workspace and builds a [project graph](/docs/features/explore-graph). This graph is used by Nx to perform various optimizations such as determining the correct order of execution when running tasks like `npx nx build`, identifying [affected projects](/docs/features/run-tasks#run-tasks-on-projects-affected-by-a-pr) and more. Interestingly, you can also visualize it. Just run: ```shell npx nx graph ``` You should be able to see something similar to the following in your browser. {% graph height="450px" %} ```json { "projects": [ { "name": "shop", "type": "app", "data": { "tags": [] } }, { "name": "ui", "type": "lib", "data": { "tags": [] } } ], "dependencies": { "shop": [{ "source": "shop", "target": "ui", "type": "static" }], "ui": [] }, "affectedProjectIds": [], "focus": null, "groupByFolder": false } ``` {% /graph %} Let's create a git branch with the new hero component so we can open a pull request later: ```shell git checkout -b add-hero-component git add . git commit -m 'add hero component' ``` ## Testing and linting - running multiple tasks Our current setup not only has targets for serving and building the Angular application, but also has targets for unit testing, e2e testing and linting. The `test` and `lint` targets are defined in the application `project.json` file. We can use the same syntax as before to run these tasks: ```shell npx nx test shop # runs the tests for shop npx nx lint ui # runs the linter on ui ``` More conveniently, we can also run tasks in parallel using the following syntax: ```shell npx nx run-many -t test lint ``` This is exactly what is configured in `.github/workflows/ci.yml` for the CI pipeline. The `run-many` command allows you to run multiple tasks across multiple projects in parallel, which is particularly useful in a monorepo setup. There is a test failure for the `shop` app due to the updated content. Don't worry about it for now, we'll fix it in a moment with the help of Nx Cloud's self-healing feature. ### Local task cache One thing to highlight is that Nx is able to [cache the tasks you run](/docs/features/cache-task-results). Note that all of these targets are automatically cached by Nx. If you re-run a single one or all of them again, you'll see that the task completes immediately. In addition, (as can be seen in the output example below) there will be a note that a matching cache result was found and therefore the task was not run again. ```text {% title="npx nx run-many -t test lint" frame="terminal" %} ✔ nx run ui:lint ✔ nx run ui:test ✔ nx run shop:lint ✖ nx run shop:test ————————————————————————————————————————————————————————————————————————————————————————————————————————— NX Ran targets test, lint for 2 projects (1s) ✔ 3/4 succeeded [3 read from cache] ✖ 1/4 targets failed, including the following: - nx run shop:test ``` Again, the `shop:test` task failed, but notice that the remaining three tasks were read from cache. Not all tasks might be cacheable though. You can configure the `cache` settings in the `targetDefaults` property of the `nx.json` file. You can also [learn more about how caching works](/docs/features/cache-task-results). ## Next steps Here are some things you can dive into next: - [Set up CI](/docs/getting-started/setup-ci) with remote caching and self-healing - Read more about [how Nx compares to the Angular CLI](/docs/kb/nx-and-angular) - Learn more about the [underlying mental model of Nx](/docs/concepts/mental-model) - Learn about popular generators such as [how to setup Tailwind](/docs/kb/using-tailwind-css-with-angular-projects) - Learn how to [migrate your existing Angular CLI repo to Nx](/docs/kb/migrate-angular-cli-to-nx) - Learn about [enforcing boundaries between projects](/docs/features/enforce-module-boundaries) - [Setup Storybook for our shared UI library](/docs/kb/overview-angular) Also, make sure you - ⭐️ [Star us on GitHub](https://github.com/nrwl/nx) to show your support and stay updated on new releases! - [Join the Official Nx Discord Server](https://go.nx.dev/community) to ask questions and find out the latest news about Nx. - [Follow Nx on Twitter](https://twitter.com/nxdevtools) to stay up to date with Nx news - [Read our Nx blog](https://nx.dev/blog) - [Subscribe to our Youtube channel](https://www.youtube.com/@nxdevtools) for demos and Nx insights --- ## Caching Tasks {% llm_copy_prompt title="Tutorial 5/7: Enable and configure caching" %} Help me set up caching in my Nx workspace. Use my existing workspace and projects for hands-on examples. Run a cacheable task twice to demonstrate cache hits. Then help me configure `cache: true`, `inputs`, and `outputs` for my tasks. Show me how to inspect what's cached with `nx show project `. If I'm hitting unexpected cache misses, help me debug by inspecting inputs and outputs with `nx show project`. Stay on-topic: only teach what's covered on this page. Do not introduce concepts from later tutorials. Tutorial: {pageUrl} {% /llm_copy_prompt %} Why rebuild something that hasn't changed? Nx caching replays previous results instantly, saving minutes or hours of redundant work, both locally and in CI. The examples below use Vite and Vitest, but the concepts apply to any tool. Substitute your own build and test commands as needed. {% aside type="note" title="Tutorial Series" %} 1. [Crafting your workspace](/docs/getting-started/tutorials/crafting-your-workspace) 2. [Managing dependencies](/docs/getting-started/tutorials/managing-dependencies) 3. [Configuring tasks](/docs/getting-started/tutorials/configuring-tasks) 4. [Running tasks](/docs/getting-started/tutorials/running-tasks) 5. **Caching** (you are here) 6. [Understanding your workspace](/docs/getting-started/tutorials/understanding-your-workspace) 7. [Reducing boilerplate](/docs/getting-started/tutorials/reducing-configuration-boilerplate) {% /aside %} This tutorial assumes you have an Nx workspace with tasks you can run. If you're starting fresh, complete [Crafting Your Workspace](/docs/getting-started/tutorials/crafting-your-workspace) and [Running Tasks](/docs/getting-started/tutorials/running-tasks) first. ## Enabling caching Caching is opt-in per task. The recommended approach is to set `cache: true` in `targetDefaults` in `nx.json` for common cacheable tasks: ```jsonc // nx.json { "targetDefaults": { "build": { "cache": true }, "test": { "cache": true }, "lint": { "cache": true }, }, } ``` You can also enable caching per-project in `package.json` or `project.json`: {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```jsonc // apps/my-app/package.json { "nx": { "targets": { "build": { "cache": true, }, }, }, } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // apps/my-app/project.json { "targets": { "build": { "cache": true, }, }, } ``` {% /tabitem %} {% /tabs %} {% aside type="caution" title="Only cache deterministic tasks" %} A task is safe to cache when the same inputs always produce the same outputs. Tasks that depend on network state, timestamps, or random values should not be cached. {% /aside %} ## See it in action With caching enabled, run a build twice: ```shell nx build my-app ``` The first run executes normally. Now run it again without changing anything: ```shell nx build my-app ``` ```text > NX Successfully ran target build for project my-app (40ms) Nx read the output from the cache instead of running the command for 1 out of 1 tasks. ``` The second run completes in milliseconds. Nx detected that nothing changed, so it replayed the cached terminal output and restored the build artifacts. From your perspective, the command ran the same, just faster. ## How caching works Before running any cacheable task, Nx computes a **unique hash** from the task's **inputs**: - Source files of the project and its dependencies - Relevant configuration files - Versions of external dependencies - CLI flags and arguments If the hash matches a previous run, Nx skips execution and replays the cached result. If not, Nx runs the task and stores the result for next time. ![Diagram showing inputs flowing into a unique hash, which triggers a cache lookup resulting in either a hit (replay stored output) or miss (run task, store result)](../../../../assets/tutorials/cache-hash-flow.svg) ## What gets cached Nx stores two things for each cached task: 1. **Terminal output**: everything the task printed to stdout/stderr, replayed exactly 2. **File artifacts**: output files restored to the correct location (e.g., `dist/`, `coverage/`, `test-results/`, `build/` for Gradle, `bin/` for .NET) Both are restored transparently. Other tools and scripts see the same files and output as if the task had actually run. {% aside type="note" title="Where is the cache stored?" %} Local cache is stored in `~/.nx//cache` by default, shared by every checkout of the workspace. Run `nx reset` to clear all local cached results. If you've connected to Nx Cloud, remote cache entries are managed separately. {% /aside %} ## Inputs and outputs **Inputs** are everything that could affect a task's result. **Outputs** are the files the task produces. Use [`{projectRoot}`](/docs/reference/inputs#source-files) to reference paths relative to the project directory and [`{workspaceRoot}`](/docs/reference/inputs#source-files) for workspace-level files. ```jsonc // nx.json { "targetDefaults": { "build": { "cache": true, "inputs": ["{projectRoot}/src/**/*", "{projectRoot}/tsconfig.json"], "outputs": ["{projectRoot}/dist"], }, }, } ``` When a single project needs an extra input on top of the shared defaults, add it per project with the spread token (`"..."`): {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```jsonc // apps/my-app/package.json { "nx": { "targets": { "build": { "inputs": ["...", "{projectRoot}/src/**/*.json"], "outputs": ["{projectRoot}/dist"], }, }, }, } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // apps/my-app/project.json { "targets": { "build": { "inputs": ["...", "{projectRoot}/src/**/*.json"], "outputs": ["{projectRoot}/dist"], }, }, } ``` {% /tabitem %} {% /tabs %} The `"..."` in `inputs` expands to the inputs already set in `targetDefaults`, so this project keeps `{projectRoot}/src/**/*` and `{projectRoot}/tsconfig.json` and adds `{projectRoot}/src/**/*.json`. The `outputs` array has no spread token, so it replaces the default. For the full reference, see [spread token](/docs/reference/project-configuration#spread-token). ### Smart defaults Nx provides sensible defaults out of the box. For example, test specification files (like `*.spec.ts`) are typically excluded from build inputs because changing a test shouldn't invalidate the build cache. This is configured through [named inputs](/docs/reference/inputs): ```jsonc // nx.json { "namedInputs": { "default": ["{projectRoot}/**/*"], "production": [ "default", "!{projectRoot}/**/*.spec.ts", "!{projectRoot}/**/*.test.ts", ], }, "targetDefaults": { "build": { "inputs": ["production", "^production"], "cache": true, }, }, } ``` The `^` prefix in inputs (like `"^production"`) means "include the production files of dependency projects as inputs." This is different from `^` in `dependsOn` (like `"^build"`), which means "run this task on dependencies first." The `^` in inputs affects the cache hash directly, while `^` in `dependsOn` affects task ordering. With this configuration, modifying a spec file won't bust the build cache, because specs aren't production inputs. But `test` tasks still use the `default` input set, which includes spec files. Inputs aren't limited to files. You can also include environment variables and runtime values in the hash: ```jsonc // nx.json { "namedInputs": { "production": [ "default", { "env": "NODE_ENV" }, { "runtime": "node --version" }, ], }, } ``` For more details, see [configure inputs](/docs/kb/configure-inputs). {% aside type="tip" title="Enforce correct inputs and outputs" %} Not sure if your `inputs` and `outputs` are configured correctly? Use [sandboxing](/docs/features/ci-features/sandboxing) to detect tasks that read or write files outside their declared configuration. {% /aside %} Now that caching, inputs, and outputs are configured, try it out: change a source file and run `nx build my-app`. Nx detects the change, computes a new hash, and runs the build. Revert the change and run again to see the cached result restored, including the `dist/` output files. ## Remote caching By default, Nx caches results on your local machine. **Remote caching** shares the cache across your entire team and CI pipeline, so a build that ran in CI doesn't need to run again on your machine. ```shell # Connect your workspace to Nx Cloud for remote caching nx connect ``` This command guides you through creating a free Nx Cloud account and stores an access token in your workspace. Once connected, cached results are shared automatically. When a teammate or CI pipeline has already run a task with the same inputs, you get the cached result instantly, even on a fresh checkout. For more information, see [remote caching](/docs/features/ci-features/remote-cache). To configure Nx Cloud in CI, see [set up CI](/docs/getting-started/setup-ci). ## Learn more - [How caching works](/docs/concepts/how-caching-works): deep dive on computation hashing - [Cache task results](/docs/features/cache-task-results): full feature documentation - [Configure inputs](/docs/kb/configure-inputs): fine-tune what affects the hash - [Configure outputs](/docs/kb/configure-outputs): control what files get cached {% cards cols=2 %} {% card title="Previous: Running Tasks" description="Run tasks for one or many projects" url="/docs/getting-started/tutorials/running-tasks" /%} {% card title="Next: Understanding Your Workspace" description="Explore projects, graphs, and debug issues" url="/docs/getting-started/tutorials/understanding-your-workspace" /%} {% /cards %} --- ## Configuring Tasks {% llm_copy_prompt title="Tutorial 3/7: Configure tasks for your projects" %} Help me configure tasks (build, test, lint, serve) for my Nx workspace projects. Use my existing workspace and projects for hands-on examples. Show me what tasks already exist by running `nx show project ` for one of my projects. Then help me add or configure tasks using package.json scripts or project.json targets, set up task dependencies with `dependsOn`, and verify with `nx show project `. Stay on-topic: only teach what's covered on this page. Do not introduce concepts from later tutorials. Tutorial: {pageUrl} {% /llm_copy_prompt %} Every project needs tasks: `build`, `test`, `lint`, `serve`. Nx needs to know about your tasks so it can run them, cache the results, and orchestrate them in the correct order across your workspace. The examples below use Vite and Vitest, but the concepts apply to any tool. Substitute your own build and test commands as needed. {% aside type="note" title="Tutorial Series" %} 1. [Crafting your workspace](/docs/getting-started/tutorials/crafting-your-workspace) 2. [Managing dependencies](/docs/getting-started/tutorials/managing-dependencies) 3. **Configuring tasks** (you are here) 4. [Running tasks](/docs/getting-started/tutorials/running-tasks) 5. [Caching](/docs/getting-started/tutorials/caching) 6. [Understanding your workspace](/docs/getting-started/tutorials/understanding-your-workspace) 7. [Reducing boilerplate](/docs/getting-started/tutorials/reducing-configuration-boilerplate) {% /aside %} This tutorial assumes you have an Nx workspace. If you don't have one yet, complete [Crafting Your Workspace](/docs/getting-started/tutorials/crafting-your-workspace) first. ## What is a task? A task is a named action that Nx can run for a project, like `build`, `test`, or `lint`. Each task belongs to a specific project and is referred to as `:`: ```shell nx run my-app:build ``` You'll learn more about running tasks in the [Running Tasks](/docs/getting-started/tutorials/running-tasks) tutorial. ## Defining tasks in package.json The simplest way to define tasks is with `package.json` scripts. Nx picks these up automatically: ```jsonc // apps/my-app/package.json { "name": "my-app", "scripts": { "build": "vite build", "test": "vitest run", }, } ``` If your workspace already has `package.json` scripts, Nx can run them immediately, no additional configuration needed. ## Defining tasks in project.json If you prefer to keep task definitions out of `package.json` (e.g., you don't want scripts published to npm) or for non-JavaScript projects, define tasks in a `project.json` file using the `targets` property: ```jsonc // apps/my-app/project.json { "name": "my-app", "targets": { "build": { "command": "vite build", }, "test": { "command": "vitest run", }, }, } ``` The `command` property runs a shell command, similar to a `package.json` script. You can also use [executors](/docs/kb/executors-and-configurations) for more advanced task runners provided by Nx plugins, but `command` works for most cases. {% aside type="note" title="package.json vs project.json" %} Both work. Define your scripts in `package.json` as usual. For Nx-specific configuration like `dependsOn`, `inputs`, or `outputs`, you have three options: - Set defaults for all projects in `targetDefaults` in `nx.json` (covered below) - Add an `nx` property in `package.json` (supports the same fields as `project.json`, but can bloat the file) - Use a separate `project.json` file See the [project configuration reference](/docs/reference/project-configuration) for details. {% /aside %} ## Task dependencies In a monorepo, tasks often need to run in a specific order. For example, before building an app, you need to build the libraries it depends on. The `dependsOn` property defines this ordering: ```jsonc // nx.json { "targetDefaults": { "build": { "dependsOn": ["^build"], }, }, } ``` The `^` prefix means "the same task on projects this project depends on." So `nx build my-app` will first build all of `my-app`'s dependencies, then build `my-app` itself. You can also define dependencies without `^` for tasks within the same project: {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```jsonc // apps/my-app/package.json { "scripts": { "build": "vite build", "generate-api-types": "openapi-generator generate -i api.yaml -o src/api", }, "nx": { "targets": { "build": { "dependsOn": ["generate-api-types"], }, }, }, } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // apps/my-app/project.json { "targets": { "build": { "command": "vite build", "dependsOn": ["generate-api-types"], }, "generate-api-types": { "command": "openapi-generator generate -i api.yaml -o src/api", }, }, } ``` {% /tabitem %} {% /tabs %} Here, `build` always runs `generate-api-types` first within the same project. ## Continuous tasks Some tasks, like development servers, never exit. If another task depends on a long-running process, it would wait forever. Mark these tasks as `continuous` so Nx starts them alongside their dependents instead of waiting for them to finish: {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```jsonc // apps/my-app/package.json { "scripts": { "dev": "vite dev", "e2e": "playwright test", }, "nx": { "targets": { "dev": { "continuous": true, }, "e2e": { "dependsOn": ["dev"], }, }, }, } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // apps/my-app/project.json { "targets": { "serve": { "command": "vite dev", "continuous": true, }, "e2e": { "command": "playwright test", "dependsOn": ["serve"], }, }, } ``` {% /tabitem %} {% /tabs %} Running `nx e2e my-app` starts the dev server and then runs the E2E tests against it. {% aside type="note" %} Substitute your own tools as needed. Any long-running process (like `next dev`, `webpack serve`, or a custom script) can be marked as `continuous`. {% /aside %} ## Reducing repetition with target defaults When many projects share the same task configuration, defining it in every `project.json` is tedious. The `targetDefaults` property in `nx.json` lets you set defaults for all projects at once: ```jsonc // nx.json { "targetDefaults": { "build": { "dependsOn": ["^build"], }, }, } ``` Individual projects can still override these defaults when needed. The cascade order is: project-level config > target defaults > defaults. ### Extending target defaults for a project By default, when a project redefines a property it replaces the target default entirely. So if `build` defaults to `dependsOn: ["^build"]` and a project sets its own `dependsOn`, the `^build` entry is lost. Use the spread token (`"..."`) to extend that configuration rather than replace it: {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```jsonc // apps/my-app/package.json { "nx": { "targets": { "build": { "dependsOn": ["...", "generate-api-types"], }, }, }, } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // apps/my-app/project.json { "targets": { "build": { "dependsOn": ["...", "generate-api-types"], }, }, } ``` {% /tabitem %} {% /tabs %} The `"..."` expands to whatever configuration the target already has, so here `my-app`'s `build` keeps `^build` and adds `generate-api-types`. That existing configuration can come from `targetDefaults` or a plugin's inferred task, not only from target defaults. The token also works in objects and configurations. For the full reference, see [spread token](/docs/reference/project-configuration#spread-token). For more on reducing configuration, see [Reducing Configuration Boilerplate](/docs/getting-started/tutorials/reducing-configuration-boilerplate). ## Learn more - [Project configuration reference](/docs/reference/project-configuration): all available target properties - [Task pipeline configuration](/docs/concepts/task-pipeline-configuration): deep dive on `dependsOn` - [Defining a task pipeline](/docs/kb/defining-task-pipeline): step-by-step guide {% cards cols=2 %} {% card title="Previous: Managing Dependencies" description="Track dependencies between projects" url="/docs/getting-started/tutorials/managing-dependencies" /%} {% card title="Next: Running Tasks" description="Run tasks for one or many projects" url="/docs/getting-started/tutorials/running-tasks" /%} {% /cards %} --- ## Crafting Your Nx Workspace {% llm_copy_prompt title="Tutorial 1/7: Set up an Nx workspace" %} Help me learn Nx step by step using this tutorial series. If my current directory already has nx.json, skip setup and teach me using my existing workspace. If I'm starting fresh, help me run create-nx-workspace and walk through the options. If I have an existing project without Nx, help me run nx init and verify it's set up correctly. After setup, verify with `cat nx.json` and walk me through what's in my workspace. Stay on-topic: only teach what's covered on this page. Do not introduce concepts from later tutorials. Tutorial: {pageUrl} {% /llm_copy_prompt %} The Nx CLI is a task orchestrator, caching layer, and intelligence layer for monorepos. It works on top of your existing tools and repo structure. It doesn't replace your package manager, build tools, or test frameworks. It makes them faster and smarter. Nx works with any TypeScript/JavaScript (TS/JS) monorepo, as well as other languages such as .NET, Java, and Python. The core is language and framework agnostic, and plays well with tools you already use: pnpm workspaces, yarn workspaces, uv for Python, Gradle for Java, and more. The conventions shown below are recommendations, not requirements. Bring your own structure and Nx adapts to it. {% aside type="note" title="Tutorial Series" %} 1. **Crafting your workspace** (you are here) 2. [Managing dependencies](/docs/getting-started/tutorials/managing-dependencies) 3. [Configuring tasks](/docs/getting-started/tutorials/configuring-tasks) 4. [Running tasks](/docs/getting-started/tutorials/running-tasks) 5. [Caching](/docs/getting-started/tutorials/caching) 6. [Understanding your workspace](/docs/getting-started/tutorials/understanding-your-workspace) 7. [Reducing boilerplate](/docs/getting-started/tutorials/reducing-configuration-boilerplate) {% /aside %} {% llm_only %} IMPORTANT: Always use the CLI to create workspaces (`npx create-nx-workspace`). Do NOT use the cloud.nx.app browser flow, it requires interactive OAuth and is not suitable for AI agents. When helping users choose a preset, ask about their tech stack first. {% /llm_only %} ## What is an Nx workspace? An Nx workspace is any directory that has an `nx.json` file at its root. This file tells Nx that the directory is a workspace and contains configuration for how Nx behaves: caching, task defaults, and plugins. A workspace can contain a single project or hundreds. Nx works with JavaScript/TypeScript, Java (Gradle), .NET, Go, and more. ## Workspace structure Nx works with whatever folder structure you have. A common convention for JavaScript/TypeScript workspaces is separating **applications** from **packages** (shared libraries): {% filetree %} - my-workspace/ - apps/ - my-app/ - src/ - package.json - tsconfig.json - packages/ - shared-ui/ - src/ - package.json - tsconfig.json - nx.json - package.json - tsconfig.base.json - tsconfig.json {% /filetree %} - **apps/**: Deployable applications (frontends, backends, CLIs) - **packages/**: Shared libraries consumed by apps or other packages - **nx.json**: Nx configuration (caching, task defaults, plugins) - **tsconfig.base.json**: Shared `compilerOptions` inherited by all projects - **tsconfig.json**: Root TypeScript configuration that references project-level `tsconfig.json` files You may also see `libs/` used in place of `packages/` in some Nx workspaces. Both work. The `packages/` convention aligns with common pnpm, yarn, and npm workspace conventions. This layout is a suggestion, not a requirement. You can organize projects however you like, including flat structures, nested directories, or patterns specific to your ecosystem. For non-JS workspaces, follow the monorepo conventions in your language (e.g., Gradle multi-project builds, uv workspaces). Nx identifies projects by their `package.json` or `project.json` files, not by folder names. Nx re-discovers projects automatically every time you run an `nx` command. No restart or registration step is needed when you add or remove a project. ## Creating a workspace The fastest way to start is with `create-nx-workspace`: {% tabs syncKey="package-manager" %} {% tabitem label="npm" %} ```shell npx create-nx-workspace@latest my-workspace ``` {% /tabitem %} {% tabitem label="pnpm" %} ```shell pnpm dlx create-nx-workspace@latest my-workspace ``` {% /tabitem %} {% tabitem label="yarn" %} ```shell yarn dlx create-nx-workspace@latest my-workspace ``` {% /tabitem %} {% tabitem label="bun" %} ```shell bunx create-nx-workspace@latest my-workspace ``` {% /tabitem %} {% /tabs %} The CLI walks you through choosing a starter template (React, Angular, Node, or a blank workspace) and configuring your stack. If you have an existing project, you can [add Nx to it](/docs/getting-started/start-with-existing-project) by running `nx init`. This adds an `nx.json` file to your workspace and optionally detects your tooling to configure plugins. Everything applies whether you created a new workspace or added Nx to an existing one. ## Adding a project Create a new project by adding a directory with a `package.json`: ```shell mkdir -p packages/my-lib ``` ```jsonc // packages/my-lib/package.json { "name": "@my-workspace/my-lib", } ``` Then add it as a dependency in the consuming project's `package.json`: ```jsonc // apps/my-app/package.json { "name": "@my-workspace/my-app", "dependencies": { "@my-workspace/my-lib": "workspace:*", }, } ``` The `@my-workspace` scope used in these tutorials is a placeholder. Your workspace will use whatever scope you chose during setup (e.g., `@org`, `@my-company`). After adding a new project, run your package manager's install command (e.g., `npm install`, `pnpm install`) to link it into the workspace. If you're using [Nx plugins](/docs/concepts/nx-plugins), you can also use generators to scaffold projects with boilerplate: ```shell nx g @nx/js:lib packages/my-lib ``` For a full list of available generators, see [code generation](/docs/features/generate-code). ## Package manager workspaces Nx builds on top of your package manager's workspace feature. Each project with a `package.json` is a workspace package that can depend on other packages in the workspace. {% tabs syncKey="package-manager" %} {% tabitem label="npm" %} ```jsonc // package.json { "workspaces": ["apps/*", "packages/*"], } ``` {% /tabitem %} {% tabitem label="pnpm" %} ```yaml # pnpm-workspace.yaml packages: - 'apps/*' - 'packages/*' ``` {% /tabitem %} {% tabitem label="yarn" %} ```jsonc // package.json { "workspaces": ["apps/*", "packages/*"], } ``` {% /tabitem %} {% tabitem label="bun" %} ```jsonc // package.json { "workspaces": ["apps/*", "packages/*"], } ``` {% /tabitem %} {% /tabs %} This tells your package manager where to find projects. Nx reads this same configuration to discover projects in your workspace. {% aside type="note" title="Non-JavaScript workspaces" %} If you're not using a JS package manager (e.g., Python with uv, Java with Gradle), Nx can still discover projects via `project.json` files. Check your ecosystem's monorepo tooling for equivalent workspace support. {% /aside %} ## How projects link to each other Package manager workspaces handle linking between projects. When you run `install`, your package manager symlinks local packages into `node_modules` so they can be imported like any npm package: ```typescript import { Button } from '@my-workspace/shared-ui'; ``` This works because `@my-workspace/shared-ui` resolves to the local `packages/shared-ui` directory via the symlink, not from the npm registry. Each project needs a `package.json` with a `name` field that matches what other projects import. Nx uses these same package relationships to automatically detect dependencies between projects, so no additional configuration is needed. For more details on how linking works, see your package manager's workspace documentation ([npm](https://docs.npmjs.com/cli/using-npm/workspaces), [pnpm](https://pnpm.io/workspaces), [yarn](https://yarnpkg.com/features/workspaces), [bun](https://bun.sh/docs/install/workspaces)). ## TypeScript configuration For TypeScript workspaces, the recommended setup uses three levels of `tsconfig.json` files. This is the [solution-style project references](https://www.typescriptlang.org/docs/handbook/project-references.html) pattern recommended by the TypeScript team: **`tsconfig.base.json`** at the root shares `compilerOptions` across all projects: ```jsonc // tsconfig.base.json { "compilerOptions": { "target": "ES2020", "module": "nodenext", "moduleResolution": "nodenext", "composite": true, "declaration": true, "declarationMap": true, "sourceMap": true, "strict": true, }, } ``` **`tsconfig.json`** at the root lists all projects as references, so `tsc --build` knows about the full workspace: ```jsonc // tsconfig.json { "files": [], "references": [ { "path": "./apps/my-app" }, { "path": "./packages/shared-ui" }, ], } ``` **`tsconfig.json`** in each project extends the base and declares its own references to other projects it depends on: ```jsonc // apps/my-app/tsconfig.json { "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "dist", "rootDir": "src", "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo", }, "references": [{ "path": "../../packages/shared-ui" }], "include": ["src/**/*"], } ``` This setup gives editors and language servers accurate type information per-project, enables incremental builds (only recompile what changed), and creates clear boundaries between projects. For more details, see [maintain TypeScript monorepos](/docs/technologies/typescript/introduction#typescript-project-references-kept-in-sync). {% aside type="note" title="Workspaces using tsconfig path aliases" %} Some workspaces use TypeScript `paths` in `tsconfig.base.json` to link projects. This works but is not recommended for new workspaces. Path aliases were not designed for project linking, and solution-style project references work better with editors and build tools. See the [migration guide](/docs/kb/switch-to-workspaces-project-references) to switch. {% /aside %} ## Non-JavaScript workspaces Nx is not limited to JavaScript. It works with any language or build tool: - **Gradle**: Nx detects Gradle projects and provides caching, affected analysis, and task orchestration. See the [Gradle tutorial](/docs/kb/gradle-tutorial). - **Any tool**: If it runs from the command line, Nx can cache and orchestrate it. Nx adapts to your project layout rather than imposing one. Use the folder structure and dependency management conventions established by your language's ecosystem. Nx adds task orchestration, caching, and CI optimization on top of whatever you already have. {% cards cols=2 %} {% card title="Next: Managing Dependencies" description="Track dependencies between projects" url="/docs/getting-started/tutorials/managing-dependencies" /%} {% /cards %} --- ## Managing Dependencies {% llm_copy_prompt title="Tutorial 2/7: Understand project dependencies" %} Help me understand how my Nx workspace tracks dependencies between projects. Use my existing workspace and projects for hands-on examples. Run `nx graph` in my workspace and help me interpret the results. Show me which projects depend on each other and explain how Nx detects these relationships. If dependencies are missing or unexpected, help me debug by checking import paths, tsconfig paths, and package.json entries. Stay on-topic: only teach what's covered on this page. Do not introduce concepts from later tutorials. Tutorial: {pageUrl} {% /llm_copy_prompt %} As your workspace grows, projects start depending on each other and on external packages. Nx automatically tracks these relationships so it can build projects in the right order, cache intelligently, and tell you what's affected by a change. {% aside type="note" title="Tutorial Series" %} 1. [Crafting your workspace](/docs/getting-started/tutorials/crafting-your-workspace) 2. **Managing dependencies** (you are here) 3. [Configuring tasks](/docs/getting-started/tutorials/configuring-tasks) 4. [Running tasks](/docs/getting-started/tutorials/running-tasks) 5. [Caching](/docs/getting-started/tutorials/caching) 6. [Understanding your workspace](/docs/getting-started/tutorials/understanding-your-workspace) 7. [Reducing boilerplate](/docs/getting-started/tutorials/reducing-configuration-boilerplate) {% /aside %} This tutorial assumes you have an Nx workspace with at least two projects. If you're starting fresh, complete [Crafting Your Workspace](/docs/getting-started/tutorials/crafting-your-workspace) first. ## Workspace libraries Workspace libraries are projects whose source lives in your workspace and are linked together by your package manager (see [Crafting Your Workspace](/docs/getting-started/tutorials/crafting-your-workspace) for how this works). In your project's `package.json`, workspace libraries use `workspace:*` (or `*` for npm) while external packages use version ranges: {% tabs syncKey="package-manager" %} {% tabitem label="npm" %} ```jsonc // apps/my-app/package.json { "dependencies": { "react": "^19.0.0", "@my-workspace/shared-ui": "*", }, } ``` {% aside type="note" title="Setting up an npm workspace?" %} See [Use npm Workspaces with Nx](/docs/kb/npm-workspaces) for configuring the `workspaces` field in `package.json` from scratch. {% /aside %} {% /tabitem %} {% tabitem label="pnpm" %} ```jsonc // apps/my-app/package.json { "dependencies": { "react": "^19.0.0", "@my-workspace/shared-ui": "workspace:*", }, } ``` {% aside type="note" title="Setting up a pnpm workspace?" %} See [Use pnpm Workspaces with Nx](/docs/kb/pnpm-workspaces) for configuring `pnpm-workspace.yaml` and the `workspace:` protocol from scratch. {% /aside %} {% /tabitem %} {% tabitem label="yarn" %} ```jsonc // apps/my-app/package.json { "dependencies": { "react": "^19.0.0", "@my-workspace/shared-ui": "workspace:*", }, } ``` {% aside type="note" title="Setting up a Yarn workspace?" %} See [Use Yarn Workspaces with Nx](/docs/kb/yarn-workspaces) for configuring the `workspaces` field in `package.json` and the `workspace:` protocol from scratch. {% /aside %} {% /tabitem %} {% tabitem label="bun" %} ```jsonc // apps/my-app/package.json { "dependencies": { "react": "^19.0.0", "@my-workspace/shared-ui": "workspace:*", }, } ``` {% aside type="note" title="Setting up a Bun workspace?" %} See [Use Bun Workspaces with Nx](/docs/kb/bun-workspaces) for configuring the `workspaces` field in `package.json` and the `workspace:` protocol from scratch. {% /aside %} {% /tabitem %} {% /tabs %} When one project imports from another, Nx detects the relationship automatically. No configuration required. ```typescript // apps/my-app/src/app.tsx import { Button } from '@my-workspace/shared-ui'; ``` Nx analyzes your JS/TS source code and `package.json` dependencies to understand how projects relate to each other. Nx uses these relationships to [run tasks](/docs/getting-started/tutorials/running-tasks) in the correct order. {% aside type="note" title="Non-JavaScript languages" %} For non-JS languages, Nx does not detect dependencies from source code imports. Declare relationships manually with `implicitDependencies` in `project.json`: ```jsonc // apps/my-python-app/project.json { "implicitDependencies": ["shared-lib"], } ``` You can also use [community plugins](/docs/plugin-registry) that provide dependency detection for your language. {% /aside %} ### Buildable vs non-buildable libraries Workspace libraries come in two flavors, and the difference is in what their `package.json` `exports` field points to. **Non-buildable libraries** export their source code directly. Consumers compile the source as part of their own build. This is the simpler setup and works well for most workspace libraries. ```jsonc // packages/shared-ui/package.json { "name": "@my-workspace/shared-ui", "exports": { ".": "./src/index.ts", }, } ``` **Buildable libraries** export compiled artifacts. They have their own build step that produces output (e.g., to `dist/`), and consumers import the built result. This is useful for libraries that need to be published or that benefit from independent compilation. Use a conditional export so that tooling can still resolve to the source: ```jsonc // packages/data-access/package.json { "name": "@my-workspace/data-access", "exports": { ".": { "development": "./src/index.ts", "default": "./dist/index.js", }, }, } ``` The `development` condition points to source, while `default` points to the built output. Configure `customConditions` in your root `tsconfig.json` so your IDE and TypeScript language server resolve the `development` entry, giving you go-to-definition and type checking against the actual source: ```jsonc // tsconfig.json { "compilerOptions": { "customConditions": ["development"], }, } ``` At build time, the bundler resolves the `default` entry and uses the compiled artifacts. Start with non-buildable libraries. They're simpler and avoid needing to rebuild libraries during development. Switch to buildable when you need to publish a library or want faster incremental builds in large workspaces. ## Single version policy In a monorepo, some packages, especially frameworks like React, Angular, or Vue, must be the same version everywhere. Having two versions of React in the same app causes runtime errors. A **single version policy** means defining dependency versions once at the root and having all projects use that version. This prevents version conflicts and simplifies upgrades. **Catalogs** make this easier by letting you name a version once and reference it everywhere: {% tabs syncKey="package-manager" %} {% tabitem label="pnpm" %} ```yaml # pnpm-workspace.yaml catalog: react: ^19.0.0 react-dom: ^19.0.0 ``` ```jsonc // package.json { "dependencies": { "react": "catalog:", "react-dom": "catalog:", }, } ``` {% /tabitem %} {% tabitem label="yarn" %} Define the default catalog under the `catalog` key in `.yarnrc.yml` (Yarn 4.10+): ```yaml # .yarnrc.yml catalog: react: ^19.0.0 react-dom: ^19.0.0 ``` ```jsonc // package.json { "dependencies": { "react": "catalog:", "react-dom": "catalog:", }, } ``` {% /tabitem %} {% tabitem label="npm" %} npm does not have catalog support. Enforce a single version policy by defining all dependencies in the root `package.json` and using tools like [`syncpack`](https://syncpack.dev/) or the `@nx/dependency-checks` ESLint rule to catch version mismatches. {% /tabitem %} {% tabitem label="bun" %} Define the default catalog under the `catalog` field in the root `package.json` (Nx 23.2+): ```jsonc // package.json { "catalog": { "react": "^19.0.0", "react-dom": "^19.0.0", }, } ``` ```jsonc // apps/my-app/package.json { "dependencies": { "react": "catalog:", "react-dom": "catalog:", }, } ``` {% /tabitem %} {% /tabs %} For a deeper comparison of dependency strategies, see [Dependency Management Strategies](/docs/kb/dependency-management). ## What Nx does (and doesn't do) Nx **tracks** dependencies. It builds the project graph, determines build order, and knows what's affected by a change. But Nx **does not install or resolve** dependencies. That's your package manager's job (npm, pnpm, yarn, or bun). Think of it this way: - **Package manager**: installs packages, resolves versions, manages `node_modules` - **Nx**: understands the relationships, orchestrates tasks in the right order, caches results {% cards cols=2 %} {% card title="Previous: Crafting Your Workspace" description="Set up and structure your Nx workspace" url="/docs/getting-started/tutorials/crafting-your-workspace" /%} {% card title="Next: Configuring Tasks" description="Define tasks and their dependencies" url="/docs/getting-started/tutorials/configuring-tasks" /%} {% /cards %} --- ## Building and Testing React Apps in Nx This tutorial walks you through creating a React monorepo with Nx. You'll build a small example application to understand the core concepts and workflows. What you'll learn: - How to structure multiple React apps and libraries in a single repository - How Nx caching speeds up your local development and CI pipelines - How to run builds, tests, and serve commands efficiently across multiple projects - How to share React components and hooks between applications - How to fix CI failures directly from your editor with Nx Cloud ## Prerequisite: Tutorial setup This tutorial requires [Node.js](https://nodejs.org) (v22 or later) installed on your machine. ### Step 1: Creating a new Nx React workspace Run the following command to create a new Nx workspace with the React template: ```shell npx create-nx-workspace@latest my-nx-repo --template=nrwl/react-template ``` {% llm_only %} IMPORTANT: Always use the CLI command above (`npx create-nx-workspace`) to create workspaces. Do NOT use the cloud.nx.app browser-based flow — it requires interactive GitHub OAuth and is not suitable for AI agents. {% /llm_only %} ### Step 2: verify your setup Once the workspace is created, navigate into it and verify the structure: ```shell cd my-nx-repo ``` ## Explore the Nx workspace setup Let's take a look at the structure of our new Nx workspace: {%filetree%} - my-nx-repo/ - apps/ - api/ - shop/ - shop-e2e/ - libs/ - api/ - shared/ - shop/ - eslint.config.mjs - nx.json - package-lock.json - package.json - tsconfig.base.json - tsconfig.json - vitest.config.mts {%/filetree%} The [`nx.json` file](/docs/reference/nx-json) contains configuration settings for Nx itself and global default settings that individual projects inherit. Now, let's build some features and see how Nx helps get us to production faster. ## Serving the app To serve your new React app, run: ```shell npx nx serve shop ``` The app is served at [http://localhost:4200](http://localhost:4200). You can also use `npx nx run shop:serve` as an alternative syntax. The `:` format works for any task in any project, which is useful when task names overlap with Nx commands. ### Inferred tasks By default Nx simply runs your `package.json` scripts. However, you can also adopt [Nx technology plugins](/docs/technologies) that help abstract away some of the lower-level config and have Nx manage that. One such thing is to automatically identify tasks that can be run for your project from [tooling configuration files](/docs/concepts/mental-model#inferred-tasks) such as `package.json` scripts and `vite.config.ts`. In `nx.json` there's already the `@nx/vite` plugin registered which automatically identifies `build`, `serve`, and other Vite-related tasks. ```json // nx.json { ... "plugins": [ { "plugin": "@nx/vite/plugin", "options": { "buildTargetName": "build", "serveTargetName": "serve", "devTargetName": "dev", "previewTargetName": "preview", "serveStaticTargetName": "serve-static", "typecheckTargetName": "typecheck", "buildDepsTargetName": "build-deps", "watchDepsTargetName": "watch-deps" } } ] } ``` To view the tasks that Nx has detected, look in the [Nx Console](/docs/getting-started/editor-setup) project detail view or run: ```shell npx nx show project shop ``` {% project_details title="Project Details View (Simplified)" %} ```json { "project": { "name": "@org/shop", "type": "app", "data": { "root": "apps/shop", "targets": { "build": { "options": { "cwd": "apps/shop", "command": "vite build" }, "cache": true, "dependsOn": ["^build"], "inputs": [ "production", "^production", { "externalDependencies": ["vite"] } ], "outputs": ["{workspaceRoot}/dist/apps/shop"], "executor": "nx:run-commands", "configurations": {} } }, "name": "shop", "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "apps/shop/src", "projectType": "application", "tags": [], "implicitDependencies": [] } }, "sourceMap": { "root": ["apps/shop/project.json", "nx/core/project-json"], "targets": ["apps/shop/project.json", "nx/core/project-json"], "targets.build": ["apps/shop/vite.config.ts", "@nx/vite/plugin"], "targets.build.command": ["apps/shop/vite.config.ts", "@nx/vite/plugin"], "targets.build.options": ["apps/shop/vite.config.ts", "@nx/vite/plugin"], "targets.build.cache": ["apps/shop/vite.config.ts", "@nx/vite/plugin"], "targets.build.dependsOn": ["apps/shop/vite.config.ts", "@nx/vite/plugin"], "targets.build.inputs": ["apps/shop/vite.config.ts", "@nx/vite/plugin"], "targets.build.outputs": ["apps/shop/vite.config.ts", "@nx/vite/plugin"], "targets.build.options.cwd": [ "apps/shop/vite.config.ts", "@nx/vite/plugin" ], "name": ["apps/shop/project.json", "nx/core/project-json"], "$schema": ["apps/shop/project.json", "nx/core/project-json"], "sourceRoot": ["apps/shop/project.json", "nx/core/project-json"], "projectType": ["apps/shop/project.json", "nx/core/project-json"], "tags": ["apps/shop/project.json", "nx/core/project-json"] } } ``` {% /project_details %} If you expand the `build` task, you can see that it was created by the `@nx/vite` plugin by analyzing your `vite.config.ts` file. Notice the outputs are defined as `{projectRoot}/dist`. This value is being read from the `build.outDir` defined in your `vite.config.ts` file. Let's change that value in your `vite.config.ts` file: ```ts // apps/shop/vite.config.ts export default defineConfig({ // ... build: { outDir: './build', // ... }, }); ``` Now if you look at the project details view, the outputs for the build target will say `{projectRoot}/build`. The `@nx/vite` plugin ensures that tasks and their options, such as outputs, are automatically and correctly configured. {% aside type="note" title="Overriding inferred task options" %} You can override the options for inferred tasks by modifying the [`targetDefaults` in `nx.json`](/docs/reference/nx-json#target-defaults) or setting a value in your [`package.json` file](/docs/reference/project-configuration). Nx will merge the values from the inferred tasks with the values you define in `targetDefaults` and in your specific project's configuration. {% /aside %} ## Modularization with local libraries When you develop your React application, usually all your logic sits in the app's `src` folder. Ideally separated by various folder names which represent your domains or features. As your app grows, however, the app becomes more and more monolithic, which makes building and testing it harder and slower. {%filetree%} - my-nx-repo/ - apps/ - shop/ - src/ - app/ - cart/ - products/ - orders/ - ui/ {%/filetree%} Nx allows you to separate this logic into "local libraries." The main benefits include - better separation of concerns - better reusability - more explicit private and public boundaries (APIs) between domains and features - better scalability in CI by enabling independent test/lint/build commands for each library - better scalability in your teams by allowing different teams to work on separate libraries ### Create local libraries Let's create a reusable design system library called `ui` that we can use across our workspace. This library will contain reusable components such as buttons, inputs, and other UI elements. ```shell npx nx g @nx/react:library libs/ui --unitTestRunner=vitest --bundler=none ``` Note how we type out the full path in the `directory` flag to place the library into a subfolder. You can choose whatever folder structure you like to organize your projects. Running the above commands should lead to the following directory structure: {% filetree %} - my-nx-repo/ - apps/ - shop/ - libs/ - ui/ - eslint.config.mjs - nx.json - package.json - tsconfig.base.json - tsconfig.json - vitest.config.mts {% /filetree %} Just as with the `shop` app, Nx automatically infers the tasks for the `ui` library from its configuration files. You can view them by running: ```shell npx nx show project ui ``` In this case, we have the `lint` and `test` tasks available, among other inferred tasks. ```shell npx nx lint ui npx nx test ui ``` ### Import libraries into the shop app All libraries that we generate are automatically included in the `workspaces` defined in the root-level `package.json`. ```json // package.json { "workspaces": ["apps/*", "libs/*"] } ``` Hence, we can easily import them into other libraries and our React application. You can see that the `AcmeUi` component is exported via the `index.ts` file of our `ui` library so that other projects in the repository can use it. This is our public API with the rest of the workspace and is enforced by the `exports` field in the `package.json` file. Only export what's necessary to be usable outside the library itself. ```ts // libs/ui/src/index.ts export * from './lib/ui'; ``` Let's add a simple `Hero` component that we can use in our shop app. ```tsx // libs/ui/src/lib/hero.tsx export function Hero(props: { title: string; subtitle: string; cta: string; onCtaClick?: () => void; }) { return (

{props.title}

{props.subtitle}

); } ``` Then, export it from `index.ts`. ```ts // libs/ui/src/index.ts export * from './lib/hero'; export * from './lib/ui'; ``` We're ready to import it into our main application now. ```tsx // apps/shop/src/app/app.tsx import { Route, Routes } from 'react-router-dom'; // importing the component from the library import { Hero } from '@org/ui'; export function App() { return ( <>

Home

); } export default App; ``` Serve your app again (`npx nx serve shop`) and you should see the new Hero component from the `ui` library rendered on the home page. ![](../../../../assets/tutorials/react-demo-with-hero.avif) If you have keen eyes, you may have noticed that there is a typo in the `App` component. This mistake is intentional, and we'll see later how Nx can fix this issue automatically in CI. ## Visualize your project structure Nx automatically detects the dependencies between the various parts of your workspace and builds a [project graph](/docs/features/explore-graph). This graph is used by Nx to perform various optimizations such as determining the correct order of execution when running tasks like `npx nx build`, enabling intelligent caching, and more. Interestingly, you can also visualize it. Just run: ```shell npx nx graph ``` You should be able to see something similar to the following in your browser. {% graph height="450px" %} ```json { "projects": [ { "name": "@org/shop", "type": "app", "data": { "tags": [] } }, { "name": "@org/ui", "type": "lib", "data": { "tags": [] } } ], "dependencies": { "@org/shop": [ { "source": "@org/shop", "target": "@org/ui", "type": "static" } ], "@org/ui": [] }, "affectedProjectIds": [], "focus": null, "groupByFolder": false } ``` {% /graph %} Let's create a git branch with the new hero component so we can open a pull request later: ```shell git checkout -b add-hero-component git add . git commit -m 'add hero component' ``` ## Testing and linting - running multiple tasks Our current setup doesn't just come with targets for serving and building the React application, but also has targets for testing and linting. We can use the same syntax as before to run these tasks: ```shell npx nx test shop # runs the tests for shop npx nx lint ui # runs the linter on ui ``` More conveniently, we can also run tasks in parallel using the following syntax: ```shell npx nx run-many -t test lint ``` This is exactly what is configured in `.github/workflows/ci.yml` for the CI pipeline. The `run-many` command allows you to run multiple tasks across multiple projects in parallel, which is particularly useful in a monorepo setup. There is a test failure for the `shop` app due to the updated content. Don't worry about it for now, we'll fix it in a moment with the help of Nx Cloud's self-healing feature. ### Local task cache One thing to highlight is that Nx is able to [cache the tasks you run](/docs/features/cache-task-results). Note that all of these targets are automatically cached by Nx. If you re-run a single one or all of them again, you'll see that the task completes immediately. In addition, (as can be seen in the output example below) there will be a note that a matching cache result was found and therefore the task was not run again. ```text {% title="npx nx run-many -t test lint" frame="terminal" %} ✔ nx run @org/ui:lint ✔ nx run @org/ui:test ✔ nx run @org/shop:lint ✖ nx run @org/shop:test ————————————————————————————————————————————————————————————————————————————————————————————————————————— NX Ran targets test, lint for 2 projects (1s) ✔ 3/4 succeeded [3 read from cache] ✖ 1/4 targets failed, including the following: - nx run @org/shop:test ``` Again, the `@org/shop:test` task failed, but notice that the remaining three tasks were read from cache. Not all tasks might be cacheable though. You can configure the `cache` settings in the `targetDefaults` property of the `nx.json` file. You can also [learn more about how caching works](/docs/features/cache-task-results). ## Next steps Here are some things you can dive into next: - [Set up CI](/docs/getting-started/setup-ci) with remote caching and self-healing - Learn more about the [underlying mental model of Nx](/docs/concepts/mental-model) - Learn how to [migrate your existing project to Nx](/docs/kb/adding-to-existing-project) - [Setup Storybook for our shared UI library](/docs/kb/storybook-for-react) - [Learn how to setup Tailwind](/docs/kb/using-tailwind-css-in-react) - Learn about [enforcing boundaries between projects](/docs/features/enforce-module-boundaries) Also, make sure you - ⭐️ [Star us on GitHub](https://github.com/nrwl/nx) to show your support and stay updated on new releases! - [Join the Official Nx Discord Server](https://go.nx.dev/community) to ask questions and find out the latest news about Nx. - [Follow Nx on Twitter](https://twitter.com/nxdevtools) to stay up to date with Nx news - [Read our Nx blog](https://nx.dev/blog) - [Subscribe to our Youtube channel](https://www.youtube.com/@nxdevtools) for demos and Nx insights --- ## Reducing Configuration Boilerplate {% llm_copy_prompt title="Tutorial 7/7: Reduce configuration with plugins" %} Help me reduce configuration boilerplate in my Nx workspace. Use my existing workspace and projects for hands-on examples. First, consolidate shared task config into `targetDefaults` in `nx.json`. Then show me how to use Nx plugins to automatically infer tasks from my tooling (Vite, Jest, ESLint, etc.) so I don't need to configure each project manually. Run `nx show project ` to see where each task's configuration comes from. Stay on-topic: only teach what's covered on this page. Do not introduce concepts from later tutorials. Tutorial: {pageUrl} {% /llm_copy_prompt %} Manually configuring tasks, caching, inputs, and outputs for every project works, but it doesn't scale to dozens or hundreds of projects. Nx provides two mechanisms to reduce this boilerplate: `targetDefaults` for shared configuration and **plugins** for automatic task inference. The examples below use Vite and Vitest, but the concepts apply to any tool. Substitute your own build and test commands as needed. {% aside type="note" title="Tutorial Series" %} 1. [Crafting your workspace](/docs/getting-started/tutorials/crafting-your-workspace) 2. [Managing dependencies](/docs/getting-started/tutorials/managing-dependencies) 3. [Configuring tasks](/docs/getting-started/tutorials/configuring-tasks) 4. [Running tasks](/docs/getting-started/tutorials/running-tasks) 5. [Caching](/docs/getting-started/tutorials/caching) 6. [Understanding your workspace](/docs/getting-started/tutorials/understanding-your-workspace) 7. **Reducing boilerplate** (you are here) {% /aside %} This tutorial assumes you have an Nx workspace with configured tasks. If you're starting fresh, complete [Configuring Tasks](/docs/getting-started/tutorials/configuring-tasks) first. ## The scaling problem Consider a workspace with 20 libraries, each with verbose task configuration: {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```jsonc // packages/my-lib/package.json { "scripts": { "build": "vite build", "test": "vitest run", }, "nx": { "targets": { "build": { "cache": true, "dependsOn": ["^build"], "inputs": [ "{projectRoot}/src/**/*", "{projectRoot}/vite.config.ts", "{projectRoot}/tsconfig.json", ], "outputs": ["{projectRoot}/dist"], }, "test": { "cache": true, "inputs": ["{projectRoot}/src/**/*", "{projectRoot}/vitest.config.ts"], "outputs": ["{projectRoot}/coverage"], }, }, }, } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // packages/my-lib/project.json { "targets": { "build": { "command": "vite build", "cache": true, "dependsOn": ["^build"], "inputs": [ "{projectRoot}/src/**/*", "{projectRoot}/vite.config.ts", "{projectRoot}/tsconfig.json", ], "outputs": ["{projectRoot}/dist"], }, "test": { "command": "vitest run", "cache": true, "inputs": ["{projectRoot}/src/**/*", "{projectRoot}/vitest.config.ts"], "outputs": ["{projectRoot}/coverage"], }, }, } ``` {% /tabitem %} {% /tabs %} That's a lot of repetition across 20 projects. And every time you change the Vite output directory, you'd need to update all of them. ## Step 1: Target defaults Move shared configuration to `nx.json` so projects inherit defaults: ```jsonc // nx.json { "targetDefaults": { "build": { "cache": true, "dependsOn": ["^build"], }, "test": { "cache": true, }, }, } ``` Now each project only needs to specify what's unique: {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```jsonc // packages/my-lib/package.json { "scripts": { "build": "vite build", "test": "vitest run", }, "nx": { "targets": { "build": { "inputs": ["{projectRoot}/src/**/*", "{projectRoot}/vite.config.ts"], "outputs": ["{projectRoot}/dist"], }, "test": { "inputs": ["{projectRoot}/src/**/*", "{projectRoot}/vitest.config.ts"], "outputs": ["{projectRoot}/coverage"], }, }, }, } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // packages/my-lib/project.json { "targets": { "build": { "command": "vite build", "inputs": ["{projectRoot}/src/**/*", "{projectRoot}/vite.config.ts"], "outputs": ["{projectRoot}/dist"], }, "test": { "command": "vitest run", "inputs": ["{projectRoot}/src/**/*", "{projectRoot}/vitest.config.ts"], "outputs": ["{projectRoot}/coverage"], }, }, } ``` {% /tabitem %} {% /tabs %} Better, but you still repeat `inputs` and `outputs` across projects with the same tooling. ## Step 2: Nx plugins (inferred tasks) Nx plugins can read your existing tooling configuration files, like `vite.config.ts`, `jest.config.ts`, or `eslint.config.mjs`, and automatically create tasks with the correct caching settings. No `project.json` needed. ### Adding a plugin Try adding a plugin to your workspace: ```shell nx add @nx/vite ``` This installs `@nx/vite` and registers it in `nx.json`. Some plugins may also need `nx sync` to update workspace configuration files (e.g., TypeScript project references). See [maintain TypeScript monorepos](/docs/technologies/typescript/introduction#typescript-project-references-kept-in-sync) for details. After installing, check what tasks were inferred for one of your projects: ```shell nx show project my-app ``` You should see tasks like `build`, `test`, and `serve` that were automatically created from your `vite.config.ts` file. {% aside type="note" title="Prefixed target names" %} Some plugins use prefixed names (e.g., `next:build`, `next:dev`) to avoid conflicting with existing `package.json` scripts. You can rename these to `build`, `dev`, etc. in the plugin options in `nx.json`, then remove the redundant scripts from `package.json`. {% /aside %} The plugin reads your Vite configuration and sets up correct caching, inputs, and outputs without any manual configuration. The plugin is registered in `nx.json`: ```jsonc // nx.json { "plugins": [ { "plugin": "@nx/vite/plugin", "options": { "buildTargetName": "build", "testTargetName": "test", "serveTargetName": "serve", }, }, ], } ``` Now, any project with a `vite.config.ts` automatically gets `build`, `test`, and `serve` tasks, with correct `inputs`, `outputs`, and caching, without any `project.json` configuration. ### Seeing what's inferred Use `nx show project` to see all tasks for a project, including where they come from: ```shell nx show project my-lib ``` {% project_details title="Project details for my-lib" %} ```json { "project": { "name": "my-lib", "type": "lib", "data": { "root": "packages/my-lib", "targets": { "build": { "options": { "cwd": "packages/my-lib", "command": "vite build" }, "cache": true, "dependsOn": ["^build"], "inputs": ["production", "^production"], "outputs": ["{projectRoot}/dist"], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["vite"] } }, "test": { "options": { "cwd": "packages/my-lib", "command": "vitest run" }, "cache": true, "inputs": ["default", "^production"], "outputs": ["{projectRoot}/coverage"], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["vite"] } }, "lint": { "cache": true, "options": { "cwd": "packages/my-lib", "command": "eslint ." }, "inputs": ["default", "{workspaceRoot}/eslint.config.mjs"], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["eslint"] } } }, "name": "my-lib", "sourceRoot": "packages/my-lib/src", "projectType": "library", "tags": [], "implicitDependencies": [], "metadata": { "technologies": ["react"] } } }, "sourceMap": { "root": ["packages/my-lib/project.json", "nx/core/project-json"], "targets": ["packages/my-lib/project.json", "nx/core/project-json"], "targets.build": ["packages/my-lib/vite.config.ts", "@nx/vite/plugin"], "targets.build.command": [ "packages/my-lib/vite.config.ts", "@nx/vite/plugin" ], "targets.build.options": [ "packages/my-lib/vite.config.ts", "@nx/vite/plugin" ], "targets.build.cache": [ "packages/my-lib/vite.config.ts", "@nx/vite/plugin" ], "targets.build.inputs": [ "packages/my-lib/vite.config.ts", "@nx/vite/plugin" ], "targets.build.outputs": [ "packages/my-lib/vite.config.ts", "@nx/vite/plugin" ], "targets.test": ["packages/my-lib/vite.config.ts", "@nx/vite/plugin"], "targets.test.command": [ "packages/my-lib/vite.config.ts", "@nx/vite/plugin" ], "targets.test.options": [ "packages/my-lib/vite.config.ts", "@nx/vite/plugin" ], "targets.test.cache": ["packages/my-lib/vite.config.ts", "@nx/vite/plugin"], "targets.test.inputs": [ "packages/my-lib/vite.config.ts", "@nx/vite/plugin" ], "targets.test.outputs": [ "packages/my-lib/vite.config.ts", "@nx/vite/plugin" ], "targets.lint": ["packages/my-lib/eslint.config.mjs", "@nx/eslint/plugin"], "targets.lint.command": [ "packages/my-lib/eslint.config.mjs", "@nx/eslint/plugin" ], "targets.lint.options": [ "packages/my-lib/eslint.config.mjs", "@nx/eslint/plugin" ], "targets.lint.cache": [ "packages/my-lib/eslint.config.mjs", "@nx/eslint/plugin" ], "targets.lint.inputs": [ "packages/my-lib/eslint.config.mjs", "@nx/eslint/plugin" ] } } ``` {% /project_details %} The project details view shows each task's source (plugin, `targetDefaults`, or `project.json`) and its computed settings. ## How the configuration cascade works Task configuration can come from three sources, applied in this order: 1. **Plugin-inferred**: automatic from tooling config (lowest priority) 2. **targetDefaults**: shared defaults in `nx.json` 3. **Project-level**: explicit `project.json` or `package.json` config (highest priority) Each layer can override the previous one. This means you can use plugins for sensible defaults and only add project-level config when a project needs something different. Overriding replaces the inherited value. When a project needs to add to that configuration instead, whether it comes from `targetDefaults` or an inferred plugin task, use the spread token (`"..."`): {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```jsonc // packages/my-lib/package.json { "nx": { "targets": { "build": { "inputs": ["...", "{projectRoot}/extra.config.ts"], }, }, }, } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // packages/my-lib/project.json { "targets": { "build": { "inputs": ["...", "{projectRoot}/extra.config.ts"], }, }, } ``` {% /tabitem %} {% /tabs %} Here `"..."` expands to the `inputs` already inferred for `build`, so the project keeps them and adds `{projectRoot}/extra.config.ts`. For the full reference, see [spread token](/docs/reference/project-configuration#spread-token). ## This is optional Plugins are optional. The explicit task configuration from [Configuring Tasks](/docs/getting-started/tutorials/configuring-tasks) works perfectly well. Use plugins when: - You have many projects with the same tooling (Vite, Jest, ESLint, etc.) - You want caching configured automatically with correct `inputs`/`outputs` - You prefer minimal configuration files Stick with explicit configuration when: - You have unique build setups that plugins don't cover - You want full control over every task detail - Your team prefers explicit over implicit ## Learn more - [Inferred tasks](/docs/concepts/mental-model#inferred-tasks): how plugins detect and configure tasks - [Nx plugins](/docs/concepts/nx-plugins): the full plugin system - [Reduce repetitive configuration](/docs/kb/reduce-repetitive-configuration): step-by-step guide - [Extending Nx](/docs/kb/add-language-support): create your own plugins {% cards cols=2 %} {% card title="Previous: Understanding Your Workspace" description="Explore projects, graphs, and debug issues" url="/docs/getting-started/tutorials/understanding-your-workspace" /%} {% card title="Set Up CI" description="Connect Nx Cloud for remote caching and self-healing CI" url="/docs/getting-started/setup-ci" /%} {% /cards %} --- ## Running Tasks {% llm_copy_prompt title="Tutorial 4/7: Run tasks across your workspace" %} Help me run tasks in my Nx workspace efficiently. Use my existing workspace and projects for hands-on examples. Show me how to run a single task for one project, run multiple tasks across all projects with `nx run-many`, and understand how Nx orders task execution. Do not discuss caching or affected commands — those are covered in later tutorials. Stay on-topic: only teach what's covered on this page. Do not introduce concepts from later tutorials. Tutorial: {pageUrl} {% /llm_copy_prompt %} You've configured your tasks. Now how do you run them — for one project, for many, or only for what changed? The examples below use Vite and Vitest, but the concepts apply to any tool. Substitute your own build and test commands as needed. {% aside type="note" title="Tutorial Series" %} 1. [Crafting your workspace](/docs/getting-started/tutorials/crafting-your-workspace) 2. [Managing dependencies](/docs/getting-started/tutorials/managing-dependencies) 3. [Configuring tasks](/docs/getting-started/tutorials/configuring-tasks) 4. **Running tasks** (you are here) 5. [Caching](/docs/getting-started/tutorials/caching) 6. [Understanding your workspace](/docs/getting-started/tutorials/understanding-your-workspace) 7. [Reducing boilerplate](/docs/getting-started/tutorials/reducing-configuration-boilerplate) {% /aside %} This tutorial assumes you have an Nx workspace with configured tasks. If you're starting fresh, complete [Configuring Tasks](/docs/getting-started/tutorials/configuring-tasks) first. ## Running a single task Given a project with tasks defined in `package.json` (or `project.json` for non-JS projects): {% tabs %} {% tabitem label="package.json" %} ```jsonc // apps/my-app/package.json { "name": "my-app", "scripts": { "build": "vite build", "test": "vitest run", }, } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // apps/my-app/project.json { "targets": { "build": { "command": "vite build", }, "test": { "command": "vitest run", }, }, } ``` {% /tabitem %} {% /tabs %} Run a task with `nx run :`: ```shell nx run my-app:build ``` Or the shorthand, which works when the task name doesn't conflict with an Nx command: ```shell nx build my-app ``` You can also `cd` into a project directory and run without specifying the project name: ```shell cd apps/my-app nx build ``` Nx resolves the project from the current directory. Try running `nx build` or `nx test` in your own workspace to see it in action. ## Running multiple tasks Use `run-many` to run one or more tasks across multiple projects: ```shell # Run build for all projects that have a build task nx run-many --targets build # Run multiple tasks nx run-many --targets build test lint # Run tasks for specific projects only nx run-many --targets build --projects my-app ``` The `-t` and `-p` flags are shorthands for `--targets` and `--projects`. Nx runs tasks in parallel by default, respecting the [task dependencies](/docs/getting-started/tutorials/configuring-tasks) you've configured. For example, with this configuration: ```jsonc // nx.json { "targetDefaults": { "build": { "dependsOn": ["^build"], }, }, } ``` Running `nx run-many --targets build` builds dependencies first, then dependents: ![Diagram showing task execution order: shared-ui:build and utils:build run in step 1, then my-app:build and data-access:build run in step 2, driven by dependsOn configuration](../../../../assets/tutorials/task-dependency-order.svg) Nx handles the ordering automatically, even when running in parallel. ## Passing arguments Pass arguments directly to the task: ```shell # Use a named configuration nx build my-app --configuration=production # Forward arguments to the underlying tool nx test my-app --watch ``` For more details, see [pass args to commands](/docs/kb/pass-args-to-commands). ## Controlling parallelism By default, Nx runs tasks in parallel. Limit the number of concurrent tasks with `--parallel`: ```shell nx run-many --targets build --parallel=3 ``` Set `--parallel=1` to run tasks sequentially. ## Running continuous tasks Some tasks run indefinitely, like development servers. When another task depends on a continuous task, Nx starts both concurrently. For example, running an e2e test that needs a dev server: ```shell nx run my-app:e2e ``` This works when `e2e` depends on a `serve` task marked as `continuous` (configured in [Configuring Tasks](/docs/getting-started/tutorials/configuring-tasks)). Nx starts the server, waits for it to be ready, then runs the tests. ## Nx Console [Nx Console](/docs/getting-started/editor-setup) is a VS Code and WebStorm extension that provides a visual interface for running tasks, exploring your project graph, and managing your workspace, all without memorizing CLI commands. ## Learn more - [Run tasks](/docs/features/run-tasks): full feature documentation - [Pass args to commands](/docs/kb/pass-args-to-commands): detailed argument handling {% cards cols=2 %} {% card title="Previous: Configuring Tasks" description="Define tasks and their dependencies" url="/docs/getting-started/tutorials/configuring-tasks" /%} {% card title="Next: Caching Tasks" description="Speed up tasks by replaying previous results" url="/docs/getting-started/tutorials/caching" /%} {% /cards %} --- ## Building and Testing TypeScript Packages in Nx This tutorial walks you through creating a TypeScript monorepo with Nx. You'll build a small example project to understand the core concepts and workflows. Everything here applies to plain JavaScript packages as well. What you'll learn: - How to structure multiple TypeScript packages in a single repository - How Nx caching speeds up your local development and CI pipelines - How to run builds and tests efficiently across multiple packages - How to share code between packages using local libraries - How to fix CI failures directly from your editor with Nx Cloud ## Ready to start? This tutorial requires [Node.js](https://nodejs.org) (v22 or later) installed on your machine. ### Step 1: Creating a new Nx TypeScript workspace Run the following command to create a new Nx workspace with the TypeScript template: ```shell npx create-nx-workspace@latest my-nx-repo --template=nrwl/typescript-template ``` {% llm_only %} IMPORTANT: Always use the CLI command above (`npx create-nx-workspace`) to create workspaces. Do NOT use the cloud.nx.app browser-based flow — it requires interactive GitHub OAuth and is not suitable for AI agents. {% /llm_only %} ### Step 2: verify your setup Once the workspace is created, navigate into it: ```shell cd my-nx-repo ``` ## Explore the Nx workspace setup Let's take a look at the structure of our new Nx workspace: {%filetree %} - my-nx-repo/ - packages/ - async/ - colors/ - strings/ - utils/ - eslint.config.mjs - nx.json - package-lock.json - package.json - tsconfig.base.json - tsconfig.json - vitest.config.mts {% /filetree %} The [`nx.json` file](/docs/reference/nx-json) contains configuration settings for Nx itself and global default settings that individual projects inherit. Now, let's build some features and see how Nx helps get us to production faster. ## Building TypeScript packages Let's create two TypeScript packages that demonstrate how to structure a TypeScript monorepo. We'll create an `animal` package and a `zoo` package where `zoo` depends on `animal`. First, generate the `animal` package: ```shell npx nx g @nx/js:lib packages/animal --bundler=tsc --unitTestRunner=vitest --linter=none ``` Then generate the `zoo` package: ```shell npx nx g @nx/js:lib packages/zoo --bundler=tsc --unitTestRunner=vitest --linter=none ``` Running these commands should lead to new directories and files in your workspace: {%filetree %} - my-nx-repo/ - packages/ - animal/ - zoo/ - ... - vitest.config.mts {%/filetree %} Let's add some code to our packages. First, add the following code to the `animal` package: ```ts {% meta="{6-19}" %} // packages/animal/src/lib/animal.ts export function animal(): string { return 'animal'; } export interface Animal { name: string; sound: string; } const animals: Animal[] = [ { name: 'cow', sound: 'moo' }, { name: 'dog', sound: 'woof' }, { name: 'pig', sound: 'oink' }, ]; export function getRandomAnimal(): Animal { return animals[Math.floor(Math.random() * animals.length)]; } ``` Now let's update the `zoo` package to use the `animal` package: ```ts // packages/zoo/src/lib/zoo.ts import { getRandomAnimal } from '@org/animal'; export function zoo(): string { const result = getRandomAnimal(); return `${result.name} says ${result.sound}!`; } ``` Add the `@org/animal` dependency to `zoo`'s `package.json` (use `*` for npm or `workspace:*` for pnpm/yarn): ```json {% meta="{3-5}" %} // packages/zoo/package.json { "dependencies": { "@org/animal": "*" } } ``` Then link the packages: ```shell npm install ``` Now create an executable entry point for the zoo package: ```ts // packages/zoo/src/index.ts import { zoo } from './lib/zoo.js'; console.log(zoo()); ``` To build your packages, run: ```shell npx nx build animal ``` You can also use `npx nx run animal:build` as an alternative syntax. The `:` format works for any task in any project, which is useful when task names overlap with Nx commands. This creates a compiled version of your package in the `dist/packages/animal` folder. Since the `zoo` package depends on `animal`, building `zoo` will automatically build `animal` first: ```shell npx nx build zoo ``` You'll see both packages are built, with outputs in their respective `dist` folders. This is how you would prepare packages for use internally or for publishing to a package registry like NPM. You can also run the `zoo` package to see it in action: ```shell node packages/zoo/dist/index.js ``` ### Inferred tasks By default Nx simply runs your `package.json` scripts. However, you can also adopt [Nx technology plugins](/docs/technologies) that help abstract away some of the lower-level config and have Nx manage that. One such thing is to automatically identify tasks that can be run for your project from [tooling configuration files](/docs/concepts/mental-model#inferred-tasks) such as `package.json` scripts and TypeScript configuration. In `nx.json` there's already the `@nx/js` plugin registered which automatically identifies `typecheck` and `build` targets. ```json // nx.json { ... "plugins": [ { "plugin": "@nx/js/typescript", "options": { "typecheck": { "targetName": "typecheck" }, "build": { "targetName": "build", "configName": "tsconfig.lib.json", "buildDepsName": "build-deps", "watchDepsName": "watch-deps" } } } ] } ``` To view the tasks that Nx has detected, look in the [Nx Console](/docs/getting-started/editor-setup) project detail view or run: ```shell npx nx show project animal ``` {% project_details title="Project Details View (Simplified)" %} ```json { "project": { "name": "@org/animal", "type": "lib", "data": { "root": "packages/animal", "targets": { "typecheck": { "dependsOn": ["^typecheck"], "options": { "cwd": "packages/animal", "command": "tsc --build --emitDeclarationOnly" }, "cache": true, "inputs": [ "production", "^production", { "externalDependencies": ["typescript"] } ], "outputs": ["{projectRoot}/dist"], "executor": "nx:run-commands", "configurations": {} }, "build": { "options": { "cwd": "packages/animal", "command": "tsc --build tsconfig.lib.json" }, "cache": true, "dependsOn": ["^build"], "inputs": [ "production", "^production", { "externalDependencies": ["typescript"] } ], "outputs": ["{projectRoot}/dist"], "executor": "nx:run-commands", "configurations": {} } }, "name": "animal", "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "packages/animal/src", "projectType": "library", "tags": [], "implicitDependencies": [] } }, "sourceMap": { "root": ["packages/animal/project.json", "nx/core/project-json"], "targets": ["packages/animal/project.json", "nx/core/project-json"], "targets.typecheck": ["packages/animal/project.json", "@nx/js/typescript"], "targets.typecheck.command": [ "packages/animal/project.json", "@nx/js/typescript" ], "targets.typecheck.options": [ "packages/animal/project.json", "@nx/js/typescript" ], "targets.typecheck.cache": [ "packages/animal/project.json", "@nx/js/typescript" ], "targets.typecheck.dependsOn": [ "packages/animal/project.json", "@nx/js/typescript" ], "targets.typecheck.inputs": [ "packages/animal/project.json", "@nx/js/typescript" ], "targets.typecheck.outputs": [ "packages/animal/project.json", "@nx/js/typescript" ], "targets.typecheck.options.cwd": [ "packages/animal/project.json", "@nx/js/typescript" ], "targets.build": ["packages/animal/project.json", "@nx/js/typescript"], "targets.build.command": [ "packages/animal/project.json", "@nx/js/typescript" ], "targets.build.options": [ "packages/animal/project.json", "@nx/js/typescript" ], "targets.build.cache": [ "packages/animal/project.json", "@nx/js/typescript" ], "targets.build.dependsOn": [ "packages/animal/project.json", "@nx/js/typescript" ], "targets.build.inputs": [ "packages/animal/project.json", "@nx/js/typescript" ], "targets.build.outputs": [ "packages/animal/project.json", "@nx/js/typescript" ], "targets.build.options.cwd": [ "packages/animal/project.json", "@nx/js/typescript" ], "name": ["packages/animal/project.json", "nx/core/project-json"], "$schema": ["packages/animal/project.json", "nx/core/project-json"], "sourceRoot": ["packages/animal/project.json", "nx/core/project-json"], "projectType": ["packages/animal/project.json", "nx/core/project-json"], "tags": ["packages/animal/project.json", "nx/core/project-json"] } } ``` {% /project_details %} The `@nx/js` plugin automatically configures both the build and typecheck tasks based on your TypeScript configuration. Notice also how the outputs are set to `{projectRoot}/dist` - this is where your compiled TypeScript files will be placed, and it defined by the `outDir` option in `packages/animal/tsconfig.lib.json`. {% aside type="note" title="Overriding inferred task options" %} You can override the options for inferred tasks by modifying the [`targetDefaults` in `nx.json`](/docs/reference/nx-json#target-defaults) or setting a value in your [`project.json` file](/docs/reference/project-configuration). Nx will merge the values from the inferred tasks with the values you define in `targetDefaults` and in your specific project's configuration. {% /aside %} ## Code sharing with local libraries When you develop packages, creating shared utilities that multiple packages can use is a common pattern. This approach offers several benefits: - better separation of concerns - better reusability - more explicit APIs between different parts of your system - better scalability in CI by enabling independent test/lint/build commands for each package - most importantly: better caching because changes to one package don't invalidate the cache for unrelated packages ### Create a shared utilities library Let's create a shared utilities library that both our existing packages can use: ```shell npx nx g @nx/js:library packages/util --bundler=tsc --unitTestRunner=vitest --linter=none ``` Now we have: {%filetree %} - my-nx-repo/ - packages/ - animal/ - util/ - zoo/ - ... {%/filetree %} Let's add a utility function that our packages can share: ```ts {% meta="{6-12}" %} // packages/util/src/lib/util.ts export function util(): string { return 'util'; } export function formatMessage(prefix: string, message: string): string { return `[${prefix}] ${message}`; } export function getRandomItem(items: T[]): T { return items[Math.floor(Math.random() * items.length)]; } ``` ### Import the shared library This allows us to easily import them into other packages. Let's update our `animals` package to use the shared utility: ```ts {% meta="{2,19-21}" %} // packages/animals/src/lib/animals.ts import { getRandomItem } from '@org/util'; export function animal(): string { return 'animal'; } export interface Animal { name: string; sound: string; } const animals: Animal[] = [ { name: 'cow', sound: 'moo' }, { name: 'dog', sound: 'woof' }, { name: 'pig', sound: 'oink' }, ]; export function getRandomAnimal(): Animal { return getRandomItem(animals); } ``` And update the `zoo` package to use the formatting utility: ```ts {% meta="{3,7,8}" %} // packages/zoo/src/lib/zoo.ts import { getRandomAnimal } from '@org/animal'; import { formatMessage } from '@org/util'; export function zoo(): string { const result = getRandomAnimal(); const message = `${result.name} says ${result.sound}!`; return formatMessage('ZOO', message); } ``` Update the dependencies in each package's `package.json`: ```json {% meta="{3-5}" %} // packages/animal/package.json { "dependencies": { "@org/util": "*" } } ``` ```json {% meta="{3-6}" %} // packages/zoo/package.json { "dependencies": { "@org/animal": "*", "@org/util": "*" } } ``` Link the packages: ```shell npm install ``` Now when you run `npx nx build zoo`, Nx will automatically build all the dependencies in the correct order: first `util`, then `animal`, and finally `zoo`. Run the `zoo` package to see the updated output format: ```shell node packages/zoo/dist/index.js ``` ## Visualize your project structure Nx automatically detects the dependencies between the various parts of your workspace and builds a [project graph](/docs/features/explore-graph). This graph is used by Nx to perform various optimizations such as determining the correct order of execution when running tasks like `npx nx build`, enabling intelligent caching, and more. Interestingly, you can also visualize it. Just run: ```shell npx nx graph ``` You should be able to see something similar to the following in your browser. {% graph height="450px" %} ```json { "projects": [ { "name": "@org/animal", "type": "lib", "data": { "tags": [] } }, { "name": "@org/util", "type": "lib", "data": { "tags": [] } }, { "name": "@org/zoo", "type": "lib", "data": { "tags": [] } } ], "dependencies": { "@org/animal": [ { "source": "@org/animal", "target": "@org/util", "type": "static" } ], "@org/util": [], "@org/zoo": [ { "source": "@org/zoo", "target": "@org/animal", "type": "static" }, { "source": "@org/zoo", "target": "@org/util", "type": "static" } ] }, "affectedProjectIds": [], "focus": null, "groupByFolder": false } ``` {% /graph %} Let's create a git branch with our new packages so we can open a pull request later: ```shell git checkout -b add-zoo-packages git add . git commit -m 'add animal and zoo packages' ``` ## Building and testing - running multiple tasks Our packages come with preconfigured building and testing . Let's intentionally introduce a typo in our test to demonstrate the self-healing CI feature later. You can run tests for individual packages: ```shell npx nx build zoo ``` Or run multiple tasks in parallel across all packages: ```shell npx nx run-many -t build test ``` This is exactly what is configured in `.github/workflows/ci.yml` for the CI pipeline. The `run-many` command allows you to run multiple tasks across multiple projects in parallel, which is particularly useful in a monorepo setup. There is a test failure for the `zoo` package due to the updated message. Don't worry about it for now, we'll fix it in a moment with the help of Nx Cloud's self-healing feature. ### Local task cache One thing to highlight is that Nx is able to [cache the tasks you run](/docs/features/cache-task-results). Note that all of these targets are automatically cached by Nx. If you re-run a single one or all of them again, you'll see that the task completes immediately. In addition, there will be a note that a matching cache result was found and therefore the task was not run again. ```text {% title="npx nx run-many -t built test" frame="terminal" %} ✔ nx run @org/util:build ✔ nx run @org/util:test ✔ nx run @org/animal:test ✔ nx run @org/animal:build ✖ nx run @org/zoo:test ✔ nx run @org/zoo:build —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— NX Ran targets test, build for 3 projects (800ms) ✔ 5/6 succeeded [5 read from cache] ✖ 1/6 targets failed, including the following: - nx run @org/zoo:test ``` Not all tasks might be cacheable though. You can configure the `cache` settings in the `targetDefaults` property of the `nx.json` file. You can also [learn more about how caching works](/docs/features/cache-task-results). The next section deals with publishing packages to a registry like NPM, but if you are not interested in publishing your packages, you can skip to [the end](#next-steps). ## Manage releases If you decide to publish your packages to NPM, Nx can help you [manage the release process](/docs/features/manage-releases). Release management involves updating the version of your packages, populating a changelog, and publishing the new version to the NPM registry. First you'll need to define which projects Nx should manage releases for by setting the `release.projects` property in `nx.json`: ```json {% meta="{4-6}" %} // nx.json { ... "release": { "projects": ["packages/*"] } } ``` You'll also need to ensure that each package's `package.json` file sets `"private": false` so that Nx can publish them. If you have any packages that you do not want to publish, make sure to set `"private": true` in their `package.json`. Now you're ready to use the `nx release` command to publish your packages. The first time you run `nx release`, you need to add the `--first-release` flag so that Nx doesn't try to find the previous version to compare against. It's also recommended to use the `--dry-run` flag until you're sure about the results of the `nx release` command, then you can run it a final time without the `--dry-run` flag. To preview your first release, run: ```shell npx nx release --first-release --dry-run ``` The command will ask you a series of questions and then show you what the results would be. Once you are happy with the results, run it again without the `--dry-run` flag: ```shell npx nx release --first-release ``` After this first release, you can remove the `--first-release` flag and just run `nx release --dry-run`. There is also a [dedicated feature page](/docs/features/manage-releases) that goes into more detail about how to use the `nx release` command. ## Next steps Here are some things you can dive into next: - [Set up CI](/docs/getting-started/setup-ci) with remote caching and self-healing - Learn more about the [underlying mental model of Nx](/docs/concepts/mental-model) - Learn how to [migrate your existing project to Nx](/docs/kb/adding-to-existing-project) - [Learn more about Nx release for publishing packages](/docs/features/manage-releases) - Learn about [enforcing boundaries between projects](/docs/features/enforce-module-boundaries) Also, make sure you - ⭐️ [Star us on GitHub](https://github.com/nrwl/nx) to show your support and stay updated on new releases! - [Join the Official Nx Discord Server](https://go.nx.dev/community) to ask questions and find out the latest news about Nx. - [Follow Nx on Twitter](https://twitter.com/nxdevtools) to stay up to date with Nx news - [Read our Nx blog](https://nx.dev/blog) - [Subscribe to our Youtube channel](https://www.youtube.com/@nxdevtools) for demos and Nx insights --- ## Understanding Your Workspace {% llm_copy_prompt title="Tutorial 6/7: Explore and debug your workspace" %} Help me explore and debug my Nx workspace. Use my existing workspace and projects for hands-on examples. Run `nx show projects` to list all projects, `nx graph` to visualize dependencies, and `nx show project ` to inspect task details. Help me understand why specific dependencies exist and debug any caching or configuration issues. For machine-readable output (useful for scripting or AI agents), use `nx show project --json`. Stay on-topic: only teach what's covered on this page. Do not introduce concepts from later tutorials. Tutorial: {pageUrl} {% /llm_copy_prompt %} As your workspace grows to dozens or hundreds of projects, you need tools to explore it, debug unexpected behavior, and verify your configuration. Nx provides several commands and visualizations for this. {% aside type="note" title="Tutorial Series" %} 1. [Crafting your workspace](/docs/getting-started/tutorials/crafting-your-workspace) 2. [Managing dependencies](/docs/getting-started/tutorials/managing-dependencies) 3. [Configuring tasks](/docs/getting-started/tutorials/configuring-tasks) 4. [Running tasks](/docs/getting-started/tutorials/running-tasks) 5. [Caching](/docs/getting-started/tutorials/caching) 6. **Understanding your workspace** (you are here) 7. [Reducing boilerplate](/docs/getting-started/tutorials/reducing-configuration-boilerplate) {% /aside %} This tutorial assumes you have an Nx workspace with projects and configured tasks. If you're starting fresh, complete [Crafting Your Workspace](/docs/getting-started/tutorials/crafting-your-workspace) first. ## The project graph The best starting point for understanding your workspace is `nx graph`. It opens an interactive visualization with tabs for projects, tasks, and project details: ```shell nx graph ``` {% graph title="Project graph" height="400px" %} ```json { "projects": [ { "type": "app", "name": "my-app", "data": {} }, { "type": "app", "name": "admin", "data": {} }, { "type": "app", "name": "api", "data": {} }, { "type": "lib", "name": "shared-ui", "data": {} }, { "type": "lib", "name": "data-access", "data": {} }, { "type": "lib", "name": "auth", "data": {} }, { "type": "lib", "name": "utils", "data": {} }, { "type": "lib", "name": "models", "data": {} } ], "groupByFolder": false, "workspaceLayout": { "appsDir": "apps", "libsDir": "packages" }, "dependencies": { "my-app": [ { "target": "shared-ui", "source": "my-app", "type": "direct" }, { "target": "data-access", "source": "my-app", "type": "direct" }, { "target": "auth", "source": "my-app", "type": "direct" } ], "admin": [ { "target": "shared-ui", "source": "admin", "type": "direct" }, { "target": "data-access", "source": "admin", "type": "direct" }, { "target": "auth", "source": "admin", "type": "direct" } ], "api": [ { "target": "data-access", "source": "api", "type": "direct" }, { "target": "auth", "source": "api", "type": "direct" }, { "target": "models", "source": "api", "type": "direct" } ], "shared-ui": [ { "target": "utils", "source": "shared-ui", "type": "direct" } ], "data-access": [ { "target": "models", "source": "data-access", "type": "direct" }, { "target": "utils", "source": "data-access", "type": "direct" } ], "auth": [{ "target": "utils", "source": "auth", "type": "direct" }], "utils": [], "models": [] }, "affectedProjectIds": [] } ``` {% /graph %} The project graph shows every project and the dependencies between them. You can: - **Search** for specific projects - **Filter** to show only affected projects or specific groups - **Click edges** between projects to see which files create the dependency (import statements, `package.json` references) ### Why does this dependency exist? When you see an unexpected dependency in the graph, click the edge between the two projects. The sidebar shows which files contributed to the dependency: ![Project graph showing @my-workspace/my-app depending on @my-workspace/utils, with the edge selected and the sidebar listing the specific files that create the dependency](../../../../assets/tutorials/project-graph-edge.png) For scripting, write the graph to a JSON file: ```shell nx graph --file=/tmp/graph.json ``` ## The task graph Switch to the **Tasks** tab in the graph to visualize the task execution plan. Select a task (like `build`) to see which tasks run and in what order: ![Task graph showing @my-workspace/my-app:build depending on @my-workspace/utils:build, with the arrow indicating utils must build first](../../../../assets/tutorials/task-graph-view.png) You can also open this directly from the CLI: ```shell nx build my-app --graph ``` This is useful for verifying that your `dependsOn` configuration is correct. Write the task graph to a file for analysis: ```shell nx build my-app --graph=/tmp/task-graph.json ``` ## Inspecting project details Click any project in the graph to see its details, or use the CLI: ```shell nx show project my-app ``` {% project_details title="Project details" %} ```json { "project": { "name": "my-app", "type": "app", "data": { "root": "apps/my-app", "targets": { "build": { "options": { "cwd": "apps/my-app", "command": "vite build" }, "cache": true, "dependsOn": ["^build"], "inputs": ["production", "^production"], "outputs": ["{projectRoot}/dist"], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["vite"] } }, "test": { "options": { "cwd": "apps/my-app", "command": "vitest run" }, "cache": true, "inputs": ["default", "^production"], "outputs": ["{projectRoot}/coverage"], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["vite"] } } }, "name": "my-app", "sourceRoot": "apps/my-app/src", "projectType": "application", "tags": [], "metadata": { "technologies": ["react"] } } }, "sourceMap": { "root": ["apps/my-app/project.json", "nx/core/project-json"], "targets.build": ["apps/my-app/vite.config.ts", "@nx/vite/plugin"], "targets.test": ["apps/my-app/vite.config.ts", "@nx/vite/plugin"] } } ``` {% /project_details %} This shows every task, where its configuration comes from, its `inputs`, `outputs`, and `dependsOn`. To drill into a specific task: ```shell nx show target my-app:build ``` Add `--json` to either command for machine-readable output. ## Listing projects from the CLI ```shell nx show projects ``` Filter by criteria: ```shell # Only projects that have a build target nx show projects --with-target build # Only projects matching a pattern nx show projects --projects 'packages/*' ``` ## Debugging cache behavior If a task isn't caching as expected, inspect its inputs and outputs: ```shell nx show project my-app ``` Check the task's `inputs`. These are the files and values that contribute to the cache hash. If an input changes between runs, the cache is invalidated. Common causes of unexpected cache misses: - An untracked config file is included in inputs - An environment variable changed - A dependency was updated {% aside type="tip" title="Enforce correct inputs and outputs" %} Use [sandboxing](/docs/features/ci-features/sandboxing) to detect tasks that read files not listed in their `inputs` or write files not listed in their `outputs`. This catches configuration mistakes that lead to stale caches or missing artifacts. {% /aside %} ## Nx Console [Nx Console](/docs/getting-started/editor-setup) brings all of these capabilities into your editor. In VS Code or WebStorm, you can: - Browse projects and their targets in a tree view - View the project graph inline - Run tasks with a visual interface - Inspect project details without leaving the editor {% install_nx_console /%} ## Learn more - [Explore the graph](/docs/features/explore-graph): full graph exploration guide - [Mental model](/docs/concepts/mental-model): how Nx thinks about projects and tasks {% cards cols=2 %} {% card title="Previous: Caching Tasks" description="Speed up tasks by replaying previous results" url="/docs/getting-started/tutorials/caching" /%} {% card title="Next: Reducing Configuration Boilerplate" description="Automate task configuration with plugins" url="/docs/getting-started/tutorials/reducing-configuration-boilerplate" /%} {% /cards %} # Core Concepts --- ## Core Concepts Learn about all the different concepts Nx uses to manage your tasks and enhance your productivity. {% index_page_cards path="concepts" /%} --- ## CI Concepts {% index_page_cards path="concepts/ci-concepts" /%} --- ## Building blocks of fast CI Fast CI starts with three layers: fast individual tasks, less unnecessary work, and enough compute to run independent tasks concurrently. Nx plugins, the project graph, task orchestration, and Nx Cloud address those layers together. ## Use fast tools A CI pipeline runs tasks such as `build`, `test`, `lint`, and `e2e`. The tools behind those tasks set the minimum execution time for a cache miss. Use current tools and configuration before adding more CI machines. Nx can run tasks for any technology. Plugins provide deeper integrations for build tools such as [Vite](/docs/technologies/build-tools/vite/introduction) and [Rspack](/docs/technologies/build-tools/rspack/introduction), along with frameworks and test tools. Plugin migrations can update tool configuration when recommended settings change. The common plugin interface also makes it practical to compare tools without replacing Nx task orchestration. Browse the [plugin registry](/docs/plugin-registry) for prebuilt integrations. ## Reduce unnecessary work Most pull requests in a monorepo don't affect every project. Use [`nx affected`](/docs/features/ci-features/affected) to run tasks only for projects affected by a change and projects that depend on them. Some affected tasks may have already run with the same inputs. [Remote caching](/docs/features/ci-features/remote-cache) lets developer machines and CI jobs share those results, so Nx can restore terminal output and artifacts instead of repeating the task. Affected calculations reduce the task graph before execution. Caching removes repeated work from the remaining graph. For a scenario-by-scenario explanation, see [reduce waste in CI](/docs/kb/reduce-waste). ## Parallelize and distribute tasks Nx runs independent tasks in parallel while respecting [task pipeline](/docs/concepts/task-pipeline-configuration) dependencies. Set a workspace-wide concurrency limit with `parallel` in `nx.json`, or use a command-line option such as `nx affected -t test --parallel=4` for one run. A single machine eventually becomes the bottleneck. [Nx Agents](/docs/features/ci-features/distribute-task-execution) distribute the task graph across multiple machines and dynamically assign ready tasks to available agents. Remote caching transfers task artifacts between machines, including back to the main job. For the tradeoffs between parallelization and distribution in CI, see [parallelization and distribution](/docs/concepts/ci-concepts/parallelization-distribution). --- ## Parallelization and distribution Affected calculations and remote caching remove work that doesn't need to run. Nx still needs to execute cache misses, and the task graph determines which of those tasks can run at the same time. Nx can run independent tasks as parallel processes on one machine or distribute them across multiple machines. Both approaches preserve task dependencies. ## Parallelization on one machine Nx parallelizes ready tasks whenever you run a target. This applies to individual project commands, `run-many`, and `affected` commands. For example, Nx can build independent project dependencies concurrently before building the project that depends on them. Set the workspace-wide process limit with `parallel` in `nx.json`. Use `--parallel=` to change it for one command: ```shell nx affected -t build --parallel=2 ``` A single machine is the least complex option, and its logs and artifacts stay in one place. Its CPU and memory limit the number of useful parallel processes, so CI duration grows once the task graph exceeds that capacity. | Characteristic | Result | Notes | | -------------- | ------ | ------------------------------------------------------- | | Configuration | Pro | CI uses the same Nx commands as local development. | | Debugging | Pro | Logs and artifacts stay on one machine. | | Scale | Con | One machine limits CPU, memory, and useful parallelism. | ## Distribution across machines Distribution adds compute by running tasks on multiple CI jobs. The main job determines the tasks to run, while agent jobs execute assigned tasks. You can maintain that assignment yourself or use Nx Agents. ### Manual distribution A common manual strategy divides work into fixed bins, often by target: ```yaml title="main-job.yml" # Get affected projects and make the list available to agent jobs. - nx show projects --affected --json > affected-projects.json - node store-affected-projects.js ``` ```yaml title="lint-agent.yml" - nx run-many -t lint --projects=$PROJECTS ``` ```yaml title="test-agent.yml" - nx run-many -t test --projects=$PROJECTS ``` ```yaml title="build-agent.yml" - nx run-many -t build --projects=$PROJECTS ``` ![CI tasks divided into fixed bins](../../../../assets/concepts/ci-concepts/binning.svg) Fixed bins often finish at different times, leaving some machines idle. They can also duplicate work when a task in one bin depends on a task assigned to another bin. Remote caching reduces some duplication, but scripts still need to coordinate task order and cache availability. The ideal bins change as the project graph and affected set change. Maintaining that logic becomes a CI responsibility. | Characteristic | Result | Notes | | -------------- | ------ | ---------------------------------------------------- | | Configuration | Con | Custom scripts assign tasks and require maintenance. | | Debugging | Con | Logs and artifacts start on separate machines. | | Scale | Pro | More machines provide more CPU and memory. | ### Distribution with Nx Agents Nx Agents dynamically assign ready tasks from the task graph to available agent machines. The number and type of agents live in a config file, and the main job keeps the same Nx commands used for a single-machine pipeline: ```yaml # .nx/ci-config.yaml # Use eight agents and stop them after build tasks finish. dte: distribute-on: 8 linux-medium-js lifecycle: stop-after: - build ``` ```yaml title="main-job.yml" - npx nx start-nx-agents - nx affected -t lint test build ``` ![CI tasks distributed across three agents](../../../../assets/concepts/ci-concepts/3agents.svg) Nx Agents account for task dependencies and the affected set on each CI run. They use remote caching to move artifacts between agents and collate results on the main job. Dynamic assignment reduces the idle time caused by fixed bins without requiring a custom scheduler. | Characteristic | Result | Notes | | -------------- | ------ | ----------------------------------------------------------------------- | | Configuration | Pro | Existing Nx task commands remain unchanged. | | Debugging | Pro | Nx collates logs and artifacts on the main job. | | Scale | Pro | Dynamic assignment uses available agents across the current task graph. | Use [Nx Agents](/docs/features/ci-features/distribute-task-execution) when one machine no longer provides enough parallelism and maintaining manual distribution isn't worthwhile. You can generate a workflow with the [CI workflow generator](/docs/reference/workspace/generators#ci-workflow) or follow a [CI setup guide](/docs/kb/setup-ci). --- ## How caching works Before running a cacheable task, Nx calculates a hash from the task's inputs. Two runs with the same hash represent the same computation, so Nx can reuse the earlier result. A task hash can include: - Project source files and files from project dependencies. - Relevant workspace configuration. - Versions of external dependencies. - Runtime values, such as the operating system and CPU architecture. - Command-line arguments passed to the task. ![Inputs used to calculate a task hash](../../../assets/concepts/caching/nx-hashing.svg) You can customize these inputs for each target. For example, a lint target might depend on source files but not documentation, while a build target might depend on generated declaration files from its dependencies. ## Cache lookup and storage Nx checks the local cache after calculating the hash. If it doesn't find a result and remote caching is configured, Nx checks the remote cache. On a cache hit, Nx restores the cached output files and prints the stored terminal output. ![Nx checking local and remote caches](../../../assets/concepts/caching/cache.svg) On a cache miss, Nx runs the task and stores its terminal output and configured output files in the local cache. Nx also stores the result in the remote cache when remote caching is configured and the current authentication settings permit writes. Nx processes each node in a task graph independently. Some tasks can be cache hits while other tasks run normally. ![A large task graph with cached tasks](../../../assets/concepts/caching/task-graph-big.svg) ## Cache inputs and outputs Each cacheable target has inputs and outputs: - Inputs determine the task hash. - Outputs identify files and directories to store and restore. Plugins infer these settings when they understand the underlying tool. You can override inferred values globally in `nx.json` or for one project in `package.json` or `project.json`. For detailed configuration options, see [configure inputs](/docs/kb/configure-inputs) and [configure outputs](/docs/kb/configure-outputs). ## What Nx caches Nx caches task results at the process level, regardless of whether the task builds, tests, lints, or runs another tool. A cache entry contains: - Terminal output written to standard output and standard error. - Files and directories matched by the target's `outputs` configuration. - The task hash used to identify the entry. Nx doesn't store the source files and other inputs inside the cache entry. It uses their hashes to calculate the task hash. The following project-level configuration overrides the inferred outputs for `build`: ```jsonc title="packages/myapp/package.json" { "name": "myapp", "nx": { "targets": { "build": { "outputs": ["{projectRoot}/dist"], }, }, }, } ``` Use `targetDefaults` to apply the same output configuration to matching targets across the workspace: ```jsonc title="nx.json" { "targetDefaults": { "build": { "outputs": ["{projectRoot}/dist", "{projectRoot}/build"], }, }, } ``` When `outputs` isn't configured, Nx uses an executor's `outputPath` option as a backward-compatible fallback. For `build` and `prepare` targets without an `outputPath`, Nx checks common `dist`, `build`, and `public` directories. For reliable caching, prefer outputs inferred by a plugin or configure `outputs` explicitly. {% aside type="note" title="Output paths and executor options" %} An executor's `outputPath` option doesn't automatically become part of `outputs` when `outputs` is already configured. Reference the option explicitly with `"outputs": ["{options.outputPath}"]` when needed. {% /aside %} ## Configure inputs Nx includes all files under a project root by default. Plugins and `namedInputs` often provide more focused input sets. You can exclude files that don't affect a target or include environment variables, runtime commands, and external dependencies. ```jsonc title="nx.json" { "targetDefaults": { "build": { "inputs": ["production", "^production"], }, "test": { "inputs": ["default", "^production"], }, }, } ``` The `^production` input includes the `production` inputs of project dependencies. Project-level configuration can extend or replace these values when one project needs different hashing behavior. ```json title="packages/some-project/project.json" { "name": "some-project", "targets": { "build": { "inputs": ["...", "!{projectRoot}/**/*.md"], ... }, "test": { "inputs": [...] } ... } } ``` Inputs may include the following: - Source files that affect the task. - Environment variables that affect the task. - Runtime inputs that affect the task. - Command-line arguments passed to the task. Learn more about fine tuning caching in the [Fine-tuning Caching with Inputs page](/docs/kb/configure-inputs). ## Command-line arguments Arguments passed to a task affect its hash because they can change the result. For example, these commands produce different hashes: ```shell nx build myapp nx build myapp --sourcemap ``` Different Nx command forms that create the same task with the same task arguments produce the same hash. These commands are equivalent for caching: ```shell nx build myapp nx run-many -t build -p myapp ``` When you run a target for multiple projects, Nx calculates a separate hash for each task. One project can be restored from cache while another project runs. {% aside type="tip" title="Learn by doing" %} Follow the [caching tutorial](/docs/getting-started/tutorials/caching) to inspect cache hits in a workspace. {% /aside %} For setup and troubleshooting options, see [cache task results](/docs/features/cache-task-results). --- ## Mental Model Nx coordinates work in your workspace through a few core concepts: the project graph, inferred tasks, the task graph, affected commands, and computation caching. [Plugins](/docs/concepts/nx-plugins) apply these concepts to the tools and frameworks in your repository. ## The project graph The project graph represents the source code in your repository and external dependencies such as Vite, React, and Angular. ![A project graph with applications and libraries](../../../assets/concepts/mental-model/project-graph.svg) Nx analyzes your file system to detect projects. It identifies projects from a `project.json` file or a `package.json` file included in your package manager's workspaces configuration. Plugins can also customize how Nx identifies projects. You can manually define dependencies between the project nodes, but you don't have to do it very often. Nx analyzes source code, your installed dependencies, and TypeScript configuration to figure out these dependencies for you. Nx also stores the cached project graph, so it only reanalyzes the files you have changed. Nx caches the project graph and recomputes the parts affected by file changes. The updated graph reflects the current state of your workspace. ## Inferred tasks Every project has tasks you can run, such as `build`, `test`, or `lint`. Tasks can come from three places: - Nx can use a script in the project's `package.json`. - Nx can use a target defined in the project's `project.json`. - A plugin can infer a task from tool configuration. Your tools already describe how they run. A `vite.config.ts` file defines where the build output goes. Rather than asking you to repeat that information, Nx plugins read the tool configuration and create tasks for you. The `@nx/vite` plugin, for example, detects `vite.config.ts` files and infers tasks such as `build`, `dev`, and `preview` with the correct command, cache settings, [inputs](/docs/kb/configure-inputs), [outputs](/docs/kb/configure-outputs), and task dependencies. To see every task Nx detected for a project, and where each setting came from, show the project details: ```shell nx show project my-project ``` When a plugin infers settings you want to change, override them. Nx merges task configuration from three sources, with later sources winning: 1. Task configuration inferred by plugins registered in `nx.json`. 2. `targetDefaults` in `nx.json`. 3. The project's own configuration in `package.json` or `project.json`. For all configuration options, see [project configuration](/docs/reference/project-configuration). To move an older workspace onto inferred tasks, follow the [Migrate to Inferred Tasks](/docs/kb/convert-to-inferred) guide. ## The task graph Nx creates a task graph whenever you run tasks. Each node is a task, which is a specific target invoked for a project, such as `myapp:build`. Edges represent the dependencies between tasks. For example, `nx test lib` creates a task graph with one node: {% graph title="Task graph for nx test lib" height="160px" type="task" %} ```json { "projects": [ { "name": "lib", "type": "lib", "data": { "root": "libs/lib", "projectType": "library", "tags": [], "targets": { "test": {} } } } ], "taskIds": ["lib:test"], "taskGraph": { "roots": ["lib:test"], "tasks": { "lib:test": { "id": "lib:test", "target": { "project": "lib", "target": "test" }, "outputs": [], "projectRoot": "libs/lib", "overrides": {}, "cache": true, "parallelism": true, "continuous": false } }, "dependencies": { "lib:test": [] }, "continuousDependencies": { "lib:test": [] } } } ``` {% /graph %} The project graph and task graph aren't identical. In this project graph, both applications depend on `lib`: Nx uses the [project graph](#the-project-graph) to figure out which tasks exist, but the task graph isn't a mirror of the project graph. In the case below, `app1` and `app2` depend on `lib`: {% graph height="200px" type="project" %} ```json { "projects": [ { "name": "app1", "type": "app", "data": { "tags": [] } }, { "name": "app2", "type": "app", "data": { "tags": [] } }, { "name": "app1", "type": "app", "data": { "tags": [] } } ], "dependencies": { "app1": [{ "source": "app1", "target": "lib", "type": "static" }], "app2": [{ "source": "app2", "target": "lib", "type": "static" }], "lib": [] }, "workspaceLayout": { "appsDir": "apps", "libsDir": "libs" }, "affectedProjectIds": [], "focus": null, "groupByFolder": false } ``` {% /graph %} Running `nx run-many -t test -p app1 app2 lib` creates a task graph with three disconnected nodes: ![Tasks running in dependency order and in parallel](../../../assets/concepts/mental-model/task-graph-execution.svg) {% graph height="200px" type="task" %} ```json { "projects": [ { "name": "app1", "type": "app", "data": { "tags": [], "targets": { "test": {} } } }, { "name": "app2", "type": "app", "data": { "tags": [], "targets": { "test": {} } } }, { "name": "lib", "type": "lib", "data": { "tags": [], "targets": { "test": {} } } } ], "taskIds": ["app1:test", "app2:test", "lib:test"], "taskGraph": { "roots": ["app1:test", "app2:test", "lib:test"], "tasks": { "app1:test": { "id": "app1:test", "target": { "project": "app1", "target": "test" }, "projectRoot": "apps/app1", "overrides": {} }, "app2:test": { "id": "app2:test", "target": { "project": "app2", "target": "test" }, "projectRoot": "apps/app2", "overrides": {} }, "lib:test": { "id": "lib:test", "target": { "project": "lib", "target": "test" }, "projectRoot": "libs/lib", "overrides": {} } }, "dependencies": { "app1:test": [], "app2:test": [], "lib:test": [] } } } ``` {% /graph %} Even though the apps depend on `lib`, testing `app1` doesn't depend on testing `lib`. This means that the three tasks can run in parallel. Now let's make the test target depend on the test tasks of its dependencies: ```json title="nx.json" { "targetDefaults": { "test": { "dependsOn": ["^test"] } } } ``` With this, running the same test command creates the following task graph: {% graph height="200px" type="task" %} ```json { "projects": [ { "name": "app1", "type": "app", "data": { "tags": [], "targets": { "test": {} } } }, { "name": "app2", "type": "app", "data": { "tags": [], "targets": { "test": {} } } }, { "name": "lib", "type": "lib", "data": { "tags": [], "targets": { "test": {} } } } ], "taskIds": ["app1:test", "app2:test", "lib:test"], "taskGraph": { "roots": ["lib:test"], "tasks": { "app1:test": { "id": "app1:test", "target": { "project": "app1", "target": "test" }, "projectRoot": "apps/app1", "overrides": {} }, "app2:test": { "id": "app2:test", "target": { "project": "app2", "target": "test" }, "projectRoot": "apps/app2", "overrides": {} }, "lib:test": { "id": "lib:test", "target": { "project": "lib", "target": "test" }, "projectRoot": "libs/lib", "overrides": {} } }, "dependencies": { "app1:test": ["lib:test"], "app2:test": ["lib:test"], "lib:test": [] } } } ``` {% /graph %} This often makes more sense for builds, where to build `app1`, you want to build `lib` first. You can also define similar relationships between targets of the same project, including a test target that depends on the build. Learn more about configuring task pipelines in [Task Pipeline Configuration](/docs/concepts/task-pipeline-configuration). A task graph can contain different targets that run in parallel. For instance, while Nx builds `app2`, it can test `app1` at the same time. ![task-graph-execution](../../../assets/concepts/mental-model/task-graph-execution.svg) Nx also runs the tasks in the task graph in the right order. Nx executing tasks in parallel speeds up your overall execution time. ## Affected commands Running `nx test app1` creates the `app1:test` task and any tasks it depends on. Running `nx run-many -t test -p app1 lib` creates tasks for both projects. Without a project filter, `nx run-many -t test` creates a test task for every project with that target. Use `nx affected -t test` to limit the task graph to projects affected by your changes. Nx maps changed files to projects, applies plugin-specific change analysis, and follows the project graph to include dependent projects. It then runs the requested target for that set. For example, if a change affects `lib`, and both `app1` and `app2` depend on it, the affected project set contains all three projects. ![Affected projects after a library change](../../../assets/concepts/mental-model/affected.svg) When you run `nx affected -t test`, Nx identifies the files changed in your PR and follows their project dependencies and imports to determine which projects the change can affect. It then runs the `run-many` command with that list. For instance, when a PR changes `lib`, Nx detects that `app1` and `app2` depend on `lib`, then invokes `nx run-many -t test -p app1 app2 lib`. ![affected](../../../assets/concepts/mental-model/affected.svg) Nx analyzes the nature of the changes. For example, if you change the version of Next.js in the package.json, Nx knows that `app2` cannot be affected by it, so it only retests `app1`. ## Computation hashing and caching Before running a task, Nx computes a hash based on source files, configuration, dependencies, and other inputs. If the hash matches a previous run, Nx replays the cached result, including terminal output and file artifacts. If not, Nx runs the task and stores the result for next time. ![Inputs used to calculate a task hash](../../../assets/concepts/mental-model/computation-hashing.svg) Nx checks the local cache first and then checks the [remote cache](/docs/features/ci-features/remote-cache) when one is configured. A cache hit has the same result as executing the task, but avoids the computation. ![Nx checking local and remote caches](../../../assets/concepts/mental-model/cache.svg) For the complete hashing and cache model, see [how caching works](/docs/concepts/how-caching-works). ## Distributed task execution For large workspaces, even with caching, running all tasks on a single machine can be slow. [Nx Agents](/docs/features/ci-features/distribute-task-execution) distribute the task graph across multiple machines, running tasks in parallel while using remote caching to share artifacts between agents. CI receives the results as though everything ran on a single machine. ![Distribution](../../../assets/concepts/mental-model/dte.svg) ## How it fits together - Nx analyzes your source code to create a project graph. - Nx plugins infer tasks from your tool configuration, so most task settings don't have to be written by hand. - Nx uses the project graph and task configuration to create a task graph and run tasks in the right order. - Code-change analysis creates the smallest task graph for your PR. - [Computation caching](/docs/features/cache-task-results) makes sure the same computation never runs twice. You can plug in a cache provider and distribute cached results. --- ## How Nx plugins work Nx plugins package knowledge about a tool or framework so every project doesn't need to recreate the same integration. A plugin can contribute project graph data, inferred tasks, generators, migrations, and executors. For example, plugins can: - [Configure Nx cache settings](/docs/concepts/mental-model#inferred-tasks) for a tool. The [`@nx/vite`](/docs/technologies/build-tools/vite/introduction) plugin can automatically configure the [inputs](/docs/kb/configure-inputs) and [outputs](/docs/kb/configure-outputs) for a `build` task based on the settings in the `vite.config.ts` file it uses. - [Update tooling configuration](/docs/features/automate-updating-dependencies) when upgrading the tool version. When Storybook 7 introduced a [new format](https://storybook.js.org/blog/storybook-csf3-is-here) for their configuration files, anyone using the [`@nx/storybook`](/docs/technologies/test-tools/storybook/introduction) plugin could automatically apply those changes to their repository when upgrading. - [Set up a tool](/docs/features/generate-code) for the first time. With the [`@nx/playwright`](/docs/technologies/test-tools/playwright/introduction) plugin installed, you can use the `@nx/playwright:configuration` code generator to set up Playwright tests in an existing project. - [Run a tool in an advanced way](/docs/kb/executors-and-configurations). The [`@nx/js`](/docs/technologies/typescript/introduction) plugin's [`@nx/js:tsc` executor](/docs/technologies/typescript/executors#tsc) combines the Nx understanding of your repository with Typescript's native batch mode feature to make your builds [even more performant](/docs/kb/enable-tsc-batch-mode). ## Plugin features {% linkcard title="Infer tasks" href="/docs/concepts/mental-model#inferred-tasks" description="Automatically configure Nx settings for tasks based on tooling configuration" /%} {% linkcard title="Generate Code" href="/docs/features/generate-code" description="Generate and modify code to set up and use the tool or framework" /%} {% linkcard title="Maintain Dependencies" href="/docs/features/automate-updating-dependencies" description="Automatically update package versions and tooling configuration" /%} {% linkcard title="Enhance Tooling with Executors" href="/docs/kb/executors-and-configurations" description="Run a tool in an advanced way that may not be possible from the command line" /%} ## Find or build a plugin Nx maintains plugins for frameworks and tools across frontend, backend, testing, and build ecosystems. Community plugins extend that ecosystem with additional technologies. {% aside type="tip" title="Learn by doing" %} Follow the [reducing configuration boilerplate tutorial](/docs/getting-started/tutorials/reducing-configuration-boilerplate) to add a plugin and inspect its inferred tasks. {% /aside %} {% linkcard title="Plugin registry" href="/docs/plugin-registry" description="Browse official and community plugins" /%} {% linkcard title="Build a plugin" href="/docs/kb/organization-specific-plugin" description="Create a plugin for your organization or the community" /%} {% linkcard title="Multi-language support" href="/docs/features/multi-language-support" description="Teach Nx to create projects, tasks, and dependencies for any language" /%} --- ## Sync Generators Sync generators ensure that your repository is maintained in a correct state. One specific application is to use the project graph to update files. These can be global configuration files or scripts, or at the task level to ensure that files are in sync before a task is run. Sync Generator Examples: - Update a custom CI script with binning strategies based on the current project graph - Update TypeScript config files with project references based on the current project graph - Ensure code is formatted in a specific way before CI is run ## Task sync generators Sync generators can be associated with a particular task. Nx will use the sync generator to ensure that code is correctly configured before running the task. Nx does this in different ways, depending on whether the task is being run on a developer machine or in CI. On a developer machine, the sync generator is run in `--dry-run` mode and if files would be changed by the generator, the user is prompted to run the generator or skip it. This prompt can be disabled by setting the `sync.applyChanges` property to `true` or `false` in the `nx.json` file. ```json title="nx.json" {4-6} { "$schema": "packages/nx/schemas/nx-schema.json", ... "sync": { "applyChanges": true } } ``` {% aside type="caution" title="Opting out of automatic sync" %} If you set `sync.applyChanges` to `false`, then developers must run `nx sync` manually before pushing changes. Otherwise, CI may fail due to the workspace being out of sync. {% /aside %} In CI, the sync generator is run in `--dry-run` mode and if files would be changed by the generator, the task fails with an error provided by the sync generator. The sync generator can be skipped in CI by passing the `--skip-sync` flag when executing the task, or you can skip an individual sync generator by adding that generator to the `sync.disabledTaskSyncGenerators` in `nx.json`. ```json title="nx.json" {4-6} { "$schema": "packages/nx/schemas/nx-schema.json", ... "sync": { "disabledTaskSyncGenerators": ["@nx/js:typescript-sync"] } } ``` Use the project details view to **find registered sync generators** for a given task. ```shell nx show project ``` The above command opens up the project details view, and the registered sync generators are under the **Sync Generators** for each target. Most sync generators are inferred when using an [inference plugin](/docs/concepts/mental-model#inferred-tasks). For example, the `@nx/js/typescript` plugin registers the `@nx/js:typescript-sync` generator on `build` and `typecheck` targets. {% project_details title="Project Details View" expandedTargets=["build"] %} ```json { "project": { "name": "foo", "data": { "root": " packages/foo", "projectType": "library", "targets": { "build": { "dependsOn": ["^build"], "cache": true, "inputs": [ "{workspaceRoot}/tsconfig.base.json", "{projectRoot}/tsconfig.lib.json", "{projectRoot}/src/**/*.ts" ], "outputs": ["{workspaceRoot}/packages/foo/dist"], "syncGenerators": ["@nx/js:typescript-sync"], "executor": "nx:run-commands", "options": { "command": "tsc --build tsconfig.lib.json --pretty --verbose" } } } } }, "sourceMap": { "targets": ["packages/foo/tsconfig.ts", "@nx/js/typescript"], "targets.build": ["packages/foo/tsconfig.ts", "@nx/js/typescript"] } } ``` {% /project_details %} Task sync generators can be thought of like the `dependsOn` property, but for generators instead of task dependencies. To [register a generator](/docs/kb/create-sync-generator) as a sync generator for a particular task, add the generator to the `syncGenerators` property of the task configuration. ## Global sync generators Global sync generators are not associated with a particular task and are executed only when the `nx sync` or `nx sync:check` command is explicitly run. They are [registered](/docs/kb/create-sync-generator) in the `nx.json` file with the `sync.globalGenerators` property. ## Sync the project graph and the file system Nx processes the file system in order to [create the project graph](/docs/features/explore-graph) which is used to run tasks in the correct order and determine project dependencies. Sync generators allow you to also go the other direction and use the project graph to update the file system. **File System:** ```text └─ myorg ├─ apps │ ├─ app1 │ └─ app2 ├─ libs │ └─ lib ├─ nx.json └─ package.json ``` **Project Graph:** {% graph title="Project Graph" height="200px" type="project" %} ```json { "projects": [ { "name": "app1", "type": "app", "data": { "tags": [], "targets": { "test": {} } } }, { "name": "app2", "type": "app", "data": { "tags": [], "targets": { "test": {} } } }, { "name": "lib", "type": "lib", "data": { "tags": [], "targets": { "test": {} } } } ], "dependencies": { "app1": [ { "source": "app1", "target": "lib", "type": "static" } ], "app2": [ { "source": "app2", "target": "lib", "type": "static" } ], "lib": [] }, "workspaceLayout": { "appsDir": "apps", "libsDir": "libs" }, "affectedProjectIds": [], "focus": null, "groupByFolder": false, "exclude": [] } ``` {% /graph %} The ability to update the file system from the project graph makes it possible to use the Nx project graph to change the behavior of other tools that are not part of the Nx ecosystem. ## Run `nx sync:check` in CI Task sync generators are executed whenever their task is run, but global sync generators need to be triggered manually with `nx sync`. In order to effectively use sync generators, make sure to add `nx sync:check` to the beginning of your CI scripts so that CI can fail quickly if the code is out of sync. It is also helpful to run `nx sync` in a pre-commit or pre-push Git hook to encourage developers to commit code that is already in sync. --- ## Task pipeline configuration Projects in a monorepo often depend on one another. A build for an application might require build artifacts from several libraries, while unrelated builds can run at the same time. Nx uses the project graph and task pipeline configuration to determine that order. In this project graph, `myreactapp` depends on `feat-products`, which depends on `shared-ui`: {% graph height="450px" %} ```json { "projects": [ { "name": "myreactapp", "type": "app", "data": { "tags": [] } }, { "name": "shared-ui", "type": "lib", "data": { "tags": [] } }, { "name": "feat-products", "type": "lib", "data": { "tags": [] } } ], "dependencies": { "myreactapp": [ { "source": "myreactapp", "target": "feat-products", "type": "static" } ], "shared-ui": [], "feat-products": [ { "source": "feat-products", "target": "shared-ui", "type": "static" } ] }, "workspaceLayout": { "appsDir": "", "libsDir": "" }, "affectedProjectIds": [], "focus": null, "groupByFolder": false } ``` {% /graph %} A script could build `shared-ui`, then `feat-products`, and then `myreactapp`. That script would duplicate information already available in the project graph and would need updates whenever project dependencies changed. Task pipelines express the ordering rule instead. Nx applies the rule to the current project graph and runs as many independent tasks in parallel as the pipeline permits. ![Tasks running in dependency order and in parallel](../../../assets/concepts/mental-model/task-graph-execution.svg) ## Define task dependencies Use `dependsOn` in `targetDefaults` to define workspace-wide task dependencies: ```jsonc title="nx.json" { "targetDefaults": { "build": { "dependsOn": ["^build", "prebuild"], }, "test": { "dependsOn": ["build"], }, }, } ``` With this configuration, `nx test myproj` produces the following sequence: 1. Nx adds `myproj:test` to the task graph. 1. Because `test` depends on `build`, Nx adds `myproj:build`. 1. Because `build` depends on `prebuild`, Nx adds `myproj:prebuild`. 1. The `^build` entry adds `build` tasks for projects that `myproj` depends on. 1. Nx runs each task when its dependencies are complete. Nx doesn't wait for every build to finish before starting every test. It runs independent tasks in parallel while respecting the graph constraints. The caret in `^build` means "run the target on project dependencies." Without the caret, `prebuild` and `build` refer to targets on the same project. Define shared rules in `nx.json`. Use the `nx.targets` section of `package.json`, or `project.json`, when one project needs a different pipeline. {% aside type="tip" title="Learn by doing" %} Follow the [configuring tasks tutorial](/docs/getting-started/tutorials/configuring-tasks) to create a task pipeline in a workspace. {% /aside %} For all `dependsOn` forms and project-specific examples, see [define a task pipeline](/docs/kb/defining-task-pipeline). --- ## Managing configuration files An Nx workspace contains configuration for Nx and for the tools used by each project. Nx plugins read both sources and combine them into resolved project configuration. You can inspect or override that configuration when a project needs different behavior. ## Kinds of configuration Configuration varies along two dimensions: - Type: Nx configuration controls task orchestration and caching, while tool configuration controls tools such as Vite, ESLint, and TypeScript. - Scope: Workspace configuration applies broadly, while project configuration applies to one project. A workspace might contain these files: | Scope | Nx configuration | TypeScript configuration | | --------- | ------------------------------ | ------------------------------- | | Workspace | `/nx.json` | `/tsconfig.base.json` | | Project | `/packages/myapp/package.json` | `/packages/myapp/tsconfig.json` | Projects can store Nx-specific settings in the `nx` section of `package.json`. `project.json` is also supported for project configuration, but most task settings don't need to be defined in either file when a plugin can infer them. ## How plugins reduce configuration 1. Abstracting away common tooling configuration settings so that if your project is using the tool in the most common way, you won't need to worry about configuration at all. The default settings for any Nx plugin are intended to work without modification for most projects in the community. 2. Allowing you to [provide `targetDefaults`](/docs/kb/reduce-repetitive-configuration) so that the most common settings for projects in your repo can all be defined in one place. Then, only projects that are exceptions need to overwrite those settings. With the judicious application of this method, larger repositories can actually have less lines of configuration after adding Nx than before. Use `targetDefaults` in `nx.json` for settings shared by matching targets. Project-level configuration should contain only exceptions or tasks that no plugin can infer. If you need to track down the value of a specific configuration property (say `runInBand` for `jest` on the `/apps/my-app` project) you need to look in the following locations. The configuration settings are merged with priority being given to the file higher up in the list. 1. In `/apps/my-app/project.json`, the `options` listed under the `test` target. 2. In `/nx.json`, the `targetDefaults` entries matching the `test` target. 3. One of the `test` target options references `/apps/my-app/jest.config.ts` 4. Which extends `/jest.config.ts` ```text repo/ ├── apps/ │ └── my-app/ │ ├── jest.config.ts │ └── project.json ├── jest.config.ts └── nx.json ``` ```jsonc title="packages/myapp/package.json" { "name": "myapp", "nx": { "targets": { "build": { "inputs": ["production", "^production", "{workspaceRoot}/brand/**"], }, }, }, } ``` The project-level `inputs` value overrides the corresponding inferred or default value for `myapp:build`. ## Resolve a target configuration Nx merges target configuration in this order, from least specific to most specific: 1. Configuration inferred by plugins listed in `nx.json`. 1. Matching `targetDefaults` from `nx.json`. 1. Project configuration from `package.json` or `project.json`. Tool configuration remains the source of truth for the tool itself. A plugin translates relevant values from that file into Nx target metadata before Nx applies the Nx-specific overrides. Run `nx show target :` to inspect the resolved target. Add `--verbose` to see which file or plugin contributed each value: ```shell nx show target myapp:build --verbose ``` {% aside type="tip" title="Learn by doing" %} Follow the [configuring tasks tutorial](/docs/getting-started/tutorials/configuring-tasks) to inspect and override inferred configuration. {% /aside %} For configuration schemas, see [`nx.json` reference](/docs/reference/nx-json) and [project configuration](/docs/reference/project-configuration). # How Nx Works --- ## How Nx Works {% sidebar_group_cards group="How Nx works" /%} # Features --- ## Features Learn the core features of Nx with in depth guides. {% index_page_cards path="features" /%} --- ## Automate Updating Dependencies {% youtube src="https://youtu.be/A0FjwsTlZ8A" title="How Automated Code Migrations Work" /%} Keeping your tooling up to date is a tedious and time-consuming part of maintaining any project. The `nx migrate` command automates that work by: - Updating your `package.json` dependencies. - Updating your configuration files (e.g. Vite, Playwright, Nx config). - Updating your source code to match the new versions of packages (e.g., migrating across breaking changes). The command guides you through the update interactively: ```shell nx migrate ``` ## How Nx migrate works Nx knows where its configuration files are located and ensures they match the expected format. This automated update process is commonly referred to as "migration." Each [Nx plugin](/docs/plugin-registry) can provide migrations for its area of competency. For example, the Vite plugin ships migrations that update Vite configuration files across breaking changes. When you run `nx migrate`, Nx collects the pending migrations from all the plugins you have installed and applies the necessary changes to your workspace. ## Migration steps Updating your Nx workspace happens in two phases: 1. **Generate** - `nx migrate` applies the package version updates to your `package.json` and writes a `migrations.json` file. No source code is touched yet. 2. **Run** - `nx migrate --run-migrations` runs the generated migrations to update your configuration files and source code. You can intervene between the phases and make adjustments as needed for your specific workspaces. This is especially important in large codebases where you might want to control the changes more granularly. ### Step 1: Generate migrations Run the `migrate` command and follow the prompts: ```shell nx migrate ``` Nx resolves the latest version and, when the update crosses more than one major version, asks how far to jump. Updating [one major version at a time](/docs/guides/tips-n-tricks/advanced-update#one-major-version-at-a-time-small-steps) is the safest path and is what Nx recommends. Nx also asks which package versions to migrate. The answer maps to the `--include` flag: - `required` - the target package and the packages it ships with. For example, Nx itself and its plugins such as `@nx/vite`. - `optional` - the dependency updates those packages recommend. For example, `vite` itself rather than `@nx/vite`. - `all` - both of the above. When unsure, choose `required`. Updating only Nx and its plugins keeps the PR scope small and has less chance of introducing issues, which matters most in large workspaces. Follow up with `nx migrate --include=optional` to catch up on the rest. If you're okay with doing everything in one PR, use `--include=all`. In some cases you can scope the optional catch-up to a single plugin's dependencies, such as `nx migrate @nx/vite --include=optional` - see [choosing which packages to migrate](/docs/guides/tips-n-tricks/advanced-update#choosing-which-packages-to-migrate) for the caveat. This results in: - The `package.json` being updated with the new package versions - A `migrations.json` being generated if there are pending migrations. At this point, no packages have been installed, and no other files have been touched. Now, inspect `package.json` to see if the changes make sense. Sometimes the migration can update a package to a version that is either not allowed or conflicts with another package. You are free to adjust versions before running install. {% tabs syncKey="install-type" %} {% tabitem label="npm" %} ```shell npm install ``` {% /tabitem %} {% tabitem label="yarn" %} ```shell yarn install ``` {% /tabitem %} {% tabitem label="pnpm" %} ```shell pnpm install ``` {% /tabitem %} {% tabitem label="bun" %} ```shell bun install ``` {% /tabitem %} {% /tabs %} Also, look at the `migrations.json` file for the type of migrations that are going to be applied. If this file does not exist, then there are no migrations to run. ### Step 2: Run migrations Run the migrations that were generated in the previous step (`migrations.json`): ```shell nx migrate --run-migrations ``` #### What's in a migration? Migrations run one at a time and contain two types of changes: 1. **Generator-based** ("script-based"): programmatic config or code changes (e.g. `rollupOptions` becomes `rolldownOptions` in `vite.config.ts` for Vite 8). 2. **Prompt-based**: AI-aided changes that can't be expressed deterministically and need judgment about your specific code. A migration can be **generator-only**, **prompt-only**, or a **hybrid** (a generator followed by AI-aided changes). #### Running the migrations Generator-only migrations run automatically. All the changes are unstaged ready for you to review. When prompt-only or hybrid migrations are queued and a supported AI agent is installed (Claude Code, OpenAI Codex, or OpenCode), Nx asks whether to continue with an agentic flow. You can answer for this run only, or have Nx remember your choice in `nx.json`. With the agentic flow enabled: - Generator-based changes run first, and the agent validates the results. - The agent then applies the prompt-based changes as instructed in the prompt. Nx creates a commit for each migration while the agentic flow is enabled, so the agent reviews each migration's changes in isolation. Without an agent, generator-only migrations and the generator half of hybrid migrations still run. The skipped prompt files are listed in the next-steps output, in order, so you can apply them yourself. {% aside type="note" title="Running inside an AI agent" %} If you run `nx migrate --run-migrations` from within an AI agent's terminal, Nx defers the prompt-based migrations to that agent instead of spawning another one. {% /aside %} {% aside type="tip" title="Migrations are version specific" %} Each Nx plugin provides migrations that are relevant to particular versions of the package. The generated `migrations.json` only contains the migrations appropriate for the update you are currently applying. {% /aside %} ### Step 3: Clean up After you run all the migrations, you can remove `migrations.json` and commit any outstanding changes. Note: You may want to keep the `migrations.json` until every branch that was created before the migration has been merged. Leaving the `migrations.json` in place allows devs to run `nx migrate --run-migrations` to apply the same migration process to their newly merged code as well. ### Step 4: Update community plugins (Optional) If you have any [Nx community plugins](/docs/plugin-registry) installed you need to migrate them individually (assuming they provide migration scripts) by using the following command: ```shell nx migrate my-plugin ``` For a list of all the plugins you currently have installed, run: ```shell nx report ``` ## Configure migrate defaults Set workspace-wide defaults for `nx migrate` in the `migrate` section of `nx.json` instead of passing the same flags on every run. You can control commit behavior, package selection, multi-major version handling, and the agentic flow: ```json // nx.json { "migrate": { "agentic": "claude-code", "createCommits": true, "commitPrefix": "chore(repo): apply nx migration " } } ``` For all available options, see the [`migrate` section of the `nx.json` reference](/docs/reference/nx-json#migrate). ## Keep Nx packages on the same version When you run `nx migrate`, the `nx` package and all the `@nx/` packages get updated to the same version. It is important to [keep these versions in sync](/docs/kb/keep-nx-versions-in-sync) to have Nx work properly. As long as you run `nx migrate` instead of manually changing the version numbers, you shouldn't have to worry about it. Also, when you add a new plugin, use `nx add ` to automatically install the version that matches your repository's version of Nx. ## Need more control? Sometimes you need to deviate from the defaults: skip optional package updates and catch them up later, pin a specific AI agent or disable the agentic flow, run migrations one at a time, or opt out of specific migrations by adjusting `migrations.json`. Find all of these in our [Advanced Update Process](/docs/guides/tips-n-tricks/advanced-update) guide. --- ## Cache Task Results {% youtube src="https://youtu.be/o-6jb78uuP0" title="Remote caching with Nx Cloud" /%} Rebuilding and retesting the same code repeatedly is costly. Nx offers a sophisticated and battle-tested computation caching system that ensures **code is never rebuilt twice**. This: - drastically **speeds up your task execution times** while developing locally and even more [in CI](/docs/features/ci-features/remote-cache) - **saves you money on CI/CD costs** by reducing the number of tasks that need to be executed Nx **restores both the terminal output and the files** created from running the task (e.g., your build or dist directory). If you want to learn more about the conceptual model behind Nx caching, read [How Caching Works](/docs/concepts/how-caching-works). ## Define cacheable tasks To enable caching for `build` and `test`, edit the `targetDefaults` property in `nx.json` to include entries for the `build` and `test` tasks: ```json // nx.json { "targetDefaults": { "build": { "cache": true }, "test": { "cache": true } } } ``` {% aside type="note" title="Cacheable operations need to be side effect free" %} This means that given the same input they should always result in the same output. As an example, e2e test runs that hit the backend API cannot be cached as the backend might influence the result of the test run. {% /aside %} ## Enable remote caching By default, Nx caches task results locally. The biggest benefit of caching comes from using remote caching in CI, where you can **share the cache between different runs**. Nx comes with a managed remote caching solution built on top of Nx Cloud. To enable remote caching, connect your workspace to [Nx Cloud](https://nx.dev/nx-cloud?utm_source=nx-docs&utm_medium=website&utm_campaign=cache-task-results) by running the following command: ```shell npx nx@latest connect ``` {% call_to_action title="Connect from your browser" url="https://cloud.nx.app/get-started?utm_source=nx-docs&utm_medium=website&utm_campaign=cache-task-results" icon="nxcloud" description="Sign in to Nx Cloud and connect your repository without the CLI" /%} Learn more about [remote caching with Nx Cloud](/docs/features/ci-features/remote-cache). ## Fine-tune caching with inputs and outputs Nx caching feature starts with sensible defaults, but you can also **fine-tune the defaults** to control exactly what gets cached and when. There are two main options that control caching: - **Inputs -** define what gets included as part of the calculated hash (e.g. files, environment variables, etc.) - **Outputs -** define folders where files might be placed as part of the task execution. You can define these inputs and outputs at the project level (`project.json`) or globally for all projects (in `nx.json`). Take the following example: we want to exclude all `*.md` files from the cache so that whenever we change the README.md (or any other markdown file), it does _not_ invalidate the build cache. We also know that the build output will be stored in a folder named after the project name in the `dist` folder at the root of the workspace. To achieve this, we can add `inputs` and `outputs` definitions globally for all projects or at a per-project level: {% tabs %} {% tabitem label="Globally" %} ```json // nx.json { "targetDefaults": { "build": { "inputs": ["{projectRoot}/**/*", "!{projectRoot}/**/*.md"], "outputs": ["{workspaceRoot}/dist/{projectName}"] } } } ``` {% /tabitem %} {% tabitem label="Project Level (project.json)" %} ```json // packages/some-project/project.json { "name": "some-project", "targets": { "build": { ... "inputs": ["!{projectRoot}/**/*.md"], "outputs": ["{workspaceRoot}/dist/apps/some-project"], ... } ... } } ``` {% /tabitem %} {% tabitem label="Project Level (package.json)" %} ```json // packages/some-project/package.json { "name": "some-project", "nx": { "targets": { "build": { ... "inputs": ["!{projectRoot}/**/*.md"], "outputs": ["{workspaceRoot}/dist/apps/some-project"], ... } ... } } } ``` {% /tabitem %} {% /tabs %} Note that you only need to define output locations if they differ from the usual `dist` or `build` directory, which Nx automatically recognizes. Learn more [about configuring inputs including `namedInputs`](/docs/kb/configure-inputs). ## Configure caching automatically When using [Nx plugins](/docs/concepts/nx-plugins), many tasks have caching configured automatically, saving you the effort of manual setup. **Nx plugins can [automatically infer tasks](/docs/concepts/mental-model#inferred-tasks) and configure caching** based on your underlying tooling configuration files. For example, if you add the `@nx/vite` plugin using the following command... ```shell npx nx add @nx/vite ``` ...it automatically detects your `vite.config.ts` file, infers the tasks you'd be able to run, such as `build`, and **automatically configures the cache settings** for these tasks as well as the [task pipeline](/docs/concepts/task-pipeline-configuration) (e.g., triggering dependent builds). This means **you don't need to manually specify cacheable operations for Vite tasks** and the cache setting such as inputs and outputs are always in sync with the `vite.config.ts` file. To view the task settings that have been automatically configured by a plugin, use the following command: ```shell nx show project --web ``` Alternatively, you can view these directly in your editor by installing [Nx Console](/docs/getting-started/editor-setup). Learn more details about [Nx plugins](/docs/concepts/nx-plugins) and [inferred tasks](/docs/concepts/mental-model#inferred-tasks). ## Troubleshoot cache settings Caching is hard. If you run into issues, check out the following resources: - [Debug cache misses](/docs/kb/troubleshoot-cache-misses) - [Turn off or skip the cache](/docs/kb/skipping-cache) - [Change the cache location](/docs/kb/change-cache-location) - [Clear the local or remote cache](/docs/reference/nx-commands#nx-reset) --- ## Orchestration & CI with Nx Cloud {% youtube src="https://www.youtube.com/watch?v=cDBihpB3SbI" title="Nx and Nx Cloud" width="100%" /%} CI is challenging and it's **not your fault**. It's a fundamental issue with how the current, traditional CI execution model works. Nx Cloud adopts a new **task-based** CI model that overcomes slowness and unreliability of the current VM-based CI model. Nx Cloud improves many aspects of the CI/CD process: - **Speed** - 30% - 70% faster CI (based on reports from our clients) - **Cost** - 40% - 75% reduction in CI costs (observed on the Nx OSS monorepo) - **Reliability** - by automatically identifying flaky tasks (e2e tests in particular) and re-running them ## What you get with Nx Cloud on top of Nx Core | | Nx Core | Nx Cloud | | ---------------- | ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | | Caching | Local cache, per machine | Adds a [remote cache](/docs/features/ci-features/remote-cache) shared across your team and CI | | Task scheduling | Task graph on one machine, [`nx affected`](/docs/features/ci-features/affected) to skip untouched projects | Adds [distribution across agent machines](/docs/features/ci-features/distribute-task-execution) | | End-to-end tests | Run as one task per project | [Split per file](/docs/features/ci-features/split-e2e-tasks) and distributed | | Flaky tasks | You rerun the pipeline | [Detected and rerun](/docs/features/ci-features/flaky-tasks) within the same run | | Failing CI | You read the logs and fix it | [Self-healing CI](/docs/features/ci-features/self-healing-ci) proposes a fix on the PR | | Visibility | Terminal output for the run in front of you | Run history, task analytics, and CI observability | ## Connect your workspace to Nx Cloud Run the following command in your Nx workspace (make sure you have it pushed to a remote repository first): ```shell npx nx connect ``` This connects your workspace to Nx Cloud and enables remote caching and CI features. For more details, [follow our in-depth guide](/docs/kb/setup-ci) for setting up CI with Nx. {% call_to_action title="Connect from your browser" url="https://cloud.nx.app/get-started?utm_source=nx-docs&utm_medium=website&utm_campaign=ci-features" icon="nxcloud" description="Sign in to Nx Cloud and connect your repository without the CLI" /%} ## How Nx Cloud improves CI In traditional CI models, work is statically assigned to CI machines. This creates inefficiencies that many teams experience at scale. Nx Cloud uses a **task-based approach to dynamically assign tasks** to agent machines. CI becomes scalable, maintainable, and more reliable because Nx Cloud coordinates work among agent machines automatically and acts on individual tasks directly. For example: - An agent machine fails in a setup step — Nx Cloud automatically reassigns the work to other agent machines. - More work needs to run in CI — add more agent machines, Nx Cloud automatically assigns available work. - Known flaky tasks waste CI time on needed reruns — Nx Cloud automatically detects flaky tasks and reruns them in the current CI execution. [Learn how our customers use Nx Cloud](https://nx.dev/blog?filterBy=customer+story) to scale their workspaces and be more efficient. ## Nx Cloud features {% index_page_cards path="features/ci-features" /%} ## Frequently asked questions ### Is Nx Cloud free? Yes. Nx Cloud starts on the free Hobby plan, which includes 50,000 credits per month, 5 contributors, and 10 concurrent CI connections, with no credit card. Nx itself is free and open source too. Task running, the project graph, local caching, `nx affected`, generators, and migrations all work without an Nx Cloud account. ### What happens when I use up my credits? On the Hobby plan, Nx Cloud features turn off once the monthly credits are gone and turn back on at the next reset. The Team plan has no such cap, and usage beyond the $29 of included credit is billed at $5.50 per 10,000 credits. See [credit pricing](/docs/reference/nx-cloud/credits-pricing) for what each operation costs. ### Do unused credits carry over? No. Your allowance resets at the start of each billing cycle. ### Is there a plan for open source projects? Yes. Nx Cloud is free for qualifying open source organizations. [Talk to our engineering team](https://nx.dev/contact/engineering) to get set up. ### Can I get a shared cache without Nx Cloud? Yes. Build your own server against the [remote cache OpenAPI specification](/docs/kb/self-hosted-caching), in any language. You maintain the storage, authentication, and uptime. ### When should I consider Enterprise or self-hosted options? The [Enterprise plan](https://nx.dev/enterprise) adds SSO and SAML, dedicated support with SLAs, [conformance rules](/docs/enterprise/conformance), [code owners](/docs/enterprise/owners), and cross-repo visibility. On a [single-tenant instance](/docs/enterprise/single-tenant/overview) or an on-prem install, you choose the region and the infrastructure your cache and run data live on. For what a standard Nx Cloud workspace sends and stores, see the [privacy policy](https://nx.app/privacy). ## Learn more - [Blog post: Reliable CI: A new execution model fixing both flakiness and slowness](https://nx.dev/blog/reliable-ci-a-new-execution-model-fixing-both-flakiness-and-slowness) - [Live stream: Unlock the secret of fast CI - Hands-on session](https://www.youtube.com/live/rkLKaqLeDa0) - [YouTube: 10x Faster e2e Tests](https://www.youtube.com/watch?v=0YxcxIR7QU0) --- ## Run Only Tasks Affected by a PR {% youtube src="https://youtu.be/q-cu5Lw3DoE" title="Only Run Tasks for Projects That Changed" /%} As your workspace grows, re-testing, re-building, and re-linting **all projects becomes too slow**. To address this, Nx comes with an "affected" command. Using this command, Nx - determines the minimum set of **projects that are affected by the change** - only runs tasks on those affected projects This drastically improves the speed of your CI and reduces the amount of compute needed. This advantage is further enhanced when [paired with remote caching and distribution](#best-paired-with-remote-caching-and-distribution). {% graph title="Making a change in lib10 only affects a sub-part of the project graph (shown in purple)" height="400px" %} ```json { "projects": [ { "type": "app", "name": "app1", "data": {} }, { "type": "app", "name": "app2", "data": {} }, { "type": "lib", "name": "lib1", "data": {} }, { "type": "lib", "name": "lib2", "data": {} }, { "type": "lib", "name": "lib3", "data": {} }, { "type": "lib", "name": "lib4", "data": {} }, { "type": "lib", "name": "lib5", "data": {} }, { "type": "lib", "name": "lib6", "data": {} }, { "type": "lib", "name": "lib7", "data": {} }, { "type": "lib", "name": "lib8", "data": {} }, { "type": "lib", "name": "lib9", "data": {} }, { "type": "lib", "name": "lib10", "data": {} }, { "type": "lib", "name": "lib11", "data": {} }, { "type": "lib", "name": "lib12", "data": {} } ], "groupByFolder": false, "workspaceLayout": { "appsDir": "apps", "libsDir": "libs" }, "dependencies": { "app1": [ { "target": "lib1", "source": "app1", "type": "direct" }, { "target": "lib2", "source": "app1", "type": "direct" } ], "app2": [ { "target": "lib4", "source": "app2", "type": "direct" }, { "target": "lib5", "source": "app2", "type": "direct" }, { "target": "lib6", "source": "app2", "type": "direct" } ], "lib1": [ { "target": "lib7", "source": "lib1", "type": "direct" }, { "target": "lib8", "source": "lib1", "type": "direct" } ], "lib2": [ { "target": "lib3", "source": "lib2", "type": "direct" } ], "lib3": [ { "target": "lib8", "source": "lib3", "type": "direct" } ], "lib4": [ { "target": "lib3", "source": "lib4", "type": "direct" }, { "target": "lib9", "source": "lib4", "type": "direct" }, { "target": "lib10", "source": "lib4", "type": "direct" } ], "lib5": [ { "target": "lib10", "source": "lib5", "type": "direct" }, { "target": "lib11", "source": "lib5", "type": "direct" }, { "target": "lib12", "source": "lib5", "type": "direct" } ], "lib6": [ { "target": "lib12", "source": "lib6", "type": "direct" } ], "lib7": [], "lib8": [], "lib9": [], "lib10": [], "lib11": [], "lib12": [] }, "affectedProjectIds": ["lib10", "lib4", "lib5", "app2"] } ``` {% /graph %} ## Using Nx affected commands To leverage this feature, use the following command when running your tasks, particularly on CI: ```shell nx affected -t ``` When you run `nx affected -t test`, Nx will: - Use Git to determine the files you changed in your PR. - Use the [project graph](/docs/features/explore-graph) to determine which projects the files belong to. - Determine which projects depend on the projects you modified. Once the projects are identified, Nx runs the tasks you specified on that subset of projects. You can also visualize the affected projects using the [Nx graph](/docs/features/explore-graph). Simply run: ```shell nx graph --affected ``` ## Best paired with remote caching and distribution Using `nx affected` is a powerful tool to reduce the amount of compute that needs to be run. However, this might not be sufficient to significantly speed up your CI pipeline. For example: - If you're modifying a **project that is used by a large portion** of your monorepo projects, you might end up running tasks for almost all the projects in the workspace. - If you have a set of 10 projects affected by a PR and you continue making changes, you will **always end up running tasks for those 10 projects**. The set of affected projects doesn't change but is always calculated with respect to your last successful run on the main branch. This is why Nx Affected is best paired with [remote caching](/docs/features/ci-features/remote-cache) and [distributed task execution](/docs/features/ci-features/distribute-task-execution). ## Configure affected on CI To understand which projects are affected, Nx uses the Git history and the [project graph](/docs/features/explore-graph). Git knows which files changed, and the Nx project graph knows which projects those files belong to. The affected command takes a `base` and `head` commit. The default `base` is your `main` branch, and the default `head` is your current file system. This is generally what you want when developing locally, but in CI, you need to customize these values. ```shell nx affected -t build --base=origin/main --head=$PR_BRANCH_NAME # where PR_BRANCH_NAME is defined by your CI system nx affected -t build --base=origin/main~1 --head=origin/main # rerun what is affected by the last commit in main ``` You can also set the base and head SHAs as environment variables: ```shell NX_BASE=origin/main~1 NX_HEAD=origin/main ``` **The recommended approach is to set the base SHA to the latest successful commit** on the `main` branch. This ensures that all changes since the last successful CI run are accounted for. See our guides to get the [last successful CI run for your CI provider](/docs/kb/setup-ci#get-the-commit-of-the-last-successful-build). ## Ignoring files from affected commands Nx provides two methods to exclude glob patterns (files and folders) from `affected:*` commands: - Glob patterns defined in your `.gitignore` file are ignored. - Glob patterns defined in an optional `.nxignore` file are ignored. ## Marking projects affected by dependency updates By default, Nx will mark **all** projects as affected whenever your package manager's lock file changes. This behavior is a failsafe in case Nx misses a project that should be affected by a dependency update. You can configure this behavior with the `projectsAffectedByDependencyUpdates` option in `nx.json`: ```json // nx.json { "pluginsConfig": { "@nx/js": { "projectsAffectedByDependencyUpdates": "auto" } } } ``` The `projectsAffectedByDependencyUpdates` option accepts the following values: | Value | Description | | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `"all"` | **(default)** Every project in the workspace is marked as affected when the lock file changes. This is the safest option but may result in unnecessary CI work. | | `"auto"` | Only projects whose dependencies actually changed in the lock file are marked as affected. Nx inspects the lock file structure to determine which workspace projects had their resolved dependencies change. See the note below for details. | | `string[]` | An array of project names or glob patterns (e.g., `["app-*", "shared-lib"]`). Only the matching projects are marked as affected when the lock file changes. | {% aside type="note" title="How 'auto' works per package manager" %} In `"auto"` mode, Nx parses both the base and head revisions of your lock file using the same parsers it uses to build the project graph, diffs the resolved package metadata to find which dependencies changed, then maps those packages back to the workspace projects that depend on them. Nx supports the following lock files: `pnpm-lock.yaml`, `pnpm-lock.yml`, `package-lock.json`, `yarn.lock`, `bun.lock`, and `bun.lockb`. For binary Bun lockfiles (`bun.lockb`), Nx asks Bun to render the lockfile before diffing it, so Bun needs to be available in the environment that runs `nx affected`. {% /aside %} ## Not using git If you aren't using Git, you can pass `--files` to any affected command to indicate what files have been changed. --- ## Dedicated Compute Cluster A **dedicated compute cluster** reserves an isolated Nx Cloud compute environment for your organization, so your [Nx Agents](/docs/features/ci-features/distribute-task-execution) run in a cluster provisioned just for your org instead of on the shared multi-tenant pool. The dedicated cluster lets agents run Docker-in-Docker (DinD) and unlocks additional Nx Cloud add-ons that require isolation in order to run with elevated capabilities. {% aside type="note" title="Nx Cloud add-on" %} The **dedicated compute cluster** is an Nx Cloud add-on. Manage it under [**Settings > Add-ons**](https://cloud.nx.app/go/organization/add-ons) for your organization. Nx Enterprise customers on [single-tenant](/docs/enterprise/single-tenant/overview) deployments already run in a dedicated environment and get these capabilities through their deployment. {% /aside %} ## What it unlocks A dedicated compute cluster unlocks the following: - [**Docker-in-Docker**](#docker-in-docker-on-agents) - build and push container images, run Testcontainers, and run any task that needs a Docker daemon, directly on Nx Agents. - [**Sandboxing**](/docs/features/ci-features/sandboxing) - confine each task to its declared [`inputs`](/docs/reference/project-configuration#inputs-and-named-inputs) and [`outputs`](/docs/reference/project-configuration#outputs), and catch any read or write outside them. - [**Docker layer caching**](/docs/features/ci-features/docker-layer-caching) - reuse Docker build layers across CI runs. - [**Docker read-through cache**](/docs/features/ci-features/docker-read-through-cache) - serve repeated image pulls from a cache close to your agents. - [**npm read-through cache**](/docs/features/ci-features/npm-read-through-cache) - serve repeated npm installs from a cache close to your agents. DinD is available on every agent in the cluster automatically. The four add-ons are enabled individually once the cluster is active, and are cancelled if you cancel the dedicated compute cluster. ## Docker-in-Docker on agents Every agent in a dedicated compute cluster can run DinD. This lets your tasks build and push container images, run [Testcontainers](https://testcontainers.com), and execute any workflow that needs a Docker daemon, directly on Nx Agents. On the shared multi-tenant pool, agents run a fixed set of approved images and cannot run privileged containers. The dedicated cluster lifts that restriction for your organization, so you can run custom agent images and DinD workloads. ## Enabling dedicated compute cluster A dedicated compute cluster is provisioned through your organization settings: 1. Open [**Settings > Add-ons**](https://cloud.nx.app/go/organization/add-ons) for your organization. 2. On the **Dedicated compute cluster** card, click **Request add-on** and confirm. 3. You will be notified via email when the cluster is ready to use. Once the cluster is active, the dependent add-ons (sandboxing, Docker layer caching, and the read-through caches) become available to enable on the same page. If you request one of them before the cluster is ready, it is queued and activates automatically when the cluster comes online. --- ## Distribute Task Execution (Nx Agents) {% youtube src="https://youtu.be/XS-exYYP_Gg" title="Nx Agents Walkthrough" /%} Nx Agents is a **distributed task execution system that intelligently allocates tasks across multiple machines**, optimizing your CI pipeline for speed and efficiency. While using [Nx Affected](/docs/features/ci-features/affected) and [remote caching](/docs/features/ci-features/remote-cache) can significantly speed up your CI pipeline, you might still encounter bottlenecks as your codebase scales. Combining affected runs and remote caching with task distribution is key to maintaining low CI times. Nx Agents handles this distribution efficiently, avoiding the complexity and maintenance required if you were to set it up manually. ![Nx Cloud visualization of how tasks are being distributed with Nx Agents](../../../../assets/features/nx-agents-live-chart.avif) Nx Agents offer several key advantages: - **Declarative Configuration:** No maintenance is required as your monorepo evolves, thanks to a declarative setup. - **Efficient Task Replay:** By leveraging [remote caching](/docs/features/ci-features/remote-cache), tasks can be replayed efficiently across machines, enhancing distribution speed. - **Intelligent Task Distribution:** Tasks are distributed based on historical run times and dependencies, ensuring correct and optimal execution. - **Dynamic Resource Allocation:** Agents are [allocated dynamically based on the size of the PR](/docs/features/ci-features/dynamic-agents), balancing cost and speed. - **Seamless CI Integration:** Easily adopt Nx Agents with your [existing CI provider](/docs/kb/setup-ci), requiring minimal setup changes. - **Simple Activation:** Enable distribution with just a [single line of code](#enable-nx-agents) in your CI configuration. ## Enable Nx Agents To enable task distribution with Nx Agents, make sure your Nx workspace is connected to Nx Cloud. If you haven't connected your workspace to Nx Cloud yet, run the following command: ```shell npx nx@latest connect ``` {% call_to_action title="Connect from your browser" url="https://cloud.nx.app/get-started?utm_source=nx-dev&utm_medium=website&utm_campaign=distribute-task-execution" icon="nxcloud" description="Sign in to Nx Cloud and connect your repository without the CLI" /%} For the full pipeline walkthrough, see [Setting Up CI](/docs/getting-started/setup-ci). Choose one of the following setup paths. {% tabs %} {% tabitem label="Use an Agent" %} {% llm_copy_prompt title="Let an AI agent set it up for you" previewLines=15 %} You are an Nx Agents workflow migration assistant. Your task is to analyze an existing CI pipeline and create a correct, conservative Nx Agents setup based on the pipeline's real commands, dependencies, secrets, and execution behavior. Do not assume the workflow is simple. Inspect the CI workflow/pipeline files, invoked scripts, env-derived target lists, existing `.nx/workflows` files, `nx.json`, package-manager config, version files, and any provided Nx Agents docs/examples. ## Primary goal Create an Nx Agents workflow that preserves the behavior of the existing CI pipeline while moving appropriate distributed Nx task execution onto Nx Agents. Act on the migration plan by default: edit the relevant workflow/configuration files unless the user explicitly asks for analysis only. Favor correctness over cleverness. Keep coordinator-only responsibilities in the CI provider's orchestration job. ## Required analysis 1. Identify the CI topology: - CI provider: GitHub Actions, GitLab CI, Bitbucket Pipelines, or another equivalent system. - Trigger types: PR/MR, push, merge queue, manual dispatch, reusable workflow/pipeline calls. - Required checks/statuses, protected branches, environments, permissions, concurrency. - Main orchestration job vs manually sharded jobs vs release/deploy jobs. 2. Identify commands: - Nx commands: `affected`, `run-many`, `run`, `record`, `fix-ci`, `complete-ci-run`. - Non-Nx commands that must remain local or be wrapped in `nx-cloud record`. - Hidden commands in shell scripts, TypeScript scripts, env vars, matrices, or conditionals. - Commands using `--no-dte`, `--no-agents`, special configs, custom base/head, or different retry behavior. 3. Classify execution plane: - Coordinator-only: checkout, base/head setup, secret loading, artifact upload, comments, deployment, release, commits, status reporting. - Agent init: checkout, toolchain setup, dependency install, registry auth, caches, services, browsers. - Distributed Nx work: cacheable Nx targets that should run on agents. - Recorded local work: checks that should use `nx-cloud record`. - Explicitly local work: commands that must not be distributed. 4. Infer toolchains and services: - Default agent image: `ubuntu22.04-node24.14-v1`. - Node/package manager/Corepack version. - Java/Gradle, Python/uv, Rust/Cargo, .NET, Go, browsers, Docker/Testcontainers. - Private registries, package caches, read-through registries. - Service containers or Docker Compose requirements. - Version sources such as `.nvmrc`, `packageManager`, `mise.toml`, Gradle wrapper, pinned CI-provider steps/actions. - If the workflow requires a different Node version than the base image, install it during init using the appropriate reusable step or script. 5. Identify env vars and secrets: - CI-provider-only vars. - Main orchestration job vars. - Agent-required vars to pass via `--with-env-vars`. - Vars that must be configured in Nx Cloud UI. - Secrets that must not be forwarded. - Set `NX_CLOUD_CONTINUOUS_ASSIGNMENT: true` in the global environment for the main orchestration job unless the existing pipeline has a documented reason not to. - Do not recommend `--with-env-vars=auto` unless the workflow already uses it or the user explicitly accepts broad forwarding. - Never print full env in agent setup. 6. Map reusable CI steps: - Prefer Nx Cloud reusable workflow steps where equivalent: checkout, install-node, install-node-modules, cache, install-browsers, install-mise, install-aws-cli. - Use inline scripts when no equivalent exists. - Keep CI-provider-only steps in the coordinator job, especially SHA/base-head setup, artifact upload, comments, provider app/token auth, cloud deployment auth, and release tooling. - Preserve version behavior from pinned provider steps/actions as closely as possible. ## Semantic constraints rule Preserve semantic constraints, not incidental bottlenecks from the old CI topology. When migrating to Nx Agents, distinguish these three concepts: 1. Task ordering: - "This must finish before that can start." - Prefer to model or verify this through the Nx task graph. 2. Per-machine concurrency: - "This machine should only run N tasks at once because of CPU, memory, ports, browsers, Docker, services, etc." - With Nx Agents, each agent is its own machine. Translate this to per-agent concurrency with command `--parallel` or minimal target-specific assignment-rule `parallelism`. 3. Global serialization: - "Only one of these tasks may run anywhere in the whole CI run." - Preserve this only when there is concrete evidence of a global shared resource, external environment, deployment, rate limit, mutable shared artifact, commit/push behavior, or an unmodeled dependency. Treat old CI job boundaries, dependency edges such as `needs:`, and `--parallel` values as implementation evidence only. They do not automatically prove global serialization or coordinator-level ordering. If Nx models the dependency, let Nx schedule it. If a target uses only machine-local resources, it can usually distribute safely. Preserve global serialization or coordinator-local execution only when supported by concrete evidence. ## Manual sharding rule Apply the Semantic Constraints Rule first. Do not assume separate CI jobs or dependency edges such as `needs:` imply coordinator-level ordering that must be preserved in the CI provider. Separate CI jobs only prove the old implementation split execution there. They do not prove that the split is semantically required. When manually sharded jobs each run distinct Nx targets, first check whether Nx already models the real ordering through: - `nx.json` `targetDefaults` - project target `dependsOn` - resolved project config from `nx show project --json` - inferred task dependencies from Nx plugins - generated task graph output from `nx run-many -t --graph=graph.json` If Nx already models relationships such as `e2e` depending on `build`, generated-code checks depending on generation, or tests depending on build/setup targets, consolidate compatible commands and let Nx schedule the task graph. Do not preserve separate Nx command steps merely for old job names, check readability, failure attribution, or step-level gating if the Nx task graph models the dependency. Treat those as non-semantic implementation details unless artifacts, env, retry/failure behavior, or execution plane truly differs. Preserve separate coordinator commands only when required ordering or behavior is not represented in Nx, or when commands differ by config, base/head range, retry policy, coverage behavior, event conditionals, non-DTE behavior, artifacts, or failure aggregation. If separate commands are preserved, explicitly state which Nx dependency relationship could not be proven. ## Command consolidation - Apply the Manual Sharding Rule before preserving separate CI jobs. - Treat manually sharded CI jobs as candidates for consolidation, especially when each job runs a distinct Nx target. - When deciding whether commands can be consolidated, generate and inspect the Nx task graph for the relevant targets. - Use the workspace package manager, for example: `pnpm nx run-many -t --graph=graph.json`. - Inspect `graph.json` to verify which tasks depend on each other. Use this evidence, plus `targetDefaults`, project `dependsOn`, and resolved project config, before deciding whether old job splits must remain. - If Nx target dependencies model the required ordering, combine compatible commands into one `nx affected -t ...` or `nx run-many -t ...`. - Combine multiple Nx commands only when semantics remain equivalent. - Do not combine commands with different configs, target sets, `--no-dte`, retry behavior, coverage behavior, self-healing behavior, failure aggregation, or event conditionals. - Preserve intentional parallel shell fan-out only when consolidation would change behavior or when required semantics cannot be represented by the Nx task graph. - Preserve performance intent through Nx scheduling, command `--parallel`, or minimal target-specific assignment-rule parallelism. ## Assignment rules policy - Do not get fancy with assignment rules. - Do not use assignment rules to route specific projects or target families to custom agent pools unless the existing setup already does and it is necessary. - The maximum new use of assignment rules is target-specific parallelism when many commands have been combined into one Nx command and the original workflow had materially different parallelism per target. - If target-specific parallelism is not needed, omit assignment rules. - If assignment rules are used, keep them minimal, target-only where possible, and verify every referenced agent template exists in every relevant `distribute-on` tier. ## Parallelism policy Translate old single-runner `--parallel=N` limits into per-agent task concurrency unless there is concrete evidence that the work must be globally serialized. Treat command `--parallel=N` and assignment-rule `parallelism: N` as equivalent per-agent task concurrency controls. With Nx Agents, command `--parallel` is the default per-agent task concurrency for the command unless an assignment rule overrides it. Assignment-rule parallelism is not an additional multiplier on top of command parallelism. It is a target/template-scoped override. Agent count is the separate scaling axis. Total distributed capacity comes from: `agent count * per-agent parallelism` Do not lower assignment-rule parallelism merely because multiple agents are used. Tune agent count separately. If agents are equivalently specced to the old CI runner, an agent can reasonably run with the same `--parallel` value that the old main job used. ## Base image policy - Use the standard Nx Agents base image unless the user explicitly provides a different supported image. - Default to `ubuntu22.04-node24.14-v1`. - If a required tool is missing from the base image, add an init step using a reusable Nx Cloud workflow step when available, or an inline script when necessary. ## Shutdown - Decide whether heartbeat is sufficient. - Use `--require-explicit-completion` plus guarded `complete-ci-run` for multi-step, multi-job, staged, or heartbeat-risky workflows. - Treat `--stop-agents-after` as waste reduction, not CI completion. - Build `--stop-agents-after` from the final distributed target set and validate target/configuration names. - Use `--stop-agents-on-failure=false` when later work, artifacts, coverage, or self-healing must continue. ## Output requirements After making changes, produce: 1. A concise migration summary. 2. A list of files changed. 3. A summary of what changed in each file. 4. A command classification table: - Command - Current location - New location - Reason 5. A toolchain/setup checklist for agents. 6. An env/secrets transfer checklist split into: - CI-provider-only - Agent forwarded - Nx Cloud UI / external setup 7. The generated or updated `.nx/workflows/agents.yaml`. 8. The generated or updated distribution config if needed. 9. Minimal assignment rules only if needed for target-specific parallelism. 10. Shutdown/heartbeat recommendation. 11. Validation performed and validation still required. ## Validation checklist Before finalizing, verify: - Every generated target exists. - `--stop-agents-after` matches real distributed targets. - Every env var forwarded to agents is actually needed. - No secret is printed in logs. - Main job and agents check out the same commit. - `NX_BASE` / `NX_HEAD` works for PR/MR, push, merge queue, and manual dispatch. - For each preserved ordering constraint, classify it as task ordering, per-machine concurrency, or global serialization. - Preserve global serialization only when evidence exists for a global shared resource, external environment, deployment, rate limit, mutable shared artifact, commit/push behavior, or unmodeled dependency. - Translate old runner-local limits into per-agent concurrency when the constrained resource is machine-local. - For manually sharded pipelines, verify whether Nx target dependencies already model the old job ordering before preserving separate coordinator jobs. - For consolidated target sets, generate a task graph with `nx run-many -t --graph=graph.json` and inspect the dependency edges. - Do not preserve old job boundaries solely for job names, check readability, failure attribution, or step-level gating if Nx already models the dependency. - For any preserved separate Nx commands, document why they could not be safely combined. - For any combined commands, verify target ordering is represented in `targetDefaults`, resolved project target `dependsOn`, or generated task graph edges. - If per-target performance settings were lost by consolidation, restore only target-specific parallelism with minimal assignment rules. - Verify assignment-rule `parallelism` is used only as a scoped override of command `--parallel`, not as a multiplier. - Agent init steps do not race on shared files, caches, or `$NX_CLOUD_ENV`. - Required tools missing from the default base image are installed during agent init. - Non-cacheable or OOM-heavy targets are not over-parallelized. - YAML syntax, anchors, env interpolation, and CI-provider expressions are valid. - A trial CI run shows agents start, receive tasks, restore outputs, and shut down correctly. ## Style Do not stop at a proposal unless the user asks for one. Make the changes, then explain what was changed and why. Be conservative. Explain tradeoffs. When unsure, call out the uncertainty instead of inventing behavior. Preserve existing CI semantics first; reduce complexity second; optimize agent usage third. {% /llm_copy_prompt %} {% /tabitem %} {% tabitem label="Manual" %} Check out the [connect to Nx Cloud recipe](/docs/kb/setup-ci) for more details. Then, adjust your CI pipeline configuration to **enable task distribution**. If you don't have a CI config yet, you can generate a new one using the following command: ```shell npx nx g ci-workflow ``` Declare how tasks are distributed in a `.nx/ci-config.yaml` file: ```yaml # .nx/ci-config.yaml dte: distribute-on: 3 linux-medium-js lifecycle: stop-after: - build ``` Then start the run with `start-nx-agents`: ```yaml {% meta="{15}" %} // .github/workflows/ci.yml name: CI ... jobs: main: runs-on: ubuntu-latest steps: ... - uses: actions/checkout@v7 with: fetch-depth: 0 filter: tree:0 - run: pnpm dlx nx-cloud start-nx-agents # Cache node_modules - uses: actions/setup-node@v6 with: node-version: 24 cache: 'pnpm' ... # Nx Affected runs only tasks affected by the changes in this PR/commit. Learn more: https://nx.dev/ci/features/affected - run: pnpm exec nx affected -t lint test build ``` This tells Nx Cloud to: - Provision Nx Agents (`npx nx-cloud start-nx-agents`) - Collect all Nx commands that are being issued (e.g., `pnpm exec nx affected -t lint test build`) - Distribute them across 3 agents (`3 linux-medium-js`), where `linux-medium-js` is a predefined agent [launch template](/docs/kb/launch-templates) For every configuration option, see the [CI configuration file reference](/docs/reference/nx-cloud/ci-config). ### Configure Nx Agents on your CI Provider Every organization manages their CI/CD pipelines differently, so the guides don't cover org-specific aspects of CI/CD (e.g., deployment). They mainly focus on configuring Nx correctly using Nx Agents and [remote caching](/docs/features/ci-features/remote-cache). Read our [setup guides for your CI provider of choice](/docs/kb/setup-ci). {% /tabitem %} {% /tabs %} ## How Nx Agents work ![Distribute Task Execution with Nx Agents](../../../../assets/features/nx-agents-orchestration-diagram.svg) _**Nx Agents are declarative**_ in that you only specify the number of agents and the type of agent you want to use. Nx Cloud then picks up the Nx commands that are being issued on your CI and distributes them automatically. This results in **low maintenance and a much more efficient distribution strategy**. A non-declarative approach would be one where you define which tasks or projects get executed on which machine, requiring you to adjust the configuration as your codebase changes. _**Nx Agents use a task-centric approach**_ to distribution. Current CI systems use VM-centric approaches, where tasks must be predefined for specific machines, often leading to inefficiencies as your codebase grows. Instead of defining which tasks run on which machine upfront, Nx Agents dynamically process tasks based on availability and task dependencies/ordering. Tasks are picked up by agents based on the task's required processing time (from historical data) and task dependency/ordering (from the Nx graph). This results in a faster and resource efficient processing, and is also more resilient to failures since any other agent can pick up work if one agent fails during bootup. Read more [on our blog post](https://nx.dev/blog/reliable-ci-a-new-execution-model-fixing-both-flakiness-and-slowness). _**Nx Agents use continuous assignment**_ to keep agents busy throughout a CI run. Instead of locking work into fixed upfront batches, Nx Cloud assigns ready tasks to agents as they become available. This packs heavy and light tasks across machines, reduces idle time when task durations vary, and avoids custom sharding logic. {% aside type="note" %} Continuous assignment currently requires `NX_CLOUD_CONTINUOUS_ASSIGNMENT=true` in CI. Nx Cloud will enable it by default for all workspaces soon. {% /aside %} _**Nx Agents are cost and resource-efficient**_ because tasks are automatically distributed, **optimizing for speed while keeping resource utilization high**. You can also [dynamically adjust the number of agents](/docs/features/ci-features/dynamic-agents) based on the size of the PR, and we're working on [some more AI-powered features](/docs/features/ci-features/self-healing-ci) to optimize this even further. In addition, [remote caching](/docs/features/ci-features/remote-cache) guarantees tasks are not run twice, and artifacts are shared efficiently among agents. Nx Agents are non-invasive. You don't need to overhaul your existing CI configuration or your Nx workspace to use them. You add a `.nx/ci-config.yaml` file and the `start-nx-agents` command to your existing CI provider. All artifacts and logs are played back to the main job, so your existing post-processing steps keep working as before. For a more thorough explanation of how Nx Agents optimize your CI pipeline, read this [guide to parallelization and distribution in CI](/docs/concepts/ci-concepts/parallelization-distribution). ## Nx Agents features {% cardgrid %} {% linkcard title="Create Custom Launch Templates" description="Define your own launch templates to set up agents in the exact right way" href="/docs/kb/launch-templates" /%} {% linkcard title="Enforce a Custom Node Version" description="Pin the Node version on your agents from .nvmrc, Volta, or mise" href="/docs/kb/launch-template-examples#custom-node-version" /%} {% linkcard title="Dynamically Allocate Agents" description="Assign a different number of agents to a pipeline based on the size of the PR" href="/docs/features/ci-features/dynamic-agents" /%} {% linkcard title="Automatically Split E2E Tasks" description="Split large e2e tasks into a separate task for each spec file" href="/docs/features/ci-features/split-e2e-tasks" /%} {% linkcard title="Detect and Retry Flaky Tasks" description="Re-run flaky tests and tasks in CI whenever they fail" href="/docs/features/ci-features/flaky-tasks" /%} {% /cardgrid %} ## Relevant repositories and examples By integrating Nx Agents into your CI pipeline, you can significantly reduce build times, optimize resource use, and maintain a scalable, efficient development workflow. - [Reliable CI: A New Execution Model Fixing Both Flakiness and Slowness](https://nx.dev/blog/reliable-ci-a-new-execution-model-fixing-both-flakiness-and-slowness) - [Nx: On how to make your CI 16 times faster with a small config change](https://github.com/vsavkin/interstellar) - ["Lerna & Distributed Task Execution" Example](https://github.com/vsavkin/lerna-dte) --- ## Docker Layer Caching Docker layer caching reuses the intermediate layers produced by `docker build` (the result of each `RUN`, `COPY`, and `ADD` instruction) across CI runs. When a layer's inputs haven't changed, the build pulls it from a registry cache instead of rebuilding it, cutting image build times on [Nx Agents](/docs/features/ci-features/distribute-task-execution). ## How to enable it {% aside type="note" title="Requires a dedicated compute cluster" %} Docker layer caching is an Nx Cloud add-on that runs on a [dedicated compute cluster](/docs/features/ci-features/dedicated-compute-cluster). Request the cluster, then enable this add-on under [**Settings > Add-ons**](https://cloud.nx.app/go/organization/add-ons). {% /aside %} You can enable Docker layer caching through your organization settings: 1. Open [**Settings > Add-ons**](https://cloud.nx.app/go/organization/add-ons) for your organization. 2. Under **Dedicated compute cluster** card, find **Docker layer caching** and click **Request add-on** and confirm. When the add-on is enabled, Nx Cloud runs a registry cache inside your dedicated cluster and injects its address into every agent as the `NX_DOCKER_CACHE_REGISTRY` environment variable. You point your Docker builds at that registry using BuildKit's `--cache-to` and `--cache-from` flags. Cached layers are written to and read from the in-cluster registry, so they persist across CI runs. ## Setup ### 1. Set up Docker Buildx Layer caching requires BuildKit. Add the Buildx setup step to the `init-steps` of the launch template that runs your Docker builds, so it runs before any `docker build` command: ```yaml - name: Setup Docker Buildx uses: 'nrwl/nx-cloud-workflows/main/workflow-steps/setup-docker-buildx/main.yaml' ``` ### 2. Add cache flags to your build commands Update your `docker build` commands to export and import layers from the cache registry: ```bash docker build \ --push \ -t my-registry.example.com/my-app:1.2.3 \ --cache-to type=registry,ref=${NX_DOCKER_CACHE_REGISTRY}/my-app:main,mode=max \ --cache-from type=registry,ref=${NX_DOCKER_CACHE_REGISTRY}/my-app:main \ . ``` - **`-t my-registry.example.com/my-app:1.2.3`** - your image, tag, and destination registry. Your agents must be authenticated to that registry to push images. That setup is outside the scope of this guide. - **`${NX_DOCKER_CACHE_REGISTRY}`** - provided by Nx Cloud. It points at the in-cluster cache registry, which is separate from your final image registry. Don't push your application images there. - **`/my-app:main`** - the cache reference and tag. Use the `main` tag so subsequent builds reuse the cached layers. - **`--cache-to mode=max`** - exports all layers for maximum reuse. `mode=min` exports fewer layers; see the [Docker registry cache docs](https://docs.docker.com/build/cache/backends/registry). - **`--cache-from`** - imports cached layers when available. {% aside type="note" title="Use stable cache image " %} Use the stable tag `main` for `--cache-to` and `--cache-from`. Layers stored under the `main` tag are kept permanently. Other tags are removed periodically, so non-`main` tags won't persist as long-lived cache. Use distinct names per cache image (e.g. `my-app:main` and `my-api:main`) so different builds don't overwrite each other's layers. {% /aside %} See the [Docker registry cache docs](https://docs.docker.com/build/cache/backends/registry) for more details. For branch-cache setup and a complete Nx Agents example, see [Use the Docker layer cache with Nx Agents](/docs/kb/docker-layer-cache-with-nx-agents). ### 3. Verify it's working After the cache is warm, subsequent builds should show: - `[CACHED]` markers in the `docker build` logs where layers were reused. - Shorter build times for unchanged layers. --- ## Docker Read-Through Cache The Docker read-through cache puts a registry mirror close to your [Nx Agents](/docs/features/ci-features/distribute-task-execution). The first time an image is pulled, it is fetched from the upstream registry (for example Docker Hub) and stored in the cache. Repeated pulls of the same image are then served from the cache instead of the upstream registry, cutting image download time and reducing dependence on external registries. It also protects your organization from Docker registry outages. ## How to enable it {% aside type="note" title="Requires a dedicated compute cluster" %} The Docker read-through cache is an Nx Cloud add-on that runs on a [dedicated compute cluster](/docs/features/ci-features/dedicated-compute-cluster). Request the cluster, then enable this add-on under [**Settings > Add-ons**](https://cloud.nx.app/go/organization/add-ons). {% /aside %} You can enable Docker read-through cache through your organization settings: 1. Open [**Settings > Add-ons**](https://cloud.nx.app/go/organization/add-ons) for your organization. 2. Under **Dedicated compute cluster** card, find **Docker read-through cache** and click **Request add-on** and confirm. Once enabled, it works automatically. You don't change your Dockerfiles, `docker pull` commands, or image references. Repeated image pulls are served from the cache instead of the upstream registry. --- ## Dynamically Allocate Agents By default, when you set up [Nx Agents](/docs/features/ci-features/distribute-task-execution) you specify the number and type of agents to use in your [`.nx/ci-config.yaml`](/docs/reference/nx-cloud/ci-config) file. ```yaml // .nx/ci-config.yaml dte: distribute-on: 8 linux-medium-js lifecycle: stop-after: - e2e-ci ``` ```yaml {% meta="{9}" %} // .github/workflows/main.yaml" ... jobs: - job: main name: Main Job ... steps: ... - run: npx nx start-nx-agents - ... ``` This works great, but may not be the most cost-effective way to run your tasks. The goal is to **balance cost and speed**. For example, you might want to run a small PR on a few agents to save costs, but use many agents for a large PR to get the fastest possible build time. ## Configure dynamic agents based on PR size You can configure Nx Cloud to execute a different number of agents based on the size of your PR's affected changes. Define any number of **changesets** (the number of agents and types of agents) to use for different sized PRs and point `dte.distribute-on` at the file that holds them. Start by creating a file called `distribution-config.yaml` in the `.nx/workflows` directory of your repo. This file will contain a `distribute-on` property that will be used to define the changesets to use for your PR. You can name your changesets anything you want. {% aside type="caution" title="The order of your changesets matters!" %} Define your changesets in order of increasing size (i.e. smallest changesets are defined before larger changesets). Nx Cloud uses the position of your changesets as part of its calculations to dynamically determine the correct changeset to use for your PR. {% /aside %} ```yaml // .nx/workflows/distribution-config.yaml distribute-on: small-changeset: 3 linux-medium-js medium-changeset: 6 linux-medium-js large-changeset: 10 linux-medium-js ``` You can also specify a `default` changeset if you only want one changeset to be used for all PRs. Note that `default` is a reserved keyword so do not use it if you would like to define multiple changesets. ```yaml // .nx/workflows/distribution-config.yaml distribute-on: default: 3 linux-medium-js ``` You can have as many changesets as you want. Based on the number of changesets specified, each changeset is assigned an equal percentage out of 100. Nx Cloud can determine the percentage of affected projects in your PR and use that value to evaluate which changeset to use. {% callout type="deepdive" title="How is the size of the PR determined?" %} Nx Cloud calculates the relationship between the number of [affected projects](/docs/features/ci-features/affected) and the total number of projects in the workspace to determine the size of a PR. {% /callout %} ## Setting up dynamic agents in your CI pipeline In the example below, each changeset would be assigned an equal percentage range out of 100%. If Nx Cloud determines that 30% of your projects have been affected, then it will use the medium changeset to distribute the workload on. If Nx Cloud determines that 55% of your projects have been affected, it will use the large changeset. ```yaml // .nx/workflows/distribution-config.yaml distribute-on: small-changeset: 3 linux-medium-js # Distribute on small if 1-25% of projects affected in PR medium-changeset: 6 linux-medium-js # Distribute on medium if 26-50% of projects affected in PR large-changeset: 10 linux-medium-js # Distribute on large if 51-75% of projects affected in PR extra-large-changeset: 15 linux-medium-js # Distribute on extra-large if 76-100% of projects affected in PR ``` You can then reference your distribution configuration from `.nx/ci-config.yaml`: ```yaml // .nx/ci-config.yaml dte: distribute-on: .nx/workflows/distribution-config.yaml lifecycle: stop-after: - e2e-ci ``` Your CI pipeline then starts the agents: ```yaml {% meta="{9}" %} // .github/workflows/main.yaml ... jobs: - job: main name: Main Job ... steps: ... - run: npx nx start-nx-agents - ... ``` Now your agents will distribute your tasks dynamically—scaling and adapting to your PR sizes. This feature helps save costs on smaller PRs while maintaining the high performance necessary for large PRs. --- ## Detect and Retry Flaky Tasks A flaky task passes on one run and fails on the next with no change to its inputs. Flaky e2e tests are the most common case, but any [task](/docs/reference/glossary#task) can flake. For example, builds that reach for the network. A flaky task blocks a green pull request. The usual response is to hit rerun, which is slow and manual. Nx Cloud detects flaky tasks and reruns them on a different agent automatically, so most of the time you never see the failure. ## How Nx detects flaky tasks Nx uses its cache mechanism for detection. - Nx creates a **hash of all the inputs** for a task whenever it is run. - If Nx ever encounters a task that fails with a particular set of inputs and then succeeds with those same inputs, it marks that task as **flaky**. The hash pins the inputs, so a failure after a code change is not mistaken for a flake, and results from different machines and CI runs count as the same evidence. Detection works at the task level. A task can run a single Playwright spec file, a Jest project, or a whole suite, so the flag names the nondeterministic task, not an individual test inside it. Nx can't know with certainty when the task has been fixed to no longer be flaky, so if a particular task has **no flakiness incidents for 2 weeks**, the `flaky` flag is removed for that task. ## Enable flaky task detection Flaky Task Detection is enabled by default if your workspace is connected to Nx Cloud and leverages [Nx Agents](/docs/features/ci-features/distribute-task-execution). To connect your workspace to Nx Cloud run: ```shell npx nx@latest connect ``` {% call_to_action title="Connect from your browser" url="https://cloud.nx.app/get-started?utm_source=nx-dev&utm_medium=website&utm_campaign=flaky-tasks" icon="nxcloud" description="Sign in to Nx Cloud and connect your repository without the CLI" /%} For the full pipeline walkthrough, see [Setting Up CI](/docs/getting-started/setup-ci). ## Automatically re-run flaky tasks When a flaky task fails in CI with [distributed task execution](/docs/features/ci-features/distribute-task-execution) enabled, Nx will **automatically send that task to a different agent** and run it again (up to 2 tries in total). It's important to run the task on a different agent to ensure that the agent itself or the other tasks that were run on that agent are not the reason for the flakiness. ![Flaky tasks in CI](../../../../assets/features/ci-features/flaky-tasks-ci.png) In this image, the `e2e-ci--src/e2e/app.cy.ts` task is a flaky task that has been automatically retried once. There is a `1 retry` indicator to show that it has been retried and, once expanded, you can see tabs that contain the logs for `Attempt 1` and `Attempt 2`. Comparing the output of a successful and an unsuccessful run side by side is usually where the cause shows up. The narrower the task, the cheaper the retry. [Automated task splitting](/docs/features/ci-features/split-e2e-tasks) turns an e2e suite into one task per test file for `@nx/playwright`, `@nx/cypress`, `@nx/jest`, and `@nx/vitest`, so a single flaky spec file reruns on its own instead of dragging the whole suite with it. ## Flaky task analytics {% aside type="note" title="Enterprise Feature" %} Workspace flaky task analytics is currently available for organizations on the Enterprise plan. Reach out if your organization is [interested in Nx Enterprise](https://nx.dev/enterprise?utm_source=nx.dev&utm_medium=callout&utm_campaign=flaky-task-analytics). {% /aside %} Once a workspace has more flaky tasks than anyone will fix this week, the useful question is which one to fix first. The Nx Cloud dashboard ranks every flaky task in your workspace so you can work down the list instead of guessing. ![Flaky Tasks dashboard](../../../../assets/features/ci-features/nx-cloud-flaky-tasks-metrics-chart.avif) The dashboard displays key metrics over the selected time range (7 days vs 30 days): - **Active flaky tasks** - The total number of tasks in your workspace that have a flake rate greater than 0 within the selected time window. - **Average flake rate** - A weighted average flake rate across all tasks in your workspace. A task that ran 1000 times with a 5% flake rate has more impact than one that ran 10 times with a 50% flake rate. - **High risk tasks** - The number of tasks with a flake rate higher than 20%. Tasks are plotted by **impact score**, calculated as `flake_rate × sample_size`, so frequently-run flaky tasks are weighted higher than rarely-run ones. ### Flaky task table ![Flaky Tasks Analytics Table](../../../../assets/features/ci-features/nx-cloud-flaky-tasks-table.avif) Each row includes: - **Task** - The project and target combination (e.g., `my-app:test`) - **Flake rate** - The percentage of total successes that came from unreliable (flaky) task hashes: `flaky_successes / (flaky_successes + non_flaky_successes)` - **Total reruns** - The number of extra executions caused by flakiness: `total_executions - unique_hash_count` - **Time wasted** - An estimate of the total time spent on reruns: total reruns multiplied by the average task duration - **Last failure** - The timestamp of the most recent failure across all contributing task hashes Click a row to open the detail view. The **Overview** tab shows flake rate, time wasted, and automatic deflake counts for the task, the **Activity** tab shows a timeline of failed and successful executions to jump into the runs, and the **Environments** tab shows where the task was executed to help identify whether certain environments contribute to the flakiness. ![Flaky Task Detail Overview](../../../../assets/features/ci-features/nx-cloud-flaky-tasks-details.avif) Comparing a failed attempt against a successful one is usually where the cause shows up. [Find and fix flaky tests](/docs/kb/flaky-tests-in-ci) covers the common causes and how to fix them. --- ## GitHub Actions integration GitHub Actions runs every job on every push by default, so CI time in a monorepo grows with the size of the repository. Nx keeps GitHub Actions fast by running tasks only for the projects affected by each pull request, restoring unchanged results from a remote cache, and distributing the remaining work across machines. ## How does Nx speed up GitHub Actions? Nx speeds up GitHub Actions in three layers: - `nx affected` runs lint, test, and build only for projects impacted by a pull request. - Nx Cloud restores results that were already computed from a remote cache. - Nx Agents distribute what's left across multiple machines. Affected runs work without an Nx Cloud account, so you can adopt each layer separately. ## Run Nx on GitHub Actions A complete GitHub Actions workflow for an Nx monorepo: ```yaml # .github/workflows/ci.yml name: CI on: push: branches: - main pull_request: permissions: actions: read contents: read jobs: main: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: filter: tree:0 fetch-depth: 0 - uses: actions/setup-node@v6 with: node-version: 24 cache: 'npm' - run: npm ci - uses: nrwl/nx-set-shas@v5 - run: npx nx affected -t lint test build ``` This workflow works without Nx Cloud. To generate it instead of writing it by hand, run `nx g @nx/workspace:ci-workflow --ci=github`. For a walkthrough of each step, see [setting up CI](/docs/getting-started/setup-ci). ## Run only affected projects in GitHub Actions Use `nx affected` instead of `nx run-many` in CI, and Nx compares your changes against a base commit to skip projects that couldn't have been broken. Two pieces of the workflow above make this work: - `fetch-depth: 0` on the checkout step gives Nx access to the full git history, which it needs to compute the changed file set. - `nrwl/nx-set-shas@v5` sets the `NX_BASE` and `NX_HEAD` environment variables that `nx affected` reads. On a pull request, the base is the branch you're merging into. On a push to `main`, the action sets `NX_BASE` to the commit of the last successful workflow run, so commits that land while CI is red still get verified. ## Add remote caching and task distribution Affected pruning skips projects that didn't change. [Remote caching](/docs/features/ci-features/remote-cache) goes further by reusing results for tasks whose inputs are identical to an earlier run, whether that run happened in CI or on a teammate's machine. Connect your workspace by running this command: ```shell npx nx connect ``` Or follow the [Nx Cloud getting started guide](/docs/getting-started/nx-cloud). Once connected, a `.nx/ci-config.yaml` file and one extra step [distribute tasks across multiple machines](/docs/features/ci-features/distribute-task-execution). `npx nx fix-ci` lets self-healing CI propose fixes when tasks fail. Run `start-nx-agents` at the beginning of the main job, after checkout, so the agents boot while the main job installs dependencies. If your pipeline still passes `start-ci-run` flags, see [Migrate from start-ci-run to start-nx-agents](/docs/kb/migrate-to-ci-config). ```yaml # .nx/ci-config.yaml dte: distribute-on: 3 linux-medium-js ``` ```yaml # .github/workflows/ci.yml # ... checkout step as above - run: npx nx start-nx-agents # ... install and nx-set-shas steps as above - run: npx nx affected -t lint test build - run: npx nx fix-ci if: always() ``` You can also enable [task sandboxing](/docs/features/ci-features/sandboxing) to run each distributed task in an isolated sandbox, and track per-agent CPU and memory with [resource usage](/docs/features/ci-features/resource-usage) to right-size your agents. ## What Nx adds to your GitHub PRs With the Nx Cloud GitHub App installed, every pull request gets: ![Nx Cloud bot comment on a GitHub PR showing each command with its status, duration, and a link to its logs](../../../../assets/features/ci-features/github-pr-comment.webp) - A comment with the live status of each task in the run, updated as tasks complete, so you see which check failed without waiting for the whole workflow to finish. - Links to structured, searchable logs for every task instead of one raw CI log. - Links to each run in Nx Cloud, where you can rerun a command locally and pull the outputs CI already available in the remote cache instead of recomputing them. - Proposed fixes from [self-healing CI](/docs/features/ci-features/self-healing-ci) when a task fails, which you can review and apply directly from the PR. ## Connect your repository {% call_to_action title="Get started with Nx Cloud" url="https://cloud.nx.app/get-started?utm_source=nx-dev&utm_medium=website&utm_campaign=github-integration" icon="nxcloud" description="Connect your repository to Nx Cloud" %} Get started with Nx Cloud {% /call_to_action %} --- ## npm Read-Through Cache The npm read-through cache puts a package registry proxy close to your [Nx Agents](/docs/features/ci-features/distribute-task-execution). The first time a package is requested, it is fetched from the public npm registry and stored in the cache. Repeated installs of the same package are then served from the cache instead of the upstream registry, cutting install time and external network usage. Once the cache is warm, packages often install faster than restoring them from `node_modules` caching. It also protects your organization from npm registry outages. ## How to enable it {% aside type="note" title="Requires a dedicated compute cluster" %} The npm read-through cache is an Nx Cloud add-on that runs on a [dedicated compute cluster](/docs/features/ci-features/dedicated-compute-cluster). Request the cluster, then enable this add-on under [**Settings > Add-ons**](https://cloud.nx.app/go/organization/add-ons). {% /aside %} You can enable npm read-through cache through your organization settings: 1. Open [**Settings > Add-ons**](https://cloud.nx.app/go/organization/add-ons) for your organization. 2. Under **Dedicated compute cluster** card, find **npm read-through cache** and click **Request add-on** and confirm. When the add-on is enabled, Nx Cloud runs a caching proxy in your dedicated cluster that sits in front of the public npm registry. You point your package manager at the proxy through an `.npmrc` file. Installs then flow through the cache. The cache works with **npm**, **yarn**, and **pnpm**, since they all read the `registry` setting from `.npmrc`. ## Scope - **Public npm packages only.** The cache proxies the public npm registry. - **Private and scoped packages that require authentication are not cached.** Keep their existing registry entries in your `.npmrc`. Those requests bypass the cache and go straight to your private registry. ## Configuration Point your package manager at the cache by setting the registry in an `.npmrc`. Place it in the project root (simplest) or in the agent's home directory (`~/.npmrc`): ```ini # .npmrc registry=http://npm:4873/ ``` The cache is always reachable from within your cluster at the address above. For package-manager-specific setup, private registry routing, and publishing configuration, see [Configure the npm proxy cache for Nx Agents](/docs/kb/npm-proxy-cache-setup-for-nx-agents). {% aside type="note" title="If you publish packages" %} If your CI publishes npm packages, set a publish registry so `npm publish` targets your real registry instead of the cache. Add `publishConfig.registry` to the package's `package.json`, or pass `npm publish --registry `. {% /aside %} --- ## Remote caching {% youtube src="https://youtu.be/NF1__N_snog" title="Remote caching with Nx Cloud" /%} Nx [caches task results locally](/docs/features/cache-task-results) so the same machine doesn't repeat a task with the same inputs. Remote caching shares those results across developer machines and CI jobs. ![Three developers and CI sharing cached task results](../../../../assets/features/distributed-caching.svg) On a remote cache hit, Nx restores the terminal output and declared task artifacts, such as a build or distribution directory. The command behaves like a local cache hit without running the task again. ## Configure remote caching Connect your workspace to Nx Cloud: ```shell npx nx@latest connect ``` {% call_to_action title="Connect from your browser" url="https://cloud.nx.app/get-started?utm_source=nx-dev&utm_medium=website&utm_campaign=remote-cache" icon="nxcloud" description="Sign in to Nx Cloud and connect your repository without the CLI" /%} For authentication and CI setup details, see [Setting Up CI](/docs/getting-started/setup-ci). ## Why use remote caching Remote caching can reduce work in several places: - Repeated CI runs can reuse tasks that already ran with the same inputs. - Developers can reuse results created in CI when their cache permissions grant access. - CI jobs and Nx Agents can transfer task artifacts through the shared cache. Remote caching complements [affected tasks](/docs/features/ci-features/affected). Affected calculations remove projects that don't need a task, while caching avoids rerunning matching tasks in the remaining graph. [Nx Agents](/docs/features/ci-features/distribute-task-execution) use remote caching to share artifacts between machines. When a task depends on output produced by another agent, Nx restores that output before running the dependent task. ## What Nx Cloud stores Each cached task result contains: - Terminal output written to standard output and standard error. - Files matched by the task's `outputs` configuration. - The hash that identifies the task computation. The cache entry doesn't contain the input source files. Nx hashes those inputs to calculate the task hash. For more information, see [how caching works](/docs/concepts/how-caching-works#what-nx-caches). Cache correctness depends on accurate `inputs` and `outputs`. A missing input can produce a stale cache hit, while an undeclared output won't be restored. [Task sandboxing](/docs/features/ci-features/sandboxing) can identify file access that isn't represented in the task configuration. ## Remote cache security Nx Cloud protects cached results with several controls: - Cache entries are immutable after they're written. - Access tokens control read and write permissions. - End-to-end encryption is available for task artifacts. - Enterprise plans offer regional and self-hosted deployment options. Configure token permissions so developer machines can read cached results while trusted CI jobs write them. For the permission model and protection against cache poisoning, see [cache security](/docs/kb/cache-security). For end-to-end encryption setup, see [encryption](/docs/guides/nx-cloud/encryption). Nx publishes current compliance information on the [Nx security site](https://security.nx.app). ## Remote cache availability Nx checks the local cache before requesting a result from the remote cache. If the remote cache is unavailable, local cache hits remain available, and Nx runs tasks that aren't cached locally. ## Enterprise remote caching Organizations with specific data residency or infrastructure requirements can [contact the Nx Enterprise team](https://nx.dev/enterprise/trial) to discuss remote caching deployment options. ## Skip remote caching Use `--skipRemoteCache` when one command shouldn't read from or write to the remote cache. For related local cache options, see [skip task caching](/docs/kb/skipping-cache). --- ## Resource Usage Resource usage records CPU and memory metrics while your tasks run and surfaces them in Nx Cloud. Use this data to find resource bottlenecks, debug out-of-memory (OOM) errors, and analyze the tasks that cause resource spikes. {% aside type="caution" title="Nx 22.1+ Required" %} Resource usage requires Nx 22.1 or later. {% /aside %} ## Enable resource usage Resource usage is an add-on, enabled by default for new organizations. Once it's on, Nx uploads CPU and memory metrics for your CI runs and you view them in Nx Cloud. There's nothing else to set up. Data collection for each report costs 10 credits. With Nx Agents, each agent generates its own report. For non-distributed runs, every report generated on the same machine counts as a single 10 credit charge, however many runs happen there. See the [credits pricing reference](/docs/reference/nx-cloud/credits-pricing) for details. Data collection can be turned off at either the organization or the workspace level. ### Organization Turn the resource usage add-on on or off under **Settings > Add-ons**. Disabling it here stops collection for every workspace in the organization. ![The resource usage add-on toggle in organization settings](../../../../assets/guides/nx-cloud/resource-usage-org-add-on.avif) ### Workspace Once the add-on is on for the organization, go to **Workspace > Settings** and find the **Resource usage reports** card. Two switches control collection independently: - **Distributed runs** - metrics for runs distributed across Nx Agents. - **Non distributed runs** - metrics for runs from non-distributed Nx commands. ![The distributed and non distributed run toggles in workspace settings](../../../../assets/guides/nx-cloud/resource-usage-workspace-toggles.avif) Turning a switch off stops new reports from being generated. Reports collected earlier stay viewable as long as the add-on is active for the organization. Metrics are collected in CI only. Runs on a developer machine don't report resource usage. {% aside type="note" title="Bringing your own compute" %} If you [bring your own compute](/docs/kb/bring-your-own-compute) and run the agents on your own CI, add a single CLI step per agent job to upload metrics. See [the section below](#resource-metrics-when-you-bring-your-own-compute). {% /aside %} ## Viewing resource usage What gets measured depends on how you run. With Nx Agents, Nx records every task an agent executes. For a standalone Nx command in CI, it records every task in the run. ### Runs with Nx Agents Open any CI pipeline execution and go to the **Resource usage** tab. ![The Resource usage tab for a run distributed across Nx Agents](../../../../assets/guides/nx-cloud/resource-usage-agents-view.avif) #### Agent resource usage summary The **Agent resource usage** table lists every agent in the run with its average and maximum CPU and memory, plus the machine specs (cores and RAM) of its resource class. It's the fastest way to spot an agent that ran hot. ![Agent resource usage table showing per-agent average and maximum CPU and memory](../../../../assets/guides/nx-cloud/agent-resource-usage-table.png) #### Resource usage over time Click an agent to open its **Resource usage over time** view. Separate memory and CPU charts plot utilization across the agent's lifetime, with reference lines for the machine's capacity and peak usage. When a task exceeds available memory and is killed, the chart marks the out-of-memory point so you can trace the failure back to the task that caused it. ![Resource usage over time showing memory and CPU charts by task](../../../../assets/guides/nx-cloud/resource-chart-details.png) The detail view has a few controls for digging in: - **View mode** - switch between **Individual** (each task or process plotted separately) and **Stacked** (total usage at any point in time). ![Stacked view showing total resource usage](../../../../assets/guides/nx-cloud/resource-stacked-chart-view.png) - **Reference lines** - toggle the capacity and peak-usage lines on or off. - **Snap to max** - zoom the axis to the peak memory or CPU value. - **Legend** - click items to focus on specific tasks or processes (for example a single `nx build`, the Nx daemon, or CLI overhead). ![Using the legend to focus on specific tasks](../../../../assets/guides/nx-cloud/resource-chart-legend.png) - **Timeline scrubber** - jump to a point in time or zoom in on a spike. ![Timeline scrubber for navigating resource usage over time](../../../../assets/guides/nx-cloud/resource-chart-scrubber.jpg) - **Download CSV** - export the raw per-process data for deeper analysis. ### Runs without Nx Agents For a run that isn't distributed on Nx Agents, open the run details and go to the **Resource usage** view. ![Resource usage view for a run that isn't distributed on Nx Agents](../../../../assets/guides/nx-cloud/resource-usage-non-distributed-view.avif) Three cards across the top give you the machine specs plus average and maximum figures for CPU and memory. Memory and CPU charts below them plot utilization over the run's lifetime, broken down by process. The two charts share an x-axis, so hovering one shows you the same moment in the other. The controls match the per-agent detail view described above: switch between **Individual** and **Stacked** view modes, toggle reference lines, snap the axis to peak usage, click legend items to isolate a process, drag across a chart to zoom into a spike, and download the raw per-process data as CSV. **Reset zoom** returns to the full timeline. ## Common use cases - **Find memory-hungry tasks** - figure out which project eats the most memory when running in parallel, then lower its parallelism instead of slowing everything down. - **Debug OOM kills** - trace an out-of-memory failure to the exact task that caused it. - **Spot misconfigured tooling** - catch a bundler or build tool pulling in more files than it should. - **Right-size agents** - pick the correct agent resource class when moving to Nx Agents from GitHub Actions or another CI provider. - **Detect memory leaks** - look for tasks where memory keeps climbing over time. - **Compare before and after upgrades** - check whether a dependency upgrade spiked resource usage. ## Resource metrics when you bring your own compute {% aside type="note" title="Enterprise Feature" %} Bringing your own compute requires the [Nx Enterprise plan](https://nx.dev/enterprise?utm_source=nx.dev&utm_medium=callout&utm_campaign=bring-your-own-compute). [Nx Agents](/docs/features/ci-features/distribute-task-execution) distributes your tasks on all plans. {% /aside %} If you run the agents on your own CI compute, Nx Cloud can still collect per-agent CPU and memory metrics. Opt in by adding a single CLI step to each agent job. ### What to add At the end of each agent job, run `npx nx-cloud upload-agent-metrics`. Use your provider's always-run mechanism so the step runs even when the agent is killed mid-task, which is precisely the scenario where the metrics matter most. Here's the GitHub Actions step: ```yaml - name: Upload agent metrics if: always() run: npx nx-cloud upload-agent-metrics env: NX_AGENT_NAME: ${{ matrix.agent }} ``` The `if: always()` condition is important: if an agent is OOM-killed mid-run, the normal step sequence stops, but the upload still needs to happen so you can see which task caused the kill. The [bring your own compute guide](/docs/kb/bring-your-own-compute) shows the equivalent step for CircleCI, Azure Pipelines, Bitbucket Pipelines, GitLab CI, and Jenkins. ## Configuration Metrics collection is controlled by these environment variables: | Variable | Description | | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `NX_CLOUD_DISABLE_METRICS_COLLECTION` | Set to the exact string `true` to disable CPU and memory metric collection during task execution. Other values, including `1` and `TRUE`, don't disable it. | | `NX_CLOUD_METRICS_DIRECTORY` | Directory where Nx writes resource metrics during task execution. | --- ## Task Sandboxing Task sandboxing confines each task to the files it declares as `inputs` and `outputs` in your [project configuration](/docs/reference/project-configuration) (whether explicit or [inferred](/docs/concepts/mental-model#inferred-tasks)). Reading a file the task didn't declare, or writing outside its declared outputs, is a sandbox violation. Undeclared dependencies have direct implications on [caching](/docs/features/cache-task-results) correctness, from false cache hits serving stale results to missing output files after a cache restore. {% aside type="note" title="Nx Cloud add-on" %} Sandboxing is an Nx Cloud add-on that runs on a [dedicated compute cluster](/docs/features/ci-features/dedicated-compute-cluster). Request the cluster, then enable sandboxing under [**Settings > Add-ons**](https://cloud.nx.app/go/organization/add-ons). {% /aside %} {% aside type="caution" title="Nx 22.6+ Required" %} Task sandboxing requires Nx 22.6 or later. {% /aside %} ## Why hermeticity matters A task is _hermetic_ when it only reads from its declared inputs and only writes to its declared outputs. Hermetic tasks are safe to cache and replay because their behavior is fully described by their configuration. ![Diagram showing declared inputs flowing into vite build, producing declared outputs, with a cache key derived from the input hash](../../../../assets/features/sandboxing-cache-flow.svg) [Computation caching](/docs/features/cache-task-results) relies on declared `inputs` and `outputs` to determine when a cache entry is valid. If a task reads a file that isn't listed in its inputs, the cache has no way to know that file changed, and it'll serve a stale result. If a task writes files outside its declared outputs, those files won't be captured in the cache and will go missing on a cache hit. Undeclared dependencies are difficult to catch through code review alone. A task might work correctly for months until an unrelated change to an undeclared input causes a cache-related failure that's hard to trace back to the root cause. ### Example: undeclared input (false cache hit) An API app reads `app.yaml` from the project root at startup to configure routes and middleware. If `app.yaml` isn't included in the target's `inputs`, changing the configuration won't invalidate the cache. The next CI run serves stale output built against the old configuration. Sandboxing catches this because it sees the build process reading `app.yaml` even though it isn't declared. Add the file to the target's `inputs` in `project.json` to fix it: ```json {% meta="{4}" %} { "targets": { "build": { "inputs": ["{projectRoot}/app.yaml"] } } } ``` ### Example: undeclared output (missing artifacts on replay) When using TypeScript `*.d.ts` generation (such as `vite-plugin-dts`) to emit type declarations outside of `dist/`, the build produces two output directories: ```text {% meta="{3-4}" %} packages/ my-package/ dist/ types/ src/ package.json ``` If the target's `outputs` only lists `["{projectRoot}/dist"]`, the `types/` directory isn't [stored in the remote cache](/docs/features/ci-features/remote-cache#what-gets-stored). On a cache hit, `dist/` is restored but the type declarations are missing, breaking downstream library consumers. Sandboxing catches this because it sees writes to `types/` that aren't covered by the declared outputs. Include both directories in `outputs` so they can be replayed from cache: ```json {% meta="{4}" %} { "targets": { "build": { "outputs": ["{projectRoot}/dist", "{projectRoot}/types"] } } } ``` ## How sandboxing works Sandboxing runs each task in an isolated environment scoped to its declared `inputs` and `outputs`. You get an audit trail of every file each task touched during execution, warnings when tasks have undeclared dependencies, and confidence that your cache configuration is correct rather than only appearing to work. In **Warning** mode (recommended when getting started), violations are reported in the Nx Cloud UI but tasks continue to completion. In **Strict** mode, tasks fail immediately when a violation is detected. See [Cloud settings](#cloud-settings) to configure the enforcement mode. ## Investigating violations When sandboxing detects undeclared file access, a warning banner appears on the CI Pipeline Execution (CIPE) page in Nx Cloud. Runs that contain violations are tagged with a "sandbox violation" badge. ![CIPE page showing sandbox violations detected in 109 tasks, with a run tagged as sandbox violation](../../../../assets/features/sandboxing-cipe-violations.png) Click into a run to see which individual tasks have violations. Click the **Sandbox violations** button to filter the task list to only tasks with undeclared reads or writes. ![Task list showing individual tasks with sandbox violation badges](../../../../assets/features/sandboxing-task-violations.png) Open the task details and switch to the **Sandbox analysis** tab. The process tree shows every process spawned during the task, along with the files each one read and wrote. Click a process to see its full file access list. Files flagged as "unexpected read" or "unexpected write" are the ones not covered by your declared `inputs` and `outputs`. ![Sandbox analysis tab showing process tree with unexpected reads and writes highlighted](../../../../assets/features/sandboxing-analysis.png) To export the raw report data for further analysis, click **View raw sandbox report** to download the JSON report. ![View raw sandbox report button](../../../../assets/features/sandboxing-raw-report.png) Once you have identified the violating tasks, follow [Fix sandbox violations](/docs/kb/fix-sandbox-violations) to download every report on a branch, classify each violation, and update your project configuration in a structured loop. ## Sandbox violations dashboard For an organization-wide view, open **Analytics > Sandbox violations** for your workspace. It summarizes the most recent report for each task over a time window (the last 7 days by default) with two tiles, **Tasks with violations** and **Clean tasks**, and a table of every task showing its count of unexpected reads and writes and when it was last seen. Filter by branch or task to narrow it down. The **How to fix these violations** panel offers two paths. **Fix with AI** copies a ready-made prompt for your coding agent that downloads the reports, edits the task config, and validates before stopping. The manual path gives you the equivalent command sequence. Either way, [Fix sandbox violations](/docs/kb/fix-sandbox-violations) walks through the full loop. ## Inspecting inputs and outputs Check what your tasks currently declare before enabling sandboxing. Use `nx show target` to inspect a specific target's resolved inputs and outputs: {% aside type="caution" title="Nx 22.6+ Required" %} The `--inputs` and `--outputs` flags for `nx show target` require Nx 22.6 or later. {% /aside %} ```shell nx show target : --inputs --outputs ``` Add the `--json` flag for machine-readable output: ```shell nx show target : --inputs --outputs --json ``` For a broader view of the full project configuration, run `nx show project`: ```shell nx show project ``` You can also use the [project details view](/docs/features/explore-graph) in Nx Console: {% project_details title="Project Details View" expandedTargets=["build"] %} ```json { "project": { "name": "myapp", "type": "app", "data": { "root": "apps/myapp", "targets": { "build": { "options": { "cwd": "apps/myapp", "command": "vite build" }, "cache": true, "dependsOn": ["^build"], "inputs": [ "production", "^production", { "externalDependencies": ["vite"] } ], "outputs": ["{projectRoot}/dist"], "executor": "nx:run-commands", "configurations": {} } }, "sourceRoot": "apps/myapp/src", "projectType": "application", "tags": [] } }, "sourceMap": { "root": ["apps/myapp/project.json", "nx/core/project-json"], "targets.build": ["apps/myapp/vite.config.ts", "@nx/vite/plugin"], "targets.build.inputs": ["apps/myapp/vite.config.ts", "@nx/vite/plugin"], "targets.build.outputs": ["apps/myapp/vite.config.ts", "@nx/vite/plugin"] } } ``` {% /project_details %} The `inputs` and `outputs` arrays show exactly what the cache tracks. Sandboxing compares these declarations against what the task actually reads and writes at runtime and reports discrepancies. ## Enabling sandboxing Sandboxing requires a [dedicated compute cluster](/docs/features/ci-features/dedicated-compute-cluster) and runs on [Nx Agents](/docs/features/ci-features/distribute-task-execution). It is not supported when you [bring your own compute](/docs/kb/bring-your-own-compute). 1. Request a dedicated compute cluster under [**Settings > Add-ons**](https://cloud.nx.app/go/organization/add-ons), if you don't already have one. 2. Enable **Sandboxing** on the same page. If the cluster is still being provisioned, sandboxing is queued and activates automatically once the cluster is ready. Nx Enterprise [single-tenant](/docs/enterprise/single-tenant/overview) customers already run on a dedicated environment. Contact your Nx representative to turn on sandboxing for your deployment. ### Excluding paths Create a `.nx/workflows/sandboxing-config.yaml` file to exclude paths from sandboxing checks. ```yaml # .nx/workflows/sandboxing-config.yaml exclude-reads: - '**/vite.config.*.timestamp-*' task-exclusions: - project: myapp target: build exclude-reads: - .next/cache/** exclude-writes: - logs/** - target: lint exclude-reads: - .eslintcache/** ``` Global `exclude-reads` and `exclude-writes` apply to all tasks. Use `task-exclusions` to scope exclusions to a specific project, target, or combination of both. Patterns use glob syntax relative to the workspace root. ## Cloud settings Once sandboxing is enabled, configure the enforcement mode in the Nx Cloud workspace settings under **Settings > General**. ![Nx Cloud settings sidebar showing General settings](../../../../assets/features/sandboxing-settings-sidebar.png) Three enforcement modes are available: - **Strict** - tasks that violate sandbox isolation fail immediately. - **Warning** - tasks complete but violations are reported in the Nx Cloud UI. - **Off** - sandboxing is disabled. ![Sandboxing enforcement mode setting with Strict, Warning, and Off options](../../../../assets/features/sandboxing-settings.png) --- ## AI-Powered Self-Healing CI {% youtube src="https://youtu.be/aQUlsilNSQ8" title="How Nx Self-Healing CI Works" /%} Nx Cloud Self-Healing CI is an **AI-powered system that automatically detects, analyzes, and proposes fixes for CI failures**, offering several key advantages: - **Improves Time to Green (TTG):** Automatically proposes fixes when tasks fail, significantly reducing the time to get your PR merge-ready. No more babysitting PRs. - **Keeps You in the Flow:** Get notified about failed PRs and proposed fixes via PR/MR comments or directly in your editor with Nx Console (VS Code, Cursor, or WebStorm). Review, approve, and keep working while AI handles the rest. - **Leverages Deep Context:** AI agents understand your workspace structure, project relationships, and build configurations through the Nx [project graph](/docs/features/explore-graph) and metadata. - **Non-Invasive Integration:** Works with your existing CI provider without overhauling your current setup. ## Enable self-healing CI {% aside title="VCS Integration Required" type="note" %} Your Nx Cloud workspace needs to have a [VCS integration enabled](/docs/kb/source-control-integration) to use Self-Healing CI. Self-Healing CI supports GitHub, GitLab, Azure DevOps, and Bitbucket. {% /aside %} To enable Self-Healing CI in your workspace, you'll need to connect to Nx Cloud and configure your CI pipeline. If you haven't already connected to Nx Cloud, run the following command: ```shell npx nx@latest connect ``` {% call_to_action title="Connect from your browser" url="https://cloud.nx.app/get-started?utm_source=nx-dev&utm_medium=website&utm_campaign=self-healing-ci" icon="nxcloud" description="Sign in to Nx Cloud and connect your repository without the CLI" /%} For the full pipeline walkthrough, see [Setting Up CI](/docs/getting-started/setup-ci). Next, check the [Nx Cloud workspace settings](https://cloud.nx.app/go/workspace/settings/self-healing-ci) in the Nx Cloud web application to ensure that "Self-Healing CI" is enabled. ### Configure your CI pipeline Add a step to your CI configuration's "main" job that runs the `fix-ci` command. It doesn't matter exactly what this job is called, it is whichever job in your config where you invoke `nx start-nx-agents` and kick off your `nx run-many` or `nx affected` commands. By default, your CI provider will only run the step if the previous steps succeeded, but by definition we want to run Self-Healing CI even when previous steps fail. Therefore make sure you are using a condition of `if: always()` or equivalent to ensure it runs even when previous steps fail: These examples distribute tasks across Nx Agents, so they also need a `.nx/ci-config.yaml` file at the workspace root. See the [CI configuration file reference](/docs/reference/nx-cloud/ci-config) for every key you can set. ```yaml # .nx/ci-config.yaml dte: distribute-on: 3 linux-medium-js lifecycle: stop-after: - lint - test - build ``` {% tabs %} {% tabitem label="GitHub Actions" %} ```yaml # .github/workflows/ci.yml name: CI jobs: main: runs-on: ubuntu-latest steps: # Your existing steps which check out the repo, start-nx-agents, install # dependencies, etc. # These are just illustrative examples... - uses: actions/checkout@v7 - uses: actions/setup-node@v6 - run: npx nx start-nx-agents - run: npm ci - run: npx nx affected -t lint test build # NEW: Add this step at the end of your job - run: npx nx fix-ci if: always() # IMPORTANT: Always run ``` {% /tabitem %} {% tabitem label="GitLab CI" %} ```yaml # .gitlab-ci.yml stages: - build main: stage: build script: # Your existing steps which check out the repo, start-nx-agents, install # dependencies, etc. - npx nx start-nx-agents - npm ci - npx nx affected -t lint test build # NEW: Add this section at the end of your job if you don't # already have an after_script section after_script: # IMPORTANT: after_script runs regardless of job success/failure # so it's like if: always() on GitHub - npx nx fix-ci ``` {% /tabitem %} {% tabitem label="Azure DevOps" %} ```yaml # azure-pipelines.yml trigger: - main pool: vmImage: 'ubuntu-latest' steps: # Your existing steps which check out the repo, start-nx-agents, install # dependencies, etc. # These are just illustrative examples... - task: NodeTool@0 inputs: versionSpec: '22.x' - script: npx nx start-nx-agents - script: npm ci - script: npx nx affected -t lint test build # NEW: Add this step at the end of your job - script: npx nx fix-ci condition: always() # IMPORTANT: Always run ``` {% /tabitem %} {% tabitem label="Bitbucket Pipelines" %} ```yaml # bitbucket-pipelines.yml image: node:22 pipelines: pull-requests: '**': - step: name: CI script: # Your existing steps which start-nx-agents, install # dependencies, etc. # These are just illustrative examples... - npx nx start-nx-agents - npm ci - npx nx affected -t lint test build after-script: # NEW: Add this section at the end of your step # IMPORTANT: after-script runs regardless of step success/failure # so it's like if: always() on GitHub - npx nx fix-ci ``` {% /tabitem %} {% /tabs %} > NOTE: If all tasks succeed then the `fix-ci` command becomes a no-op automatically, so that is why "always" is recommended. {% aside type="note" title="Bringing your own compute?" %} Bringing your own compute requires the [Nx Enterprise plan](https://nx.dev/enterprise?utm_source=nx.dev&utm_medium=callout&utm_campaign=bring-your-own-compute). When you run the agents on your own CI, Self-Healing CI works the same way. Add `nx fix-ci` to both the **main job** (the orchestrator) and each **agent job** with the appropriate "always run" condition. See the [bring your own compute guide](/docs/kb/bring-your-own-compute) for complete examples. {% /aside %} ## Configuring self-healing CI Self-Healing CI configuration is primarily managed through your [Nx Cloud workspace settings](https://cloud.nx.app/go/workspace/settings/self-healing-ci) in the Nx Cloud dashboard. This provides a centralized, auditable way to control the feature's behavior. ### General settings | Setting | Description | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Enable Self-Healing CI** | Enable Self-Healing CI for PRs in the current workspace | | **GitHub PR comments** | Show Self-Healing CI feedback and actions within GitHub PR comments (in addition to the Nx Cloud UI) | | **Auto-retry flaky tasks** | Automatically re-run tasks that are detected as flaky to improve CI reliability. This works by pushing an empty commit to the PR branch | | **Allow public link access** | Allow anyone with access to the link the ability to apply or reject a suggested change (not recommended unless absolutely necessary) | | **Draft PR handling** | Allow Self-Healing CI to create fixes for draft PRs | | **Protected branch prefixes** | Configure branch prefixes for which fixes should not be generated (e.g., `release/` will match `release/v1.0`). The default branch and branches named `main`, `master`, `trunk`, `dev`, `stable`, or `canary` will never have fixes generated | ### Eligible tasks Control which failing tasks Nx Cloud will actively try and fix for pull request CI pipeline executions. {% aside type="note" title="Configuration override available" %} This is analogous to [`ai.fix-tasks-patterns`](/docs/reference/nx-cloud/ci-config#aifix-tasks-patterns) in `.nx/ci-config.yaml`. Anything set there takes precedence over the settings here. {% /aside %} You can choose between two modes: | Mode | Description | | --------------------- | -------------------------------------------------------------------------------- | | **Any failing task** | Any task that fails during PR CI pipeline executions is eligible (recommended) | | **Specific patterns** | Limit Self-Healing CI to failing PR tasks that match the specified glob patterns | You can also specify **Never fix** patterns to exclude certain tasks from ever being fixed by Self-Healing CI. For example, `*e2e*` would exclude all e2e-related tasks. ### Auto-apply verified code changes {% youtube src="https://youtu.be/30qh5W8zXTY" title="Self-Healing CI Auto-Apply Suggestions" /%} Automatically commit code change suggestions to the PR branch **when ALL of these are true**: 1. The task **matches the glob patterns** configured below 2. The AI agent is **highly confident** that the suggested code change will fix the failing task 3. The suggestion has been **explicitly verified** to fix the failing task {% aside type="note" title="Configuration override available" %} This is analogous to [`ai.auto-apply-patterns`](/docs/reference/nx-cloud/ci-config#aiauto-apply-patterns) in `.nx/ci-config.yaml`. Anything set there takes precedence over the settings here. {% /aside %} #### Deterministic Nx checks A built-in preset that auto-fixes failures from `nx format:check`, `nx sync:check`, and `nx conformance:check` commands. The AI agent has special knowledge of these commands and will invoke the corresponding "writable" version to fix the issue (e.g., `nx format`). You can safely enable this preset even if you only use a subset of these commands. #### Additional include patterns Tasks matching these patterns will also have high-confidence, verified code changes auto-applied. For example: `*build*`, `*test*`, `lint`. #### Exclude patterns Tasks matching these patterns will **never** have code changes auto-applied, even if they match the include patterns or presets specified above. For example: `*e2e*`. ## Configuration with SELF_HEALING.md Create a `.nx/SELF_HEALING.md` file in your repository to provide project-specific instructions to the Self-Healing CI agent. This file contains freeform markdown that the AI agent reads and interprets naturally. {% aside title="Why a dedicated file?" type="note" %} Using `.nx/SELF_HEALING.md` instead of `AGENTS.md` (or equivalent) separates CI-specific instructions from local development context. The file lives in the `.nx` directory alongside other Nx Cloud configuration. {% /aside %} ### Example SELF_HEALING.md ```markdown # Self-Healing Configuration ## Confidence Rules - Fixes involving "test" targets should require high confidence - Formatting fixes can be applied with medium confidence ## Off-Limits Areas - `/src/generated/` - auto-generated, do not modify - `/legacy/` - requires manual review ## Fix Preferences - Prefer updating ESLint rules over adding disable comments - For type errors, prefer explicit types over `any` ## Context See ARCHITECTURE.md for module boundaries. ``` ### What to include | Section | Purpose | Example | | -------------------- | --------------------------------------------------- | ---------------------------------------------------------------------------- | | **Confidence Rules** | Override how the AI categorizes failure severity | "Failures in `**/migrations/**` should be classified as `environment_state`" | | **Off-Limits Areas** | Directories or files the agent should never modify | "`/src/generated/` - auto-generated code" | | **Fix Preferences** | Guide the agent's approach to common issues | "Prefer updating ESLint rules over adding disable comments" | | **Predefined Fixes** | Specify deterministic solutions for known failures | "For lint failures, always try running `nx lint --fix` first" | | **Context** | Reference other documentation the agent should read | "See ARCHITECTURE.md for module boundaries" | ### Using CLAUDE.md If your repository already has a `CLAUDE.md` file at the root, the Self-Healing CI agent will read it for additional context. When both files exist: - **SELF_HEALING.md takes precedence** for any conflicting instructions - Both files are read, so general context in `CLAUDE.md` is still available - CI-specific instructions should go in `SELF_HEALING.md` This allows teams to maintain `CLAUDE.md` for local development workflows while using `SELF_HEALING.md` for CI-specific behavior. ### Viewing configuration status After a CI run, navigate to the pipeline execution in Nx Cloud and check the **Configurations** tab to see whether `SELF_HEALING.md` was detected and applied. ## Receiving fix notifications ### In your editor With [Nx Console](/docs/getting-started/editor-setup) installed, you'll receive notifications directly in VS Code, Cursor, or WebStorm when a fix is available: ![Notification in your editor about an AI fix](../../../../assets/features/notification-self-healing-ci.avif) ### On your pull request/Merge request Self-Healing CI posts a comment on your PR with: - A summary of the reasoning behind the fix - A diff view showing the proposed changes - Buttons to apply or reject the fix ![Self-Healing CI GitHub Comment](../../../../assets/features/self-healing-fix-gh-comment.avif) ## Applying and reverting fixes ### Applying a fix You can apply a proposed fix through: 1. **Editor notification** - Click "Apply" in the Nx Console notification 2. **PR/MR comment** - Click the "Apply" button 3. **Nx Cloud UI** - Use the apply button in the diff viewer ### Applying locally for fine-tuning If a fix is 90% correct but needs minor adjustments: 1. Click "Apply Locally" in the GitHub comment or Nx Cloud UI 2. Run the provided command in your terminal 3. Make your adjustments and commit The provided command takes the form below. The fix identifier is shown in the Nx Cloud UI and in the PR/MR comment. Pass `--no-interactive` to disable prompting. ```shell nx apply-locally ``` ![Apply Self-Healing Fixes Locally](../../../../assets/features/self-healing-apply-locally.avif) ### Reverting a fix If you accidentally applied a fix, you can: 1. Manually revert the Git commit 2. Use the "Revert changes" button in the Nx Cloud diff viewer ## Advanced: Configuration overrides {% aside type="caution" title="Prefer workspace settings" %} We recommend configuring Self-Healing CI through **[workspace settings](https://cloud.nx.app/go/workspace/settings/self-healing-ci)** in the Nx Cloud dashboard. Configuration overrides should only be used for temporary, branch-specific adjustments. {% /aside %} {% youtube src="https://youtu.be/KSb48zHbaHg" title="Specify Which Tasks to Fix" /%} ### When to use configuration overrides - **Temporarily disable** Self-Healing CI on a sensitive branch - **Test configurations** before committing to workspace settings ### Available settings Set these under `ai` in your [`.nx/ci-config.yaml`](/docs/reference/nx-cloud/ci-config) file: | Key | Description | Example | | --------------------- | ------------------------------------------- | ---------------------- | | `fix-tasks-patterns` | Override which tasks are eligible for fixes | `['*lint*', '*test*']` | | `auto-apply-patterns` | Override which tasks can be auto-applied | `['*lint*']` | ### Pattern syntax Patterns use glob syntax to match task names, and a `!` prefix negates a pattern: ```yaml # .nx/ci-config.yaml ai: # Fix lint and test tasks, but never e2e fix-tasks-patterns: - '*lint*' - '*test*' - '!*e2e*' # Auto-apply only lint fixes auto-apply-patterns: - lint ``` ### Precedence rules When both workspace settings and `.nx/ci-config.yaml` are present: 1. **`.nx/ci-config.yaml`** takes precedence for the keys it sets 2. Falls back to **workspace settings** for the keys it does not set ### Viewing applied configuration After your CI runs, navigate to the CIPE, then the **Configurations** tab to see: - What workspace settings were relevant at the time of the CI pipeline execution - What the configuration file supplied, if anything - What the final, effective configuration was --- ## Learn more - [Nx AI Documentation](/docs/features/enhance-ai) - [Nx Console Editor Setup](/docs/getting-started/editor-setup) - [Nx Cloud](/docs/features/ci-features) --- ## Automatically Split Slow Tasks by File (Atomizer) {% youtube src="https://youtu.be/0YxcxIR7QU0" title="10x Faster e2e Tests!" width="100%" /%} Certain tasks like end-to-end (e2e) tests, integration tests, or large unit test suites can be large, monolithic tasks that take a considerable amount of time to execute. As a result, teams often push them to a nightly or even weekly build rather than running them for each PR. This approach is suboptimal as it increases the risk of merging problematic PRs. Manually splitting these slow tasks can be complex and require ongoing maintenance. Nx Atomizer solves this by **automatically generating runnable targets for each test file**. For example, a task that takes 10 minutes can be split and distributed as five 2-minute tasks per agent. This allows for: - parallelization across multiple machines with [Nx Agents](/docs/features/ci-features/distribute-task-execution) - faster [flaky test detection & retries](/docs/features/ci-features/flaky-tasks) by isolating and re-running only the failed tests ## Enable automated task splitting ### Step 1: Connect to Nx Cloud To use **automated task splitting**, you need to connect your workspace to Nx Cloud (if you haven't already). ```shell npx nx@latest connect ``` {% call_to_action title="Connect from your browser" url="https://cloud.nx.app/get-started?utm_source=nx-dev&utm_medium=website&utm_campaign=split-e2e-tasks" icon="nxcloud" description="Sign in to Nx Cloud and connect your repository without the CLI" /%} For the full pipeline walkthrough, see [Setting Up CI](/docs/getting-started/setup-ci). ### Step 2: add the appropriate plugin Run this command to set up inferred tasks and enable task splitting for each plugin: {% tabs syncKey="test-runner" %} {% tabitem label="Cypress" %} ```shell nx add @nx/cypress ``` {% /tabitem %} {% tabitem label="Playwright" %} ```shell nx add @nx/playwright ``` {% /tabitem %} {% tabitem label="Jest" %} ```shell nx add @nx/jest ``` {% /tabitem %} {% tabitem label="Vitest" %} ```shell nx add @nx/vitest ``` {% /tabitem %} {% tabitem label="Gradle" %} ```shell nx add @nx/gradle ``` {% /tabitem %} {% /tabs %} This command will register the appropriate plugin in the `plugins` array of `nx.json`. If you upgraded Nx from an older version, ensure that [inferred tasks](/docs/kb/convert-to-inferred#enable-inferred-tasks-for-older-workspaces) are enabled in `nx.json`: ```json // nx.json { ... // turned on by default; just make sure it is not set to false useInferencePlugins: true } ``` ## Update an existing project to use automated task splitting If you are already using the `@nx/cypress`, `@nx/playwright`, `@nx/jest`, `@nx/vitest`, or `@nx/gradle` plugin, you need to manually add the appropriate configuration to the `plugins` array of `nx.json`. Follow the instructions for the plugin you are using: - [Configure Cypress Task Splitting](/docs/technologies/test-tools/cypress/introduction#nxcypress-configuration) - [Configure Playwright Task Splitting](/docs/technologies/test-tools/playwright/introduction#nxplaywright-configuration) - [Configure Jest Task Splitting](/docs/technologies/test-tools/jest/introduction#splitting-e2e-tests) - [Configure Vitest Task Splitting](/docs/technologies/test-tools/vitest/introduction#splitting-e2e-tests) - [Configure Gradle Testing Task Splitting](/docs/technologies/java/gradle/introduction#test-distribution) ## Verify automated task splitting works Run the following command to open the project detail view for your test project: {% tabs %} {% tabitem label="CLI" %} ```shell nx show project my-project-e2e ``` {% /tabitem %} {% tabitem label="Project Detail View" %} {% project_details title="Project Details View" %} ```json { "project": { "name": "admin-e2e", "data": { "metadata": { "targetGroups": { "E2E (CI)": [ "e2e-ci--src/e2e/app.cy.ts", "e2e-ci--src/e2e/login.cy.ts", "e2e-ci" ] } }, "root": "apps/admin-e2e", "projectType": "application", "targets": { "e2e": { "cache": true, "inputs": ["default", "^production"], "outputs": [ "{workspaceRoot}/dist/cypress/apps/admin-e2e/videos", "{workspaceRoot}/dist/cypress/apps/admin-e2e/screenshots" ], "executor": "nx:run-commands", "dependsOn": ["^build"], "options": { "cwd": "apps/admin-e2e", "command": "cypress run" }, "configurations": { "production": { "command": "cypress run --env webServerCommand=\"nx run admin:preview\"" } }, "metadata": { "technologies": ["cypress"] } }, "e2e-ci--src/e2e/app.cy.ts": { "outputs": [ "{workspaceRoot}/dist/cypress/apps/admin-e2e/videos", "{workspaceRoot}/dist/cypress/apps/admin-e2e/screenshots" ], "inputs": [ "default", "^production", { "externalDependencies": ["cypress"] } ], "cache": true, "options": { "cwd": "apps/admin-e2e", "command": "cypress run --env webServerCommand=\"nx run admin:serve-static\" --spec src/e2e/app.cy.ts" }, "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["cypress"] } }, "e2e-ci--src/e2e/login.cy.ts": { "outputs": [ "{workspaceRoot}/dist/cypress/apps/admin-e2e/videos", "{workspaceRoot}/dist/cypress/apps/admin-e2e/screenshots" ], "inputs": [ "default", "^production", { "externalDependencies": ["cypress"] } ], "cache": true, "options": { "cwd": "apps/admin-e2e", "command": "cypress run --env webServerCommand=\"nx run admin:serve-static\" --spec src/e2e/login.cy.ts" }, "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["cypress"] } }, "e2e-ci": { "executor": "nx:noop", "cache": true, "inputs": [ "default", "^production", { "externalDependencies": ["cypress"] } ], "outputs": [ "{workspaceRoot}/dist/cypress/apps/admin-e2e/videos", "{workspaceRoot}/dist/cypress/apps/admin-e2e/screenshots" ], "dependsOn": [ { "target": "e2e-ci--src/e2e/app.cy.ts", "projects": "self", "params": "forward", "options": "forward" }, { "target": "e2e-ci--src/e2e/login.cy.ts", "projects": "self", "params": "forward", "options": "forward" } ], "options": {}, "configurations": {}, "metadata": { "technologies": ["cypress"] } }, "lint": { "executor": "@nx/eslint:lint", "inputs": ["default", "{workspaceRoot}/.eslintrc.json"], "cache": true, "outputs": ["{options.outputFile}"], "options": {}, "configurations": {}, "metadata": { "technologies": ["eslint"] } } }, "name": "admin-e2e", "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "apps/admin-e2e/src", "tags": [], "implicitDependencies": ["admin"] } }, "sourceMap": { "root": ["apps/admin-e2e/project.json", "nx/core/project-json"], "projectType": ["apps/admin-e2e/project.json", "nx/core/project-json"], "targets": ["apps/admin-e2e/project.json", "nx/core/project-json"], "targets.e2e": ["apps/admin-e2e/project.json", "nx/core/target-defaults"], "targets.e2e.options": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e.cache": [ "apps/admin-e2e/project.json", "nx/core/target-defaults" ], "targets.e2e.inputs": [ "apps/admin-e2e/project.json", "nx/core/target-defaults" ], "targets.e2e.outputs": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e.configurations": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e.executor": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e.options.cwd": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e.options.command": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e.configurations.production": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e.configurations.production.command": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/app.cy.ts": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/app.cy.ts.outputs": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/app.cy.ts.inputs": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/app.cy.ts.cache": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/app.cy.ts.options": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/app.cy.ts.executor": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/app.cy.ts.options.cwd": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/app.cy.ts.options.command": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/login.cy.ts": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/login.cy.ts.outputs": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/login.cy.ts.inputs": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/login.cy.ts.cache": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/login.cy.ts.options": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/login.cy.ts.executor": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/login.cy.ts.options.cwd": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci--src/e2e/login.cy.ts.options.command": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci.executor": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci.cache": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci.inputs": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci.outputs": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e-ci.dependsOn": [ "apps/admin-e2e/cypress.config.ts", "@nx/cypress/plugin" ], "targets.e2e.dependsOn": [ "apps/admin-e2e/project.json", "nx/core/target-defaults" ], "targets.lint": ["apps/admin-e2e/project.json", "nx/core/project-json"], "targets.lint.executor": [ "apps/admin-e2e/project.json", "nx/core/project-json" ], "targets.lint.inputs": [ "apps/admin-e2e/project.json", "nx/core/target-defaults" ], "targets.lint.cache": [ "apps/admin-e2e/project.json", "nx/core/target-defaults" ], "name": ["apps/admin-e2e/project.json", "nx/core/project-json"], "$schema": ["apps/admin-e2e/project.json", "nx/core/project-json"], "sourceRoot": ["apps/admin-e2e/project.json", "nx/core/project-json"], "tags": ["apps/admin-e2e/project.json", "nx/core/project-json"], "implicitDependencies": [ "apps/admin-e2e/project.json", "nx/core/project-json" ], "implicitDependencies.admin": [ "apps/admin-e2e/project.json", "nx/core/project-json" ], "targets.lint.outputs": [ "apps/admin-e2e/project.json", "nx/core/project-json" ] } } ``` {% /project_details %} {% /tabitem %} {% /tabs %} If you configured Nx Atomizer properly, you'll see that there are tasks named `e2e`, `e2e-ci` (or similar for other test types) and a task for each test file. During local development, you'll want to continue using the base task (e.g., `e2e`, `test`) as it is more efficient on a single machine. ```shell nx e2e my-project-e2e ``` The `-ci` variant task truly shines when configured and run on CI. ## Configure automated task splitting on CI Update your CI pipeline to run the `-ci` variant task (e.g., `e2e-ci`, `test-ci`), which will automatically run all the inferred tasks for the individual test files. Distribution and stop conditions come from your [`.nx/ci-config.yaml`](/docs/reference/nx-cloud/ci-config) file: ```yaml // .nx/ci-config.yaml dte: distribute-on: 3 linux-medium-js lifecycle: stop-after: - e2e-ci ``` Here's an example of a GitHub Actions workflow: ```yaml {% meta="{17,27}" %} // .github/workflows/ci.yml name: CI # ... jobs: main: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: fetch-depth: 0 filter: tree:0 - uses: pnpm/action-setup@v6 with: version: 9 - run: pnpm dlx nx start-nx-agents - uses: actions/setup-node@v6 with: node-version: 24 cache: 'pnpm' - run: pnpm install --frozen-lockfile - uses: nrwl/nx-set-shas@v5 - run: pnpm exec nx affected -t lint test build e2e-ci ``` Learn more about configuring your [CI provider by following these detailed recipes](/docs/kb/setup-ci). --- ## Enforce Module Boundaries If you partition your code into well-defined cohesive units, even a small organization will end up with a dozen apps and dozens or hundreds of libs. If all of them can depend on each other freely, chaos will ensue, and the workspace will become unmanageable. To help with that, Nx provides powerful mechanisms to enforce architectural boundaries and ensure projects can only depend on each other according to your organization's rules. You can declaratively define constraints using project tags and enforce them automatically. {% youtube src="https://www.youtube.com/embed/q0en5vlOsWY" title="Applying Module Boundaries" /%} ## How to enforce boundaries Nx offers two complementary approaches to enforce module boundaries: **Lint rule integration** - For JavaScript/TypeScript projects, enforce boundaries on code imports using the `@nx/enforce-module-boundaries` rule. This checks TypeScript imports and `package.json` dependencies during linting. ESLint runs the rule natively, and `@nx/oxlint` bridges it into Oxlint. **Language-Agnostic Conformance** - For any project type (e.g. Java, Python, PHP, JavaScript, etc.), use the [Conformance plugin's Enforce Project Boundaries rule](/docs/enterprise/conformance). This rule checks dependencies in the Nx graph during `nx conformance:check`. Requires [Nx Enterprise plan](https://nx.dev/enterprise). Both approaches use the same tag-based constraint system described below. ## Tags Nx comes with a generic mechanism for expressing constraints on project dependencies: tags. First, use your project configuration (in `project.json` or `package.json`) to annotate your projects with `tags`. In this example, we will use three tags: `scope:client`. `scope:admin`, `scope:shared`. {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```jsonc // client/package.json { // ... more project configuration here "nx": { "tags": ["scope:client"], }, } ``` ```jsonc // admin/package.json { // ... more project configuration here "nx": { "tags": ["scope:admin"], }, } ``` ```jsonc // utils/package.json { // ... more project configuration here "nx": { "tags": ["scope:shared"], }, } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // client/project.json { // ... more project configuration here "tags": ["scope:client"], } ``` ```jsonc // admin/project.json { // ... more project configuration here "tags": ["scope:admin"], } ``` ```jsonc // utils/project.json { // ... more project configuration here "tags": ["scope:shared"], } ``` {% /tabitem %} {% /tabs %} ## Configure boundary rules Once you have tagged your projects, configure the dependency constraints based on your chosen approach: {% tabs syncKey="boundary-enforcement" %} {% tabitem label="ESLint" %} For JavaScript/TypeScript projects, configure the `@nx/enforce-module-boundaries` ESLint rule: ```shell nx add @nx/eslint-plugin @nx/devkit ``` And configure the rule in your ESLint configuration: {% tabs syncKey="eslint-config-preference" %} {% tabitem label="Flat Config" %} ```javascript // eslint.config.mjs import nx from '@nx/eslint-plugin'; export default [ ...nx.configs['flat/base'], ...nx.configs['flat/typescript'], ...nx.configs['flat/javascript'], { files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], rules: { '@nx/enforce-module-boundaries': [ 'error', { allow: [], // update depConstraints based on your tags depConstraints: [ { sourceTag: 'scope:shared', onlyDependOnLibsWithTags: ['scope:shared'], }, { sourceTag: 'scope:admin', onlyDependOnLibsWithTags: ['scope:shared', 'scope:admin'], }, { sourceTag: 'scope:client', onlyDependOnLibsWithTags: ['scope:shared', 'scope:client'], }, ], }, ], }, }, ]; ``` {% /tabitem %} {% tabitem label="Legacy (.eslintrc.json)" %} ```jsonc // .eslintrc.json { // ... more ESLint config here // @nx/enforce-module-boundaries should already exist within an "overrides" block using `"files": ["*.ts", "*.tsx", "*.js", "*.jsx",]` "@nx/enforce-module-boundaries": [ "error", { "allow": [], // update depConstraints based on your tags "depConstraints": [ { "sourceTag": "scope:shared", "onlyDependOnLibsWithTags": ["scope:shared"], }, { "sourceTag": "scope:admin", "onlyDependOnLibsWithTags": ["scope:shared", "scope:admin"], }, { "sourceTag": "scope:client", "onlyDependOnLibsWithTags": ["scope:shared", "scope:client"], }, ], }, ], // ... more ESLint config here } ``` {% /tabitem %} {% /tabs %} If you violate the constraints, you will get an error when linting: ```plaintext A project tagged with "scope:admin" can only depend on projects tagged with "scoped:shared" or "scope:admin". ``` Read more about [ESLint rule options](/docs/kb/enforce-module-boundaries). {% /tabitem %} {% tabitem label="Oxlint" %} [`@nx/oxlint`](/docs/technologies/oxlint/introduction) exposes the rule to Oxlint as a JavaScript plugin. It reads the project graph and takes the options documented for ESLint, so your constraints carry over unchanged. {% aside type="caution" title="Experimental" %} The bridge depends on the Oxlint JavaScript plugin API. Oxlint excludes that API from its semantic versioning policy, so it can change in any release. {% /aside %} Register the bridge and configure the rule in `.oxlintrc.json`: ```json { "jsPlugins": ["@nx/oxlint/boundaries-plugin"], "rules": { "@nx/enforce-module-boundaries": [ "error", { "allow": [], "depConstraints": [ { "sourceTag": "scope:shared", "onlyDependOnLibsWithTags": ["scope:shared"] }, { "sourceTag": "scope:admin", "onlyDependOnLibsWithTags": ["scope:shared", "scope:admin"] }, { "sourceTag": "scope:client", "onlyDependOnLibsWithTags": ["scope:shared", "scope:client"] } ] } ] } } ``` {% /tabitem %} {% tabitem label="Conformance" %} For any project type or to enforce boundaries on the complete dependency graph, use the Conformance plugin: ```shell nx add @nx/conformance ``` Configure rules in your `nx.json`: ```jsonc // nx.json { "conformance": { "rules": [ { "rule": "@nx/conformance/enforce-project-boundaries", "options": { "depConstraints": [ { "sourceTag": "scope:shared", "onlyDependOnProjectsWithTags": ["scope:shared"], }, { "sourceTag": "scope:admin", "onlyDependOnProjectsWithTags": ["scope:shared", "scope:admin"], }, { "sourceTag": "scope:client", "onlyDependOnProjectsWithTags": ["scope:shared", "scope:client"], }, ], }, }, ], }, } ``` Run conformance checks in CI: ```yaml - name: Enforce all conformance rules run: npx nx conformance:check ``` Learn more about [Conformance rules](/docs/enterprise/conformance). {% /tabitem %} {% /tabs %} With these constraints in place, `scope:client` projects can only depend on projects with `scope:client` or `scope:shared`. And `scope:admin` projects can only depend on projects with `scope:admin` or `scope:shared`. So `scope:client` and `scope:admin` cannot depend on each other. Projects without any tags cannot depend on any other projects. The exception to this rule is by explicitly allowing all tags (see below). ### Tag formats - `*`: allow all tags Example: projects with any tags (including untagged) can depend on any other project. ```jsonc { "sourceTag": "*", "onlyDependOnLibsWithTags": ["*"], } ``` - `string`: allow exact tags Example: projects tagged with `scope:client` can only depend on projects tagged with `scope:util`. ```jsonc { "sourceTag": "scope:client", "onlyDependOnLibsWithTags": ["scope:util"], } ``` - `regex`: allow tags matching the regular expression Example: projects tagged with `scope:client` can depend on projects with a tag matching the regular expression `/^scope.*/`. In this case, the `scope:util`, `scope:client`, etc. are all allowed tags for dependencies. ```json { "sourceTag": "scope:client", "onlyDependOnLibsWithTags": ["/^scope.*/"] } ``` - `glob`: allow tags matching the glob Example: projects with a tag starting with `scope:` can depend on projects with a tag that starts with `scope:*`. In this case `scope:a`, `scope:b`, etc are all allowed tags for dependencies. ```json { "sourceTag": "scope:*", "onlyDependOnLibsWithTags": ["scope:*"] } ``` Globbing supports only the basic use of `*`. For more complex scenarios use the `regex` above. --- ## Enhance Your AI Coding Agent AI agents are moving beyond autocomplete. They can now operate independently across projects. But most setups hit a wall: agents lack workspace context (seeing files, not architecture), generate inconsistent code, and have a hard time to interact with CI. Nx monorepos solve this by enabling cross-project reasoning and by providing the structured metadata and CI integration that agents need to work autonomously: - Deep **workspace architecture** understanding and project relationships - **Code generators** for fast, predictable scaffolding - **CI pipeline integration** to fix failures autonomously - The ability to **iterate until CI is green** without human intervention ## Setup To configure your Nx workspace for AI agents, run: ```shell npx nx configure-ai-agents ``` This sets up: - **Agent configuration files**: `CLAUDE.md`, `AGENTS.md` with workspace-specific guidelines - **Agent skills**: Domain-specific knowledge for monorepo workflows — workspace exploration, code generation, task execution, CI monitoring, and package linking. Skills teach agents _how_ to work with Nx rather than dumping data into context. - **Nx MCP server**: Provides connectivity to Nx Cloud CI pipelines, self-healing fixes, running processes, and Nx documentation — things agents can't easily reach on their own ### Creating new workspaces For creating new Nx workspaces, use `npx create-nx-workspace@latest --template=nrwl/-template` (e.g. `react-template`, `angular-template`, `typescript-template`). For existing projects, use `npx nx init`. ## What this enables ### Self-healing CI integration Nx Cloud provides AI-powered [Self-Healing CI](/docs/features/ci-features/self-healing-ci) that analyzes failed runs and proposes verified fixes. With `configure-ai-agents`, your local agent connects to this CI counterpart via skills and the Nx MCP, gaining full context about run information, failures, and suggested fixes. Your agent can autonomously iterate until CI passes: ```text Commit this work, create a PR, and monitor CI until it's green. ``` The workflow: 1. Agent pushes changes and creates PR 2. Monitors CI pipeline 3. Receives failure context from Nx Cloud and Self-Healing CI 4. Accepts proposed fix or pulls context locally and manually applies it 5. Repeats until CI is green This reduces context-switching—you review the final PR rather than intervening at each failure. ### Workspace architecture understanding Nx exposes the project graph and relevant metadata to AI agents. This helps them move faster and more precisely: - Identify all applications and libraries in the workspace - Understand project relationships and dependencies - Recognize project types and ownership via tags - Determine which projects are affected by changes - Suggest where to implement new functionality based on existing structure This architectural awareness is critical for agents operating in large monorepos where understanding project relationships determines the quality of generated code. ### Predictable, fast code generation AI-generated code is token-intensive, slow, and not guaranteed to align with patterns in other projects. Nx generators solve this by providing predictable scaffolding that agents can invoke and then adapt. Your AI agent can: 1. Find generators from [Nx plugins](/docs/plugin-registry) or custom [local workspace generators](/docs/kb/local-generators) 2. Run the generator with correct options 3. Make small adjustments based on the specific situation This approach is faster, produces consistent code across projects, and reduces hallucinations. ## Learn more - [Autonomous AI Agents at Scale](https://nx.dev/blog/ai-agents-and-continuity): Infrastructure requirements for AI agent workflows - [Why Nx and AI Work So Well Together](https://nx.dev/blog/nx-and-ai-why-they-work-together): The foundation for AI-powered development - [Nx MCP Server Reference](/docs/reference/nx-mcp): Complete tool reference and setup instructions - [Configure AI agent sandboxes for Nx](/docs/kb/nx-sandbox-unix-sockets): Allow Unix socket access for daemon and plugin communication --- ## Explore your Workspace Nx understands your workspace as a collection of projects. Each project can be explored to view the different tasks which can be run. The projects in the workspace have dependencies between them and form a graph known as the **Project Graph**. Nx uses this project graph in many ways to make informed decisions such as which tasks to run, when the results of a task can be restored from cache, and more. In addition to the project graph, Nx also runs your tasks as a **Task Graph**. This is a separate graph of tasks and their dependencies which is based on the project graph and determines the way in which the tasks are executed. Nx allows you to interactively explore your workspace through a UI which shows the information above. Using this tool is _vital_ to understanding both your workspace as well as how Nx behaves. Teach you to use this tool to explore projects, the project graph, and the task graphs for your workspace. ## Explore projects in your workspace Projects in Nx are the different parts of the monorepo which can have tasks run for them. The best way to see what projects are in your workspace is to view the [project graph](#explore-the-project-graph) which will be covered in the next section. Another way is to look at the **Projects** pane in [Nx Console](/docs/getting-started/editor-setup) or run `nx show projects` to show a list of projects in your terminal. You can see more details about a specific project in Nx Console or by running `nx show project --web`. Both methods will show something like the example below: {% project_details %} ```json { "project": { "name": "myreactapp", "type": "app", "data": { "root": "apps/myreactapp", "targets": { "build": { "options": { "cwd": "apps/myreactapp", "command": "vite build" }, "cache": true, "dependsOn": ["^build"], "inputs": [ "production", "^production", { "externalDependencies": ["vite"] } ], "outputs": ["{workspaceRoot}/dist/apps/myreactapp"], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["vite"] } }, "serve": { "options": { "cwd": "apps/myreactapp", "command": "vite serve", "continuous": true }, "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["vite"] } }, "preview": { "options": { "cwd": "apps/myreactapp", "command": "vite preview" }, "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["vite"] } }, "serve-static": { "executor": "@nx/web:file-server", "options": { "buildTarget": "build", "continuous": true }, "configurations": {} }, "test": { "options": { "cwd": "apps/myreactapp", "command": "vitest run" }, "cache": true, "inputs": [ "default", "^production", { "externalDependencies": ["vitest"] } ], "outputs": ["{workspaceRoot}/coverage/apps/myreactapp"], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["vite"] } }, "lint": { "cache": true, "options": { "cwd": "apps/myreactapp", "command": "eslint ." }, "inputs": [ "default", "{workspaceRoot}/.eslintrc.json", "{workspaceRoot}/apps/myreactapp/.eslintrc.json", "{workspaceRoot}/tools/eslint-rules/**/*", { "externalDependencies": ["eslint"] } ], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["eslint"] } } }, "name": "myreactapp", "$schema": "../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "apps/myreactapp/src", "projectType": "application", "tags": [], "implicitDependencies": [], "metadata": { "technologies": ["react"] } } }, "sourceMap": { "root": ["apps/myreactapp/project.json", "nx/core/project-json"], "targets": ["apps/myreactapp/project.json", "nx/core/project-json"], "targets.build": ["apps/myreactapp/vite.config.ts", "@nx/vite/plugin"], "targets.build.command": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.build.options": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.build.cache": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.build.dependsOn": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.build.inputs": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.build.outputs": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.build.options.cwd": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.serve": ["apps/myreactapp/vite.config.ts", "@nx/vite/plugin"], "targets.serve.command": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.serve.options": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.serve.options.cwd": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.preview": ["apps/myreactapp/vite.config.ts", "@nx/vite/plugin"], "targets.preview.command": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.preview.options": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.preview.options.cwd": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.serve-static": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.serve-static.executor": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.serve-static.options": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.serve-static.options.buildTarget": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.test": ["apps/myreactapp/vite.config.ts", "@nx/vite/plugin"], "targets.test.command": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.test.options": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.test.cache": ["apps/myreactapp/vite.config.ts", "@nx/vite/plugin"], "targets.test.inputs": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.test.outputs": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.test.options.cwd": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.lint": ["apps/myreactapp/project.json", "@nx/eslint/plugin"], "targets.lint.command": [ "apps/myreactapp/project.json", "@nx/eslint/plugin" ], "targets.lint.cache": ["apps/myreactapp/project.json", "@nx/eslint/plugin"], "targets.lint.options": [ "apps/myreactapp/project.json", "@nx/eslint/plugin" ], "targets.lint.inputs": [ "apps/myreactapp/project.json", "@nx/eslint/plugin" ], "targets.lint.options.cwd": [ "apps/myreactapp/project.json", "@nx/eslint/plugin" ], "name": ["apps/myreactapp/project.json", "nx/core/project-json"], "$schema": ["apps/myreactapp/project.json", "nx/core/project-json"], "sourceRoot": ["apps/myreactapp/project.json", "nx/core/project-json"], "projectType": ["apps/myreactapp/project.json", "nx/core/project-json"], "tags": ["apps/myreactapp/project.json", "nx/core/project-json"] } } ``` {% /project_details %} The view shows a list of targets which can be [run by Nx](/docs/features/run-tasks). Each target has different options which determine how Nx runs the task. ## Explore the project graph Nx understands the projects in your workspace as a graph and uses this understanding to behave intelligently. Exploring this graph visually is vital to understanding how your code is structured and how Nx behaves. It always stays up to date without having to actively maintain a document as it is calculated by analyzing your source code. ### Launching the project graph To launch the project graph visualization for your workspace, use [Nx Console](/docs/getting-started/editor-setup) or run: ```shell npx nx graph ``` This will open a browser window with an interactive view of the project graph of your workspace. ### Focusing on valuable projects Viewing the entire graph can be unmanageable even for smaller repositories, so there are several ways to narrow the focus of the visualization down to the most useful part of the graph at the moment. 1. Focus on a specific project and then use the proximity and group by folder controls in the sidebar to modify the graph around that project. You can also start the graph with a project focused by running `nx graph --focus `. 2. Use the search bar to find all projects with names that contain a certain string. 3. Manually hide or show projects in the sidebar. Once the graph is displayed, you can explore deeper by clicking on nodes and edges in the graph. Click on a node to show a tooltip which also has a link to view more details about the project. You can trace the dependency chain between two projects by choosing a **Start** and **End** point in the project tooltips. Click on any dependency line to find which file(s) created the dependency. Composite nodes represent a set of projects in the same folder and can be expanded in place to show all the individual projects and their dependencies. You can also "focus" a composite node to render a graph of just the projects inside that node. Composite nodes are essential to navigate a graph of even a moderate size. Try playing around with a [fully interactive graph on a sample repo](https://nrwl-nx-examples-dep-graph.netlify.app/?focus=cart) or look at the more limited example below: **Project View:** {% graph height="450px" title="Project View" %} ```json { "composite": false, "projects": [ { "name": "shared-product-state", "type": "lib", "data": { "root": "shared/product-state", "tags": ["scope:shared", "type:state"] } }, { "name": "shared-product-types", "type": "lib", "data": { "root": "shared/product-types", "tags": ["type:types", "scope:shared"] } }, { "name": "shared-product-data", "type": "lib", "data": { "root": "shared/product-data", "tags": ["type:data", "scope:shared"] } }, { "name": "cart-cart-page", "type": "lib", "data": { "root": "cart/cart-page", "tags": ["scope:cart", "type:feature"] } }, { "name": "shared-styles", "type": "lib", "data": { "root": "shared/styles", "tags": ["scope:shared", "type:styles"] } }, { "name": "e2e-cart", "type": "e2e", "data": { "root": "e2e/cart", "tags": ["scope:cart", "type:e2e"] } }, { "name": "cart", "type": "app", "data": { "root": "cart", "tags": ["type:app", "scope:cart"] } } ], "dependencies": { "shared-product-state": [ { "source": "shared-product-state", "target": "shared-product-data", "type": "static" }, { "source": "shared-product-state", "target": "shared-product-types", "type": "static" } ], "shared-product-types": [], "shared-product-data": [ { "source": "shared-product-data", "target": "shared-product-types", "type": "static" } ], "shared-e2e-utils": [], "cart-cart-page": [ { "source": "cart-cart-page", "target": "shared-product-state", "type": "static" } ], "shared-styles": [], "e2e-cart": [ { "source": "e2e-cart", "target": "cart", "type": "implicit" } ], "cart": [ { "source": "cart", "target": "shared-styles", "type": "implicit" }, { "source": "cart", "target": "cart-cart-page", "type": "static" } ] }, "workspaceLayout": { "appsDir": "apps", "libsDir": "libs" }, "affectedProjectIds": [], "focus": null, "groupByFolder": false, "exclude": [], "enableTooltips": false } ``` {% /graph %} **Composite View:** {% graph height="450px" title="Composite View" %} ```json { "composite": true, "projects": [ { "name": "shared-product-state", "type": "lib", "data": { "root": "shared/product-state", "tags": ["scope:shared", "type:state"] } }, { "name": "shared-product-types", "type": "lib", "data": { "root": "shared/product-types", "tags": ["type:types", "scope:shared"] } }, { "name": "shared-product-data", "type": "lib", "data": { "root": "shared/product-data", "tags": ["type:data", "scope:shared"] } }, { "name": "cart-cart-page", "type": "lib", "data": { "root": "cart/cart-page", "tags": ["scope:cart", "type:feature"] } }, { "name": "shared-styles", "type": "lib", "data": { "root": "shared/styles", "tags": ["scope:shared", "type:styles"] } }, { "name": "e2e-cart", "type": "e2e", "data": { "root": "e2e/cart", "tags": ["scope:cart", "type:e2e"] } }, { "name": "cart", "type": "app", "data": { "root": "cart/cart", "tags": ["type:app", "scope:cart"] } } ], "dependencies": { "shared-product-state": [ { "source": "shared-product-state", "target": "shared-product-data", "type": "static" }, { "source": "shared-product-state", "target": "shared-product-types", "type": "static" } ], "shared-product-types": [], "shared-product-data": [ { "source": "shared-product-data", "target": "shared-product-types", "type": "static" } ], "shared-e2e-utils": [], "cart-cart-page": [ { "source": "cart-cart-page", "target": "shared-product-state", "type": "static" } ], "shared-styles": [], "e2e-cart": [ { "source": "e2e-cart", "target": "cart", "type": "implicit" } ], "cart": [ { "source": "cart", "target": "shared-styles", "type": "implicit" }, { "source": "cart", "target": "cart-cart-page", "type": "static" } ] }, "workspaceLayout": { "appsDir": "apps", "libsDir": "libs" }, "affectedProjectIds": [], "focus": null, "groupByFolder": false, "exclude": [], "enableTooltips": false } ``` {% /graph %} ### Export project graph to JSON If you prefer to analyze the underlying data of the project graph with a script or some other tool, you can run: ```shell nx graph --file=output.json ``` This will give you all the information that is used to create the project graph visualization. ### Export the project graph as an image There is a floating action button in the bottom right of the project graph view which will save the graph as a `.png` file. Sharing this image with other developers is a great way to express how a project fits into the workspace. Some moments which you may want to share these images are: - When providing a high-level overview of the workspace - When introducing new project(s) into the workspace - When changing how project(s) are related - To share which other projects are directly affected by changes you are making ## Explore the task graph Nx uses the project graph of your workspace to determine the order in which to [run tasks](/docs/features/run-tasks). Pass the `--graph` flag to view the **task graph** which is executed by Nx when running a command. ```shell nx build myreactapp --graph # View the graph for building myreactapp nx run-many --targets build --graph # View the graph for building all projects nx affected --targets build --graph # View the graph for building the affected projects ``` Click on the nodes of this graph to see more information about the task such as: - Which executor was used to run the command - Which [inputs](/docs/kb/configure-inputs) are used to calculate the computation hash. - A link to see more details about the project which the task belongs to Dependencies in this graph mean that Nx will need to wait for all task dependencies to complete successfully before running the task. --- ## Generate Code {% youtube src="https://youtu.be/hSM6MgWOYr8" title="Generate Code" /%} Code generators are like automation scripts that help you scaffold projects, enforce best practices, and automate repetitive tasks. Essentially, they are TypeScript functions that accept parameters and help boost your productivity by: - Allowing you to **scaffold new projects** or **augment existing projects** with new features, like [adding Storybook support](/docs/technologies/test-tools/storybook/introduction#generating-storybook-configuration) - **Automating repetitive tasks** in your development workflow - Ensuring your **code is consistent and follows best practices** ## Invoke generators Generators come as part of [Nx plugins](/docs/concepts/nx-plugins) and can be invoked using the `nx generate` command (or `nx g`) using the following syntax: `nx g : [options]`. Here's an example of generating a React library: ```shell nx g @nx/react:lib packages/mylib ``` You can also specify just the generator name and Nx will prompt you to pick between the installed plugins that provide a generator with that name. ```shell nx g lib packages/mylib ``` When running this command, you could be prompted to choose between the `@nx/react` and `@nx/js` plugins that each provide a library generator. To see a list of available generators in a given plugin, run `nx list `. As an example, to list all generators in the @nx/react plugin: ```shell nx list @nx/react ``` ### Use Nx Console If you prefer a visual interface, then [Nx Console](/docs/getting-started/editor-setup) is an excellent alternative. It provides a way to visually find and run generators: ![Using Nx Console to run generators](../../../assets/nx-console/nx-console-gen-code.avif) Nx Console is an IDE extension that can be [installed here](/docs/getting-started/editor-setup). ## Build your own generator You can also customize existing generators by overwriting their behavior or create completely new ones. This is a powerful mechanism as it allows you to: - **automate** your organization's specific processes and workflows - **standardize** how and where projects are created in your workspace to make sure they reflect your organization's best practices and coding standards - **ensure** that your codebase follows your organization's best practices and style guides At their core, generators are just functions with a specific signature and input options that get invoked by Nx. Something like the following: ```typescript // generator.ts import { Tree, formatFiles, installPackagesTask } from '@nx/devkit'; export default async function (tree: Tree, schema: any) { // Your implementation here // ... await formatFiles(tree); return () => { installPackagesTask(tree); }; } ``` To help build generators, Nx provides the `@nx/devkit` package containing utilities and helpers. Learn more about creating your own generators on [our docs page](/docs/kb/local-generators) or watch the video below: {% youtube src="https://www.youtube.com/embed/myqfGDWC2go" title="Scaffold new Pkgs in a PNPM Workspaces Monorepo" caption="Demonstrates how to use Nx generators in a PNPM workspace to automate the creation of libraries" /%} --- ## Manage Releases Once you have leveraged Nx powerful code generation and task running capabilities to build your libraries and applications, you will want to share them with your users. {% linkcard title="Free Course: Versioning and Releasing NPM packages with Nx" href="https://www.epicweb.dev/tutorials/versioning-and-releasing-npm-packages-with-nx" /%} Nx provides a set of tools to help you manage your releases called `nx release`. > We recommend always starting with --dry-run, because publishing is difficult to undo ```shell nx release --dry-run ``` ## What makes up a release? A release can be thought about in three main phases: 1. **Versioning** - The process of determining the next version of your projects, and updating any projects that depend on them to use the new version. 2. **Changelog** - The process of deriving a changelog from your commit messages or [version plan](/docs/guides/nx-release/file-based-versioning-version-plans) files, which can be used to communicate the changes to your users. 3. **Publishing** - The process of publishing your projects to a registry, such as npm for TypeScript/JavaScript libraries, crates.io for Rust, or Docker registries for container images. ## Running releases The `nx release` command is used to run the release process from end to end. It is a wrapper around the three main phases of a release to provide maximum convenience and ease of use. By default, when you run `nx release` it will prompt you for a version keyword (e.g. major, minor, patch) or a custom version number. The release command will then run the three phases of the release process in order: versioning, changelog generation, and publishing. When trying it out for the first time, you need to pass the `--first-release` flag since there is no previous release to compare against for changelog purposes. It is strongly recommended to use the `--dry-run` flag to see what will be published in the first release without actually pushing anything to the registry. ```shell nx release --first-release --dry-run ``` {% aside type="tip" title="Semantic Versioning" %} By default, the version follows semantic versioning (semver) rules. To disable this behavior, set `release.releaseTag.requireSemver` to `false` in your `nx.json` file. This allows you to use custom versioning schemes. {% /aside %} ## Set up your workspace Follow our guides to set up Nx Release for your workspace. {% cardgrid %} {% linkcard title="TypeScript/JavaScript to NPM" description="Publish TypeScript and JavaScript packages to NPM or private registries with semantic versioning." href="/docs/kb/release-npm-packages" /%} {% linkcard title="Docker Images" description="Version and publish Docker images with calendar-based versioning for continuous deployment." href="/docs/kb/release-docker-images" /%} {% linkcard title="Rust Crates" description="Publish Rust packages to crates.io with cargo integration." href="/docs/kb/publish-rust-crates" /%} {% /cardgrid %} ## Basic configuration Configure Nx Release in your `nx.json` file: ```jsonc // nx.json { "release": { "projects": ["packages/*"], }, } ``` The nx release command is customizable. You can customize the versioning, changelog, and publishing phases of the release process independently through a mixture of configuration and CLI arguments. See the [configuration reference](/docs/reference/nx-json#release) for all available options. ## Using the programmatic API for Nx release A powerful feature of Nx Release is the fact that it is designed to be used via a Node.js programmatic API in addition to the `nx release` CLI. Releases are a hugely complex and nuanced process, filled with many special cases and idiosyncratic preferences, and it is impossible for a CLI to be able to support all of them out of the box. By having a first-class programmatic API, you can go beyond the CLI and create custom release workflows that are highly dynamic and tailored to your specific needs. See our dedicated guide on the [programmatic API](/docs/guides/nx-release/programmatic-api) to learn more and see some example release scripts. ## Learn more ### Configuration & customization - **[Version Projects Independently](/docs/guides/nx-release/release-projects-independently)** - Version projects independently or together - **[Release Groups](/docs/guides/nx-release/release-groups)** - Organize projects into release groups with specific configuration for each group - **[Conventional Commits](/docs/guides/nx-release/automatically-version-with-conventional-commits)** - Automate versioning based on commit messages - **[Custom Registries](/docs/guides/nx-release/configure-custom-registries)** - Publish to private or alternative registries - **[CI/CD Integration](/docs/guides/nx-release/publish-in-ci-cd)** - Automate releases in your pipeline - **[Changelog Customization](/docs/guides/nx-release/configure-changelog-format)** - Control changelog generation and formatting - **[Custom Commit Types](/docs/guides/nx-release/customize-conventional-commit-types)** - Define custom conventional commit types - **[Version Prefixes](/docs/guides/nx-release/configuration-version-prefix)** - Configure version prefix patterns ### Workflows - **[Automate with GitHub Actions](/docs/kb/automate-github-releases)** - Set up automated releases in GitHub workflows - **[Release Projects Independently](/docs/guides/nx-release/release-projects-independently)** - Manage independent versioning for projects - **[Use Conventional Commits](/docs/guides/nx-release/automatically-version-with-conventional-commits)** - Enable automatic versioning from commits - **[Build Before Versioning](/docs/guides/nx-release/build-before-versioning)** - Run builds before version updates ### References - **[`nx.json` configuration options](/docs/reference/nx-json#release)** - All available options for configuring `nx release` - **[`nx release` command](/docs/reference/nx-commands#nx-release)** - Run versioning, changelog generation, and publishing - **[`nx release version` command](/docs/reference/nx-commands#nx-release-version)** - Run only the versioning step - **[`nx release changelog` command](/docs/reference/nx-commands#nx-release-changelog)** - Run only the changelog generation step - **[`nx release publish` command](/docs/reference/nx-commands#nx-release-publish)** - Run only the publishing step - **[`nx release plan` command](/docs/reference/nx-commands#nx-release-plan)** - Create a version plan file for file-based versioning --- ## Multi-Language Support Nx works with any language or toolchain. The task runner, caching, and [affected](/docs/features/ci-features/affected) commands all operate on the project graph. Plugins build that graph by reading existing configuration files, so adding support for a new language or tool means teaching Nx to read those existing files. ## Basic configuration Nx doesn't need a plugin to run tasks for another language. Any directory with a [`project.json`](/docs/reference/project-configuration) file is an Nx project, and its targets run any command with the same caching and orchestration as a JS task: {% tabs %} {% tabitem label="Python + uv" %} ```jsonc // packages/py-api/project.json { "name": "py-api", "targets": { "test": { "command": "uv run pytest", "options": { "cwd": "packages/py-api" }, "cache": true, "inputs": ["{projectRoot}/**/*.py", "{workspaceRoot}/uv.lock"], }, }, } ``` {% /tabitem %} {% tabitem label="Rust + Cargo" %} ```jsonc // crates/rust-parser/project.json { "name": "rust-parser", "targets": { "build": { "command": "cargo build --release", "options": { "cwd": "crates/rust-parser" }, "cache": true, "inputs": ["{projectRoot}/src/**/*.rs", "{projectRoot}/Cargo.toml"], "outputs": ["{workspaceRoot}/target/release"], }, }, } ``` {% /tabitem %} {% tabitem label="Java + Gradle" %} ```jsonc // apps/java-app/project.json { "name": "java-app", "targets": { "test": { "command": "./gradlew :java-app:test", "cache": true, "inputs": ["{projectRoot}/src/**/*.java", "{projectRoot}/build.gradle"], "outputs": ["{projectRoot}/build/test-results"], }, }, } ``` {% /tabitem %} {% tabitem label=".NET" %} ```jsonc // apps/dotnet-app/project.json { "name": "dotnet-app", "targets": { "test": { "command": "dotnet test", "options": { "cwd": "apps/dotnet-app" }, "cache": true, "inputs": ["{projectRoot}/**/*.cs", "{projectRoot}/DotnetApp.csproj"], }, }, } ``` {% /tabitem %} {% /tabs %} ## Automatically configure projects and tasks Plugins remove the need to write that configuration by hand. A language plugin declares which files mark a project, usually the toolchain's manifest: `pyproject.toml` for Python, `go.mod` for Go, `Cargo.toml` for Rust, `build.gradle` for Gradle. For every match, the plugin creates a project and its tasks, with caching, inputs, and outputs configured once in the plugin instead of per project. This is the same [inferred tasks](/docs/concepts/mental-model#inferred-tasks) mechanism the JavaScript plugins use to read `vite.config.ts` or `jest.config.ts`. Enabling a language plugin is one entry in `nx.json`: ```jsonc // nx.json { "plugins": ["@nx/gradle", "@nx/dotnet"], } ``` With these plugins enabled, the `java-app` and `dotnet-app` projects above no longer need a `project.json`. Their tasks are inferred from `build.gradle` and the `.csproj` file. From there, Nx runs each toolchain's own commands, and cache hits restore outputs no matter what language produced them: ```shell {% meta="prompt=true" %} $ nx test py-api > nx run py-api:test 12 passed in 1.2s $ nx build rust-parser > nx run rust-parser:build Finished `release` profile [optimized] target(s) in 4.1s $ nx test java-app > nx run java-app:test BUILD SUCCESSFUL in 6s $ nx test dotnet-app > nx run dotnet-app:test Passed! - Failed: 0, Passed: 24, Total: 24 ``` Run `nx show project ` to see everything a plugin inferred for a project. ## Dependencies come from your toolchain Plugins also tell Nx how projects relate by reading the dependency information your toolchain already wrote down: {% tabs %} {% tabitem label="Python + uv" %} ```toml {% meta="{4,6-7}" %} # packages/py-api/pyproject.toml [project] name = "py-api" dependencies = ["shared-utils"] [tool.uv.sources] shared-utils = { workspace = true } ``` {% /tabitem %} {% tabitem label="Rust + Cargo" %} ```toml {% meta="{5-6}" %} # crates/rust-parser/Cargo.toml [package] name = "rust-parser" [dependencies] shared-utils = { path = "../shared-utils" } ``` {% /tabitem %} {% tabitem label="Java + Gradle" %} ```groovy {% meta="{3}" %} // apps/java-app/build.gradle dependencies { implementation project(':shared-utils') } ``` {% /tabitem %} {% tabitem label=".NET" %} ```xml {% meta="{4}" %} ``` {% /tabitem %} {% /tabs %} Manifests aren't the only source. The built-in JavaScript and TypeScript support parses source files and creates dependencies from `import` statements, in addition to `package.json` dependencies, and a plugin can do the same for its own language. These edges are what make `nx affected`, task ordering, and the [graph visualization](/docs/features/explore-graph) accurate across languages, and they connect cross-language boundaries too, such as a deploy task that depends on both a JS frontend and a Java backend. ## Use an existing plugin Check what already covers your toolchain before writing a plugin: - [Gradle](/docs/technologies/java/gradle/introduction) - [Maven](/docs/technologies/java/maven/introduction) - [.NET](/docs/technologies/dotnet/introduction) - [@nxlv/python](https://github.com/lucasvieirasilva/nx-plugins/tree/main/packages/nx-python) (Community) - [@nx-go/nx-go](https://github.com/nx-go/nx-go) (Community) - [@monodon/rust](https://github.com/cammisuli/monodon/tree/main/packages/rust) (Community) Browse the [plugin registry](/docs/plugin-registry) for the full list. ## Build your own plugin If your toolchain isn't covered, you can add your own support with a plugin. Follow [add language support to Nx](/docs/kb/add-language-support) for a complete walkthrough that uses Python with uv and adapts to any toolchain. --- ## Run Tasks {% youtube src="https://youtu.be/aEdfYiA5U34" title="Run tasks with Nx" /%} Whether you have one project or hundreds in a monorepo, Nx provides a task runner that allows you to: - easily **run multiple targets** for multiple projects **in parallel** - define **task pipelines** to run tasks in the correct order - only run tasks for **projects affected by a given change** - **speed up task execution** with [caching](/docs/features/cache-task-results) ## Define tasks Nx tasks can be created from existing `package.json` scripts, [inferred from tooling configuration files](/docs/concepts/mental-model#inferred-tasks), or defined in a `project.json` file. Nx combines these three sources to determine the tasks for a particular project. Whichever source a project uses, Nx reads it as it stands, so a workspace can run through Nx without adding task configuration. {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```json // libs/mylib/package.json { "name": "mylib", "scripts": { "build": "tsc -p tsconfig.lib.json", "test": "jest" } } ``` {% /tabitem %} {% tabitem label="project.json" %} ```json // libs/mylib/project.json { "root": "libs/mylib", "targets": { "build": { "command": "tsc -p tsconfig.lib.json" }, "test": { "executor": "@nx/jest:jest", "options": {/* ... */} } } } ``` {% /tabitem %} {% tabitem label="Inferred by Nx Plugins" %} [Nx plugins](/docs/concepts/nx-plugins) can detect your tooling configuration files (e.g. `vite.config.ts` or `.eslintrc.json`) and automatically configure runnable tasks including [Nx cache](/docs/features/cache-task-results). For example, the `@nx/jest` plugin will automatically create a `test` task for a project that uses Jest. The names can be configured in the `nx.json` file: ```json // nx.json { ... "plugins": [ { "plugin": "@nx/vite/plugin", "options": { "buildTargetName": "build", "testTargetName": "test", "serveTargetName": "serve", "previewTargetName": "preview", "serveStaticTargetName": "serve-static" } }, { "plugin": "@nx/eslint/plugin", "options": { "targetName": "lint" } }, { "plugin": "@nx/jest/plugin", "options": { "targetName": "test" } } ], ... } ``` Learn more about [inferred tasks here](/docs/concepts/mental-model#inferred-tasks). {% /tabitem %} {% /tabs %} The [project configuration docs](/docs/reference/project-configuration) has the details for all the available configuration options. ## Run tasks Nx uses the following syntax: ![Syntax for Running Tasks in Nx](../../../assets/features/run-target-syntax.svg) {% aside type="tip" title="Terminal UI" %} In Nx 21, task output is displayed in an [interactive terminal UI](/docs/kb/terminal-ui) that allows you to actively choose which task output to display, search through the list of tasks and display multiple tasks side by side. {% /aside %} ### Run a single task To run the `test` task for the `header` project run this command: ```shell npx nx test header ``` ### Run tasks for multiple projects You can use the `run-many` command to run a task for multiple projects. Here are a couple of examples. Run the `build` task for all projects in the repo: ```shell npx nx run-many -t build ``` Run the `build`, `lint` and `test` task for all projects in the repo: ```shell npx nx run-many -t build lint test ``` Run the `build`, `lint`, and `test` tasks only on the `header` and `footer` projects: ```shell npx nx run-many -t build lint test -p header footer ``` Nx parallelizes these tasks, ensuring they **run in the correct order based on their dependencies** and [task pipeline configuration](/docs/concepts/task-pipeline-configuration). You can also [control how many tasks run in parallel at once](/docs/kb/run-tasks-in-parallel). Learn more about the [run-many](/docs/reference/nx-commands#nx-run-many) command. ### Run tasks on projects affected by a PR You can also run a command for all the projects affected by your PR like this: ```shell npx nx affected -t test ``` Learn more about the [affected command here](/docs/features/ci-features/affected). ## Defining a task pipeline It is pretty common to have dependencies between tasks, requiring one task to be run before another. For example, you might want to run the `build` target on the `header` project before running the `build` target on the `app` project. Nx can automatically detect the dependencies between projects (see [project graph](/docs/features/explore-graph)). {% graph height="450px" %} ```json { "projects": [ { "name": "myreactapp", "type": "app", "data": { "tags": [] } }, { "name": "shared-ui", "type": "lib", "data": { "tags": [] } }, { "name": "feat-products", "type": "lib", "data": { "tags": [] } } ], "dependencies": { "myreactapp": [ { "source": "myreactapp", "target": "feat-products", "type": "static" } ], "shared-ui": [], "feat-products": [ { "source": "feat-products", "target": "shared-ui", "type": "static" } ] }, "workspaceLayout": { "appsDir": "", "libsDir": "" }, "affectedProjectIds": [], "focus": null, "groupByFolder": false } ``` {% /graph %} However, you need to specify for which targets this ordering is important. In the following example we are telling Nx that before running the `build` target it needs to run the `build` target on all the projects the current project depends on: ```json // nx.json { ... "targetDefaults": { "build": { "dependsOn": ["^build"] } } } ``` This means that if we run `nx build myreactapp`, Nx will first execute `build` on `shared-ui` and `feat-products` before running `build` on `myreactapp`. You can define these task dependencies globally for your workspace in `nx.json` or individually in each project's `project.json` file. Learn more about: - [What a task pipeline is all about](/docs/concepts/task-pipeline-configuration) - [How to configure a task pipeline](/docs/kb/defining-task-pipeline) ## Reduce repetitive configuration Learn more about leveraging `targetDefaults` to reduce repetitive configuration in the [dedicated recipe](/docs/kb/reduce-repetitive-configuration). ## Run root-level tasks Sometimes, you need tasks that apply to the entire codebase rather than a single project. To still benefit from caching, you can run these tasks through the "Nx pipeline". Define them in the root-level `package.json` or `project.json` as follows: {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```json // package.json { "name": "myorg", "scripts": { "docs": "node ./generateDocsSite.js" }, "nx": {} } ``` > Note the `nx: {}` property on the `package.json`. This is necessary to inform Nx about this root-level project. The property can also be expanded to specify cache inputs and outputs. If you want Nx to cache the task, but prefer to use npm (or pnpm/yarn) to run the script (i.e. `npm run docs`) you can use the [nx exec](/docs/reference/nx-commands#nx-exec) command: ```json // package.json { "name": "myorg", "scripts": { "docs": "nx exec -- node ./generateDocsSite.js" }, "nx": {} } ``` {% /tabitem %} {% tabitem label="project.json" %} ```json // project.json { "name": "myorg", ... "targets": { "docs": { "command": "node ./generateDocsSite.js" } } } ``` {% /tabitem %} {% /tabs %} To invoke the task, use: ```shell npx nx docs ``` Learn more about root-level tasks on [our dedicated recipe page](/docs/kb/root-level-scripts). # Platform Features --- ## Platform Features {% sidebar_group_cards group="Platform Features" /%} --- ## Code Organization {% sidebar_group_cards group="Platform Features/Code Organization" /%} --- ## Enforce Module Boundaries {% sidebar_group_cards group="Platform Features/Code Organization/Enforce Module Boundaries" /%} --- ## Enterprise {% sidebar_group_cards group="Platform Features/Enterprise" /%} --- ## Conformance Reference {% sidebar_group_cards group="Platform Features/Enterprise/Conformance Reference" /%} --- ## Single Tenant {% sidebar_group_cards group="Platform Features/Enterprise/Single Tenant" /%} --- ## Maintenance {% sidebar_group_cards group="Platform Features/Maintenance" /%} --- ## Orchestration & CI {% sidebar_group_cards group="Platform Features/Orchestration & CI" /%} --- ## Release & Publishing {% sidebar_group_cards group="Platform Features/Release & Publishing" /%} # Guides --- ## Guides {% index_page_cards path="guides" /%} --- ## Prepare Applications for Deployment via CI {% aside type="note" title="Using TS Solution Setup?" %} If your workspace uses TS project references (the default in Nx 20+), use the [prune workflow](/docs/kb/deploying-node-projects) instead. The `generatePackageJson` approach below applies to workspaces without TS Solution Setup. {% /aside %} A common approach to deploying applications is via docker containers. Some applications can be built into bundles that are environment agnostic, while others depend on OS-specific packages being installed. For these situations, having just bundled code is not enough, we also need to have `package.json`. Nx supports the generation of the project's `package.json` by identifying all the project's dependencies. The generated `package.json` is created next to the built artifacts (usually at `dist/apps/name-of-the-app`). Additionally, we should generate pruned lock file according to the generated `package.json`. This makes the installation in the container significantly faster as we only need to install a subset of the packages. Nx offers two varieties of Webpack plugin which can be used to generate `package.json`. ## Basic plugin configuration `@nx/webpack/plugin` plugin is compatible with a conventional webpack configuration setup which offers a smooth integration with the Webpack CLI. It is configured in the `plugins` array in `nx.json`. ```json // nx.json { "plugins": [ { "plugin": "@nx/webpack/plugin", "options": { "buildTargetName": "build", "serveTargetName": "serve", "serveStaticTargetName": "serve-static", "previewStaticTargetName": "preview" } } ] } ``` Where `build`, `serve`, `serve-static` and `preview` in conjunction with your `webpack.config.js` are the names of the targets that are used to _build_, _serve_, and _preview_ the application respectively. ### NxAppWebpackPlugin The [`NxAppWebpackPlugin`](/docs/kb/webpack-plugins#nxappwebpackplugin) plugin takes a `main` entry file and produces a bundle in the output directory as defined in `output.path`. You can also pass the `index` option if it is a web app, which will handle outputting scripts and stylesheets in the output file. To generate a `package.json` we would declare it in the plugin options. ```js // apps/acme/app/webpack.config.js const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin'); const { join } = require('path'); module.exports = { output: { path: join(__dirname, '../../dist/apps/acme'), }, devServer: { port: 4200, }, plugins: [ new NxAppWebpackPlugin({ tsConfig: './tsconfig.app.json', compiler: 'swc', main: './src/main.tsx', index: '.src/index.html', styles: ['./src/styles.css'], generatePackageJson: true, }), ], }; ``` ## Programmatic usage If you are using a custom setup that does not support the creation of a `package.json` or a lock file, you can still use Nx to generate them with functions exported from `@nx/js`: {% tabs syncKey="nx-version" %} {% tabitem label="Nx >= 23.2" %} Use the `createPackageJson` and `generatePrunedDeployOutput` functions: {% tabs %} {% tabitem label="Custom script" %} If you need to use a custom script, to build your application it should look similar to the following: ```javascript // scripts/create-package-json.js const { createProjectGraphAsync, detectPackageManager, writeJsonFile, } = require('@nx/devkit'); const { createPackageJson, generatePrunedDeployOutput } = require('@nx/js'); async function main() { const outputDir = 'dist'; // You can replace this with the output directory you want to use // Detect the package manager you are using (npm, yarn, pnpm, bun) const pm = detectPackageManager(); const projectGraph = await createProjectGraphAsync(); const projectName = ''; const packageJson = createPackageJson(projectName, projectGraph, { isProduction: true, // Used to strip any non-prod dependencies }); // Writes the pruned lock file and, for pnpm, the install-time artifacts it // needs. Mutates "packageJson", so write the manifest after it. generatePrunedDeployOutput( packageJson, projectGraph, projectGraph.nodes[projectName].data.root, { outputDirectory: outputDir, packageManager: pm } ); writeJsonFile(`${outputDir}/package.json`, packageJson); //... Any additional steps you want to run } main(); ``` Then to run the script, update your `package.json` to include the following: ```json // package.json { "scripts": { "copy-package-json": "node scripts/create-package-json.js", "custom-build": "nx build && npm run copy-package-json" } } ``` Now, you can run `npm run custom-build` to build your application and generate the `package.json` and lock file. You can replace _npm_ with _yarn_, _pnpm_, or _bun_ if you are using those package managers. {% /tabitem %} {% tabitem label="Custom executor" %} ```typescript // Custom executor import { Schema } from './schema'; import { createPackageJson, generatePrunedDeployOutput } from '@nx/js'; import { detectPackageManager, ExecutorContext, writeJsonFile, } from '@nx/devkit'; export default async function buildExecutor( options: Schema, context: ExecutorContext ) { // ...your executor code const packageManager = detectPackageManager(); const packageJson = createPackageJson( context.projectName, context.projectGraph, { root: context.root, isProduction: true, // We want to strip any non-prod dependencies } ); // do any additional manipulations to "package.json" here // Writes the pruned lock file and, for pnpm, the install-time artifacts it // needs. Mutates "packageJson", so write the manifest after it. generatePrunedDeployOutput( packageJson, context.projectGraph, context.projectGraph.nodes[context.projectName].data.root, { outputDirectory: options.outputPath, packageManager, workspaceRoot: context.root, } ); writeJsonFile(`${options.outputPath}/package.json`, packageJson); // any subsequent executor code } ``` {% /tabitem %} {% /tabs %} `generatePrunedDeployOutput` supports npm, yarn, and pnpm. Bun has no lock file generation, so the function warns and the output keeps only the `package.json`. `generatePrunedDeployOutput` doesn't rewrite `workspace:` dependencies or copy workspace libraries into the output. Use the `@nx/js:prune-lockfile` and `@nx/js:copy-workspace-modules` executors for that. {% /tabitem %} {% tabitem label="Nx < 23.2" %} Use the `createPackageJson` and `createLockFile` functions: {% tabs %} {% tabitem label="Custom script" %} If you need to use a custom script, to build your application it should look similar to the following: ```javascript // scripts/create-package-json.js const { createProjectGraphAsync, detectPackageManager, writeJsonFile, } = require('@nx/devkit'); const { createLockFile, createPackageJson, getLockFileName, } = require('@nx/js'); const { writeFileSync } = require('fs'); async function main() { const outputDir = 'dist'; // You can replace this with the output directory you want to use // Detect the package manager you are using (npm, yarn, pnpm, bun) const pm = detectPackageManager(); const projectGraph = await createProjectGraphAsync(); const projectName = ''; const packageJson = createPackageJson(projectName, projectGraph, { isProduction: true, // Used to strip any non-prod dependencies }); const lockFile = createLockFile( packageJson, projectGraph, detectPackageManager() ); const lockFileName = getLockFileName(pm); writeJsonFile(`${outputDir}/package.json`, packageJson); writeFileSync(`${outputDir}/${lockFileName}`, lockFile, { encoding: 'utf8', }); //... Any additional steps you want to run } main(); ``` Then to run the script, update your `package.json` to include the following: ```json // package.json { "scripts": { "copy-package-json": "node scripts/create-package-json.js", "custom-build": "nx build && npm run copy-package-json" } } ``` Now, you can run `npm run custom-build` to build your application and generate the `package.json` and lock file. You can replace _npm_ with _yarn_, _pnpm_, or _bun_ if you are using those package managers. {% /tabitem %} {% tabitem label="Custom executor" %} ```typescript // Custom executor import { Schema } from './schema'; import { createPackageJson, createLockFile, getLockFileName } from '@nx/js'; import { writeFileSync } from 'fs'; import { detectPackageManager, ExecutorContext, writeJsonFile, } from '@nx/devkit'; export default async function buildExecutor( options: Schema, context: ExecutorContext ) { // ...your executor code const packageManager = detectPackageManager(); const packageJson = createPackageJson( context.projectName, context.projectGraph, { root: context.root, isProduction: true, // We want to strip any non-prod dependencies } ); // do any additional manipulations to "package.json" here const lockFile = createLockFile( packageJson, context.projectGraph, packageManager ); const lockFileName = getLockFileName(packageManager); writeJsonFile(`${options.outputPath}/package.json`, packageJson); writeFileSync(`${options.outputPath}/${lockFileName}`, lockFile, { encoding: 'utf-8', }); // any subsequent executor code } ``` {% /tabitem %} {% /tabs %} `createLockFile` supports npm, yarn, and pnpm. For Bun, it logs a message and returns an empty string, so skip the lock file write and run `bun install` in the output directory instead. {% /tabitem %} {% /tabs %} {% aside type="note" %} **What about Vite?** Vite is a build tool that is great for development, and we want to make sure that it is also great for production. We are working on an `NxVitePlugin` plugin for Vite that will have parity with the `NxWebpackPlugin`. Stay tuned for updates. {% /aside %} --- ## Enforce Module Boundaries {% index_page_cards path="guides/enforce-module-boundaries" /%} --- ## Ban Dependencies with Certain Tags Specifying which tags a project is allowed to depend on can sometimes lead to a long list of possible options: ```jsonc { "sourceTag": "scope:client", // we actually want to say it cannot depend on `scope:admin` "onlyDependOnLibsWithTags": [ "scope:shared", "scope:utils", "scope:core", "scope:client", ], } ``` The property `notDependOnLibsWithTags` is used to invert this condition by explicitly specifying which tag(s) it cannot depend on: ```jsonc { "sourceTag": "scope:client", // we accept any tag except for `scope:admin` "notDependOnLibsWithTags": ["scope:admin"], } ``` In contrast to `onlyDependOnLibsWithTags`, the `notDependOnLibsWithTags` will also follow down the _entire dependency tree_ to make sure there are no sub-dependencies that violate this rule. You can also use a combination of these two rules to restrict certain types of projects to be imported: ```jsonc { "sourceTag": "type:react", "onlyDependOnLibsWithTags": [ "type:react", "type:utils", "type:animation", "type:model", ], // make sure no `angular` code ends up being referenced by react projects "notDependOnLibsWithTags": ["type:angular"], } ``` --- ## Ban External Imports **This constraint is only available for projects using ESLint.** You may want to constrain what external packages a project may import. For example, you may want to prevent backend projects from importing packages related to your frontend framework. You can ban these imports using `bannedExternalImports` property in your dependency constraints configuration. A common example of this is for backend projects that use NestJS and frontend projects that use Angular. Both frameworks contain a class named `Injectable`. It's very easy for a developer to import the wrong one by mistake, especially when using auto-import in an IDE. To prevent this, add tags to define the type of project to distinguish between backend and frontend projects. Each tag should define its own list of banned external imports. {% tabs syncKey="eslint-config-preference" %} {% tabitem label="Flat Config" %} ```javascript // eslint.config.mjs import nx from '@nx/eslint-plugin'; export default [ ...nx.configs['flat/base'], ...nx.configs['flat/typescript'], ...nx.configs['flat/javascript'], { files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], rules: { '@nx/enforce-module-boundaries': [ 'error', { allow: [], // update depConstraints based on your tags depConstraints: [ // projects tagged with "frontend" can't import from "@nestjs/common" { sourceTag: 'frontend', bannedExternalImports: ['@nestjs/common'], }, // projects tagged with "backend" can't import from "@angular/core" { sourceTag: 'backend', bannedExternalImports: ['@angular/core'], }, ], }, ], }, }, ]; ``` {% /tabitem %} {% tabitem label="Legacy (.eslintrc.json)" %} ```jsonc // .eslintrc.json { // ... more ESLint config here // @nx/enforce-module-boundaries should already exist at the top-level of your config "@nx/enforce-module-boundaries": [ "error", { "allow": [], // update depConstraints based on your tags "depConstraints": [ // projects tagged with "frontend" can't import from "@nestjs/common" { "sourceTag": "frontend", "bannedExternalImports": ["@nestjs/common"], }, // projects tagged with "backend" can't import from "@angular/core" { "sourceTag": "backend", "bannedExternalImports": ["@angular/core"], }, ], }, ], // ... more ESLint config here } ``` {% /tabitem %} {% /tabs %} Another common example is ensuring that util libraries stay framework-free by banning imports from these frameworks. You can use wildcard `*` to match multiple projects e.g. `react*` would match `react`, but also `react-dom`, `react-native` etc. You can also have multiple wildcards e.g. `*react*` would match any package with word `react` in it's name. A workspace using React would have a configuration like this. {% tabs syncKey="eslint-config-preference" %} {% tabitem label="Flat Config" %} ```javascript // eslint.config.mjs import nx from '@nx/eslint-plugin'; export default [ ...nx.configs['flat/base'], ...nx.configs['flat/typescript'], ...nx.configs['flat/javascript'], { files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], rules: { '@nx/enforce-module-boundaries': [ 'error', { allow: [], // update depConstraints based on your tags depConstraints: [ // projects tagged with "type:util" can't import from "react" or related projects { sourceTag: 'type:util', bannedExternalImports: ['*react*'], }, ], }, ], }, }, ]; ``` {% /tabitem %} {% tabitem label="Legacy (.eslintrc.json)" %} ```jsonc // .eslintrc.json { // ... more ESLint config here // @nx/enforce-module-boundaries should already exist at the top-level of your config "@nx/enforce-module-boundaries": [ "error", { "allow": [], // update depConstraints based on your tags "depConstraints": [ // projects tagged with "type:util" can't import from "react" or related projects { "sourceTag": "type:util", "bannedExternalImports": ["*react*"], }, ], }, ], // ... more ESLint config here } ``` {% /tabitem %} {% /tabs %} ## Allowlist external imports with `allowedExternalImports` If you need a more restrictive approach, you can use the `allowedExternalImports` option to ensure that a project only imports from a specific set of packages. This is useful if you want to enforce separation of concerns _(e.g. keeping your domain logic clean from infrastructure concerns, or ui libraries clean from data access concerns)_ or keep some parts of your codebase framework-free or library-free. {% tabs syncKey="eslint-config-preference" %} {% tabitem label="Flat Config" %} ```javascript // eslint.config.mjs import nx from '@nx/eslint-plugin'; export default [ ...nx.configs['flat/base'], ...nx.configs['flat/typescript'], ...nx.configs['flat/javascript'], { files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], rules: { '@nx/enforce-module-boundaries': [ 'error', { allow: [], // update depConstraints based on your tags depConstraints: [ // limiting the dependencies of util libraries to the bare minimum // projects tagged with "type:util" can only import from "date-fns" { sourceTag: 'type:util', allowedExternalImports: ['date-fns'], }, // ui libraries clean from data access concerns // projects tagged with "type:ui" can only import packages matching "@angular/*" except "@angular/common/http" { sourceTag: 'type:ui', allowedExternalImports: ['@angular/*'], bannedExternalImports: ['@angular/common/http'], }, // keeping the domain logic clean from infrastructure concerns // projects tagged with "type:core" can't import any external packages. { sourceTag: 'type:core', allowedExternalImports: [], }, ], }, ], }, }, ]; ``` {% /tabitem %} {% tabitem label="Legacy (.eslintrc.json)" %} ```jsonc // .eslintrc.json { // ... more ESLint config here // @nx/enforce-module-boundaries should already exist at the top-level of your config "@nx/enforce-module-boundaries": [ "error", { "allow": [], // update depConstraints based on your tags "depConstraints": [ // limiting the dependencies of util libraries to the bare minimum // projects tagged with "type:util" can only import from "date-fns" { "sourceTag": "type:util", "allowedExternalImports": ["date-fns"], }, // ui libraries clean from data access concerns // projects tagged with "type:ui" can only import packages matching "@angular/*" except "@angular/common/http" { "sourceTag": "type:ui", "allowedExternalImports": ["@angular/*"], "bannedExternalImports": ["@angular/common/http"], }, // keeping the domain logic clean from infrastructure concerns // projects tagged with "type:core" can't import any external packages. { "sourceTag": "type:core", "allowedExternalImports": [], }, ], }, ], } ``` {% /tabitem %} {% /tabs %} --- ## Tag in Multiple Dimensions The example listed in [Enforce Module Boundaries](/docs/features/enforce-module-boundaries#tags) shows using a single dimension: `scope`. It's the most commonly used one. But you can find other dimensions useful. You can define which projects contain components, state management code, and features, so you, for instance, can disallow projects containing presentational UI components to depend on state management code. You can define which projects are experimental and which are stable, so stable applications cannot depend on experimental projects etc. You can define which projects have server-side code and which have client-side code to make sure your node app doesn't bundle in your frontend framework. Let's consider our previous three scopes - `scope:client`. `scope:admin`, `scope:shared`. By using just a single dimension, our `client-e2e` application would be able to import `client` application or `client-feature-main`. This is likely not something we want to allow as it's using framework that our E2E project doesn't have. Let's add another dimension - `type`. Some of our projects are applications, some are UI features and some are just plain helper libraries. Let's define three new tags: `type:app`, `type:feature`, `type:ui` and `type:util`. Our project configurations might now look like this: ```jsonc {% title="client" %} { // ... more project configuration here "tags": ["scope:client", "type:app"], } ``` ```jsonc {% title="client-e2e" %} { // ... more project configuration here "tags": ["scope:client", "type:app"], "implicitDependencies": ["client"], } ``` ```jsonc {% title="admin" %} { // ... more project configuration here "tags": ["scope:admin", "type:app"], } ``` ```jsonc {% title="admin-e2e" %} { // ... more project configuration here "tags": ["scope:admin", "type:app"], "implicitDependencies": ["admin"], } ``` ```jsonc {% title="client-feature-main" %} { // ... more project configuration here "tags": ["scope:client", "type:feature"], } ``` ```jsonc {% title="admin-feature-permissions" %} { // ... more project configuration here "tags": ["scope:admin", "type:feature"], } ``` ```jsonc {% title="components-shared" %} { // ... more project configuration here "tags": ["scope:shared", "type:ui"], } ``` ```jsonc {% title="utils" %} { // ... more project configuration here "tags": ["scope:shared", "type:util"], } ``` We can now restrict projects within the same group to depend on each other based on the type: - `app` can only depend on `feature`, `ui` or `util`, but not other apps - `feature` cannot depend on app or another feature - `ui` can only depend on other `ui` - everyone can depend on `util` including `util` itself {% tabs syncKey="eslint-config-preference" %} {% tabitem label="Flat Config" %} ```javascript // eslint.config.mjs import nx from '@nx/eslint-plugin'; export default [ ...nx.configs['flat/base'], ...nx.configs['flat/typescript'], ...nx.configs['flat/javascript'], { files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], rules: { '@nx/enforce-module-boundaries': [ 'error', { allow: [], // update depConstraints based on your tags depConstraints: [ { sourceTag: 'scope:shared', onlyDependOnLibsWithTags: ['scope:shared'], }, { sourceTag: 'scope:admin', onlyDependOnLibsWithTags: ['scope:shared', 'scope:admin'], }, { sourceTag: 'scope:client', onlyDependOnLibsWithTags: ['scope:shared', 'scope:client'], }, { sourceTag: 'type:app', onlyDependOnLibsWithTags: [ 'type:feature', 'type:ui', 'type:util', ], }, { sourceTag: 'type:feature', onlyDependOnLibsWithTags: ['type:ui', 'type:util'], }, { sourceTag: 'type:ui', onlyDependOnLibsWithTags: ['type:ui', 'type:util'], }, { sourceTag: 'type:util', onlyDependOnLibsWithTags: ['type:util'], }, ], }, ], }, }, ]; ``` {% /tabitem %} {% tabitem label="Legacy (.eslintrc.json)" %} ```jsonc // .eslintrc.json { // ... more ESLint config here // @nx/enforce-module-boundaries should already exist at the top-level of your config "@nx/enforce-module-boundaries": [ "error", { "allow": [], // update depConstraints based on your tags "depConstraints": [ { "sourceTag": "scope:shared", "onlyDependOnLibsWithTags": ["scope:shared"], }, { "sourceTag": "scope:admin", "onlyDependOnLibsWithTags": ["scope:shared", "scope:admin"], }, { "sourceTag": "scope:client", "onlyDependOnLibsWithTags": ["scope:shared", "scope:client"], }, { "sourceTag": "type:app", "onlyDependOnLibsWithTags": ["type:feature", "type:ui", "type:util"], }, { "sourceTag": "type:feature", "onlyDependOnLibsWithTags": ["type:ui", "type:util"], }, { "sourceTag": "type:ui", "onlyDependOnLibsWithTags": ["type:ui", "type:util"], }, { "sourceTag": "type:util", "onlyDependOnLibsWithTags": ["type:util"], }, ], }, ], // ... more ESLint config here } ``` {% /tabitem %} {% /tabs %} There are no limits to the number of tags, but as you add more tags the complexity of your dependency constraints rises exponentially. It's always good to draw a diagram and carefully plan the boundaries. ## Matching multiple source tags Matching just a single source tag is sometimes not enough for solving complex restrictions. To avoid creating ad-hoc tags that are only meant for specific constraints, you can also combine multiple tags with `allSourceTags`. Each tag in the array must be matched for a constraint to be applied: {% tabs syncKey="eslint-config-preference" %} {% tabitem label="Flat Config" %} ```javascript // eslint.config.mjs import nx from '@nx/eslint-plugin'; export default [ ...nx.configs['flat/base'], ...nx.configs['flat/typescript'], ...nx.configs['flat/javascript'], { files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], rules: { '@nx/enforce-module-boundaries': [ 'error', { allow: [], // update depConstraints based on your tags depConstraints: [ { // this constraint applies to all "admin" projects sourceTag: 'scope:admin', onlyDependOnLibsWithTags: ['scope:shared', 'scope:admin'], }, { sourceTag: 'type:ui', onlyDependOnLibsWithTags: ['type:ui', 'type:util'], }, { // we don't want our admin ui components to depend on anything except utilities, // and we also want to ban router imports allSourceTags: ['scope:admin', 'type:ui'], onlyDependOnLibsWithTags: ['type:util'], bannedExternalImports: ['*router*'], }, ], }, ], }, }, ]; ``` {% /tabitem %} {% tabitem label="Legacy (.eslintrc.json)" %} ```jsonc // .eslintrc.json { // ... more ESLint config here // @nx/enforce-module-boundaries should already exist at the top-level of your config "@nx/enforce-module-boundaries": [ "error", { "allow": [], // update depConstraints based on your tags "depConstraints": [ { // this constraint applies to all "admin" projects "sourceTag": "scope:admin", "onlyDependOnLibsWithTags": ["scope:shared", "scope:admin"], }, { "sourceTag": "type:ui", "onlyDependOnLibsWithTags": ["type:ui", "type:util"], }, { // we don't want our admin ui components to depend on anything except utilities, // and we also want to ban router imports "allSourceTags": ["scope:admin", "type:ui"], "onlyDependOnLibsWithTags": ["type:util"], "bannedExternalImports": ["*router*"], }, ], }, ], // ... more ESLint config here } ``` {% /tabitem %} {% /tabs %} ## Further reading - [Article: Taming Code Organization with Module Boundaries in Nx](https://nx.dev/blog/mastering-the-project-boundaries-in-nx) --- ## Tags Allow List Sometimes there are specific situations where you want to break the tag rules you've set up for project dependencies. Each project can set an `allow` property in the project configuration to override the tagging rules that have been set up. - `"allow": ['@myorg/mylib/testing']` allows importing `'@myorg/mylib/testing'`. - `"allow": ['@myorg/mylib/*']` allows importing `'@myorg/mylib/a'` but not `'@myorg/mylib/a/b'`. - `"allow": ['@myorg/mylib/**']` allows importing `'@myorg/mylib/a'` and `'@myorg/mylib/a/b'`. - `"allow": ['@myorg/**/testing']` allows importing `'@myorg/mylib/testing'` and `'@myorg/nested/lib/testing'`. --- ## Guides {% index_page_cards path="guides/installation" /%} --- ## Nx Cloud Guides {% index_page_cards path="guides/nx-cloud" /%} --- ## Reduce the Number of Affected Projects in a CI Pipeline Execution When it comes to troubleshooting long-running CI pipeline executions, there are different tools available to help you identify the potential issues. One such tool is the **Affected Project Graph** feature on the CI Pipeline Execution page. ## Getting to the CI pipeline execution affected project graph To access the affected project graph for the CI pipeline execution, navigate to the CI pipeline execution details page and click on the **Affected Project Graph** navigation item. ![CIPE Affected Project Graph](../../../../assets/nx-cloud/cipe-affected-project-graph-nav-item.png) The affected project graph visualizes the projects that are part of the **current** CI pipeline execution. ## Identifying a potential over-run of a CI pipeline execution In this recipe, a scenario where the affected project graph can be used to identify a potential over-run of a CI pipeline execution. This is our repository structure: {% filetree %} - apps/ - web/ - web-e2e - nx-graph-test/ - nx-graph-test-e2e/ - recipes/ - client/ - client-e2e/ - libs/ - ui/ (button and icon components) - forms/ - input/ - tooltip/ {% /filetree %} Our most recent CI pipeline execution affects everything in the repository. ![CIPE Affected Project Graph -- every tasks](../../../../assets/nx-cloud/cipe-affected-project-graph-every-tasks.png) Likewise, the affected project graph for the Ci pipeline execution also visualizes all projects because everything is affected. ![CIPE Affected Project Graph -- affect everything](../../../../assets/nx-cloud/cipe-affected-project-graph-every-projects.png) ## Create a new CI pipeline execution with a code change Our `ui` library has 2 components: `button` and `tooltip`. From the graph, we can see that both our apps `client` and `web` depend on the `ui` library: `client` uses the `tooltip` component and `web` uses the `button` component. Let's make an update to the `tooltip` component and see how it affects our next CI pipeline execution. Pushing this change to our repository will trigger a new CI pipeline execution. ![CIPE Affected Project Graph -- new CIPE](../../../../assets/nx-cloud/cipe-affected-project-graph-tooltip-tasks.png) This CI pipeline execution contains 14 tasks that are affected by the change we made to the `tooltip` component. ![CIPE Affected Project Graph -- new CIPE tasks](../../../../assets/nx-cloud/cipe-affected-project-graph-tooltip-affected.png) The affected project graph also shows that the change to `tooltip` component , which is part of the `ui` library, affects both the `client` and `web` apps. At this point, we can ask ourselves that "Should a change to the `tooltip` component affect both the `client` and `web` apps or should it only affect the `web` app?" Our goal should be to always have the most efficient CI pipeline executions possible. Decreasing the number of affected projects will allow the number of tasks to be reduced, which will reduce the overall CI pipeline execution time. ## Break up the source of the affected projects To achieve our goal, we can break up the `ui` library into 2 separate libraries: `button` and `tooltip`. > Check out our [blog post](https://nx.dev/blog/improve-architecture-and-ci-times-with-projects) about splitting large projects into smaller ones. Once we have done this, we will end up with the following project graph: ![CIPE Affected Project Graph -- break up ui](../../../../assets/nx-cloud/cipe-affected-project-graph-break-up-ui.png) Let's make a change to the `button` component this time and see how it affects our next CI pipeline execution. ![CIPE Affected Project Graph -- button tasks](../../../../assets/nx-cloud/cipe-affected-project-graph-button-tasks.png) We've reduced the number of affected tasks from 14 to 8. ![CIPE Affected Project Graph -- button affected](../../../../assets/nx-cloud/cipe-affected-project-graph-button-affected.png) And the affected project graph also reflects that change properly. {% aside type="tip" %} **Does your Affected Project Graph only show affected projects and not touched?** - If your commit has changes to one of the global inputs, your projects will be affected but no specific project is touched directly. - Make sure your main job starts the run, with `start-nx-agents` or `start-ci-run`, for touched projects to be recorded. Learn more about [Nx Agents](/docs/features/ci-features/distribute-task-execution) {% /aside %} --- ## Enable AI Features This guide walks you through enabling AI features in Nx Cloud. ## Enable AI features To enable AI features for your organization, go to [your organization's settings](https://cloud.nx.app/go/organization/edit) on Nx Cloud and select the organization where you want to enable AI. In the **settings** menu, find the **AI Features** section and toggle it **On**. ![enable ai features](../../../../assets/features/ci-features/ai-features.avif) Make sure you **accept the AI terms** to start using the AI features. Once AI features are enabled, turn on Self-Healing CI from your [workspace settings](https://cloud.nx.app/go/workspace/settings). For how the fix runs in your pipeline, see the [Self-Healing CI documentation](/docs/features/ci-features/self-healing-ci). ## Bring your own API key (BYOK) An organization can use its own AI provider API key for Self-Healing CI instead of the installation's key. BYOK doesn't require `NX_CLOUD_AI_TOKEN_PROVIDER_TYPE`. Enabling **Bring Your Own API Key** requires an installation admin (a user with `installationAdmin: true`). Regular organization admins see the toggle disabled. 1. Log in as an installation admin. 2. Open the organization settings. 3. Enable the **AI Features** toggle and accept the AI terms. 4. Enable the **Bring Your Own API Key** toggle. Supply the key in one of two ways: - Configure the provider and API key on the organization's AI provider settings page. The key is stored encrypted. - Leave the UI unset and provide `ANTHROPIC_API_KEY` in the CI runner environment. ## Regional availability AI features aren't available for the EU cluster in public cloud installations due to regional restrictions. Providing your own Anthropic API key (BYOK) for Self-Healing CI isn't subject to this restriction. --- ## Enable End to End Encryption To turn on end to end encryption, specify an encryption key in one of two ways: - Set the `nxCloudEncryptionKey` property in `nx.json` - Set the `NX_CLOUD_ENCRYPTION_KEY` environment variable The key can be any string up to 32 characters long. Providing an encryption key tells Nx to encrypt task artifacts on your machine before they are sent to the remote cache. Then when cached results are downloaded to your machine they are decrypted before they are used. This ensures that even if someone gained access to the Nx Cloud servers, they wouldn't be able to view your task artifacts. ## Metadata All the artifacts Nx Cloud uses to replay a task for you are encrypted. That means that even if someone gets access to your Nx Cloud storage bucket, they will not be able to tamper with the files and terminal output that is restored when the task is replayed on your CI or developer's machines. We also store an un-encrypted version of the terminal output separately that is accessible only to invited members of the workspace on the Nx Cloud web app, so they can see why certain tasks failed. This un-encrypted output is only used in the browser, and not used when replaying the task. ## Summary Data is encrypted both at rest and in transit. - Every communication with the Nx Cloud API is encrypted in transit, including fetching/storing artifacts. - When using Nx Public Cloud, the stored metadata is encrypted. - When using Nx Public Cloud and e2e encryption, stored artifacts are encrypted. - When using the on-prem version of Nx Cloud, the stored metadata is encrypted if you run MongoDB yourself with encryption on - When using the on-prem version of Nx Cloud, stored artifacts are encrypted using e2e encryption. --- ## Connecting Nx Cloud to your existing Google identity provider If your organization uses [Google Identity](https://cloud.google.com/identity) or [Google Workspaces](https://workspace.google.com/intl/en_uk/) to manage employee accounts and permissions, your Nx Cloud workspace members can re-use the same accounts to sign-in to Nx Cloud and view runs, cache stats etc. Besides being more convenient for the employee, as they don't have to sign-in again, it also has a security benefit: if an employee leaves the company and their Google account is disabled, they won't be able to sign-in to Nx Cloud anymore. By default, when you invite a member by email, they can create a separate Nx Cloud account using their work e-mail address. **If their primary email address gets disabled, they will still be able to sign-in with their Nx Cloud account, unless you explicitly revoke their membership from the Members page.** If you'd like them to sign-in with Google directly, which ensures they automatically lose access to their Nx Cloud account if their email gets disabled, you need to enable this option when inviting them: "_Require Social OAuth Sign-In_". They will then only be able to accept the invite if they sign-in with Google directly. ![Require Google OAuth Sign-In toggle](../../../../assets/nx-cloud/require-google-signin.webp) ## SAML integration Direct integration with SAML identity providers is a feature of [Nx Enterprise](https://nx.dev/enterprise). You can, however, connect your existing SAML provider to Google, and then use the method above to invite employees: - [Azure AD](https://docs.microsoft.com/en-us/azure/active-directory/saas-apps/google-apps-tutorial) - [Okta](https://www.okta.com/integrations/google-workspace/#overview) --- ## Optimize Your Time to Green (TTG) Time to Green (TTG) is the **time from when a pull request (PR) opens and triggers CI to the moment all checks are green and the PR is review-ready**. TTG is a practical sub-metric of Time to Merge (TTM): by compressing TTG (lower is better), you remove the biggest day‑to‑day bottlenecks that developers feel, which in turn improves overall TTM. ## Why is this important? The biggest day‑to‑day waste in engineering teams: **constant context switching and PR babysitting**. The common loop is: - 🧑‍💻 Write code - 🧑‍💻 Push PR - ⏳ CI runs - ❌ CI fails 2 minutes later - ⏳ Discover it much later - ‍🧑‍💻 Switch context to debug and trigger CI run - ⏳ Re-running CI - ❌ Flaky test fails CI - ‍🧑‍💻 Switch context to debug and trigger CI run - ⏳ Re-running CI - ✅ CI is finally green - ‍🧑‍💻 Reach out to someone to review This delay compounds across teams and drastically slows delivery. **Nx Cloud fixes this.** ## How to improve TTG High TTG usually comes from three sources: slow failure discovery, disruptive PR babysitting, and raw execution time. Tackle them in this order. **Prerequisite: Connect your workspace to Nx Cloud** If you haven't already, run the following command to connect your workspace to Nx Cloud: ```shell npx nx@latest connect ``` {% call_to_action title="Connect from your browser" url="https://cloud.nx.app/get-started?utm_source=nx-dev&utm_medium=website&utm_campaign=optimize-your-ttg" icon="nxcloud" description="Sign in to Nx Cloud and connect your repository without the CLI" /%} For the full pipeline walkthrough, see [Setting Up CI](/docs/getting-started/setup-ci). ### 1) Get failure feedback immediately (avoid late discovery) When you don't notice CI failed, you lose time before you can act. Tighten the loop so failures surface where you're working. **What to do:** See failures immediately where you work by getting a notification in your editor: install [Nx Console](/docs/getting-started/editor-setup). ### 2) Eliminate PR babysitting (minimize context switching) The expensive loop is switching branches to fix, re‑pushing, waiting, and repeating; especially with flakes. **What to do:** - Approve fixes instead of branch‑hopping: enable **[Self‑Healing CI](/docs/features/ci-features/self-healing-ci#configure-your-ci-pipeline)** to analyze failed tasks, propose and verify fixes, and commit to your PR after approval. - Also enable **[flaky task detection and retries](/docs/features/ci-features/flaky-tasks)** to automatically re-run flaky tasks in the background while you keep working undisturbed. ### 3) Shorten actual CI time (make the pipeline fast) Once feedback and context switching are handled, compress the compute side. **What to do:** - Reuse work with [remote caching](/docs/features/ci-features/remote-cache). - Run more in parallel with **[Distributed task execution (Nx Agents)](/docs/features/ci-features/distribute-task-execution)**. - Scale long suites with **[E2E test splitting](/docs/features/ci-features/split-e2e-tasks)** so they finish quickly. ## Measure TTG and diagnose bottlenecks ![TTG metrics](../../../../assets/nx-cloud/nx-cloud-ttg-stats.avif) **Doing too much work on PRs** - Use [Nx Affected](/docs/features/ci-features/affected) to only run what changed. **Cache hit rate is low** - Ensure tasks are cacheable and deterministic. Define `outputs` and configure `inputs`/`namedInputs` correctly. See: [Configure Inputs](/docs/kb/configure-inputs), [Configure Outputs](/docs/kb/configure-outputs), [Inputs Reference](/docs/reference/inputs) - Standardize Node/PNPM versions across dev and CI; avoid environment variables that unintentionally affect inputs. - Use [remote caching](/docs/features/ci-features/remote-cache) to share results between CI and developer machines. **Agents are idle, or queue time is high** - Increase or right-size distributed capacity and parallelism with [Nx Agents](/docs/features/ci-features/distribute-task-execution). - Use [Dynamic Agents](/docs/features/ci-features/dynamic-agents) to scale based on PR size. - Remove unnecessary serialization (global locks, [overly strict `dependsOn`](/docs/kb/defining-task-pipeline)). **E2E suites take too long** - Enable [E2E test splitting](/docs/features/ci-features/split-e2e-tasks) so large suites run across agents. - Ensure tests are shardable (no hidden global state, independent specs). **Flaky task rate is high** - Enable [flaky test detection and automatic retries](/docs/features/ci-features/flaky-tasks). - [Find and fix flaky tests](/docs/kb/flaky-tests-in-ci) before they consume more pipeline time. **Late failure discovery / PR babysitting** - Install [Nx Console](/docs/getting-started/editor-setup) for instant failure and fix notifications in your editor. - Enable [Self‑Healing CI](/docs/features/ci-features/self-healing-ci) to propose and validate fixes automatically (ensure the `npx nx fix-ci` step runs with `if: always()`). ## Talk to us If you still need help feel free to [reach out to us](/contact). --- ## Recording Non-Nx Commands Build and deploy pipelines often do much more than run builds. Unfortunately, this creates more opportunities for pieces to fail. To minimize the number of different sites you need to visit to diagnose issues, Nx Cloud 13.3 and above is capable of recording and saving output from arbitrary commands. ## Enable command recording To record a command with Nx Cloud: 1. Identify a command you would like recorded from your CI/CD configuration, or think of one to run on your machine. (example: echo "hello world") 2. Prefix your command with `npx nx record --`, or the appropriate execute command of your package manager. The `--` is optional but makes it easier to read what portion of the command will be recorded. (example: npx nx record -- echo "hello world") 3. Run the command! Nx Cloud will record output and status codes, and generate a link for you to view your output on so you can easily view or share the result. Make sure you run this command from your workspace root or one of its subdirectories so Nx Cloud can properly locate configuration information. ![npx nx record -- echo "hello world"](../../../../assets/nx-cloud/set-up/record-hello-world.webp) ## Locating command output in Nx Cloud Commands that Nx Cloud stores will appear under your "Runs" view. For easy identification, the stored output will be displayed as a "record-output" target being invoked on the "nx-cloud-tasks-runner" project. ![nx record -- nx format:check](https://nx.dev/nx-cloud/set-up/record-format-check.webp) If you use the Nx Cloud GitHub Integration, links to recorded output will also be displayed based on the exit code in the summary comment. ![Nx Cloud Report](../../../../assets/nx-cloud/set-up/record-report.webp) --- ## Azure DevOps Integration The Nx Cloud + Azure Devops Integration lets you access the result of every run—with all its logs and build insights—straight from your PR. ## Connecting your workspace ![Access VCS Setup](../../../../../assets/nx-cloud/set-up/access-vcs-setup.webp) Once on the VCS Integrations setup page, select "Azure DevOps". You will be prompted to enter the name of your organization and project. Identifying your organization and project can be done by looking at the URL of your project summary page. ```text // URL format https://dev.azure.com/[organization]/[project] ``` For example, the url `https://dev.azure.com/nrwl/my-monorepo-project` has an organization name of "nrwl", and a project name of "large-monorepo". You will also need to provide the id of your Azure Git repository, this can either be the internal GUID identifier, if known, or you can use the name of the repository from the URL you use to access it. For example, a URL of `https://dev.azure.com/nrwl/_git/large-monorepo` has the repository id of "large-monorepo". ![Add Azure DevOps Repository](../../../../../assets/nx-cloud/set-up/add-azure-devops-repository.webp) ### Configuring authentication #### Using a personal access token To use a Personal Access Token for authentication, one must be generated with proper permissions. The minimum required permissions are shown in the screenshot below. ![Work Items - Read, Code - Read, Build - Read & execute, Release - Read, write, & execute](../../../../../assets/nx-cloud/set-up/minimal-ado-access-token.webp) Once this token is created paste the value and then click "Connect". This will verify that Nx Cloud can connect to your repo. Upon a successful test, your configuration is saved, and setup is complete. Please note that Azure DevOps will impose rate limits which can degrade the performance of the integration leading to missing data or functionality. To mitigate the impact, we recommend you assign the [Basic + Test plan](https://learn.microsoft.com/en-us/azure/devops/organizations/billing/buy-basic-access-add-users?view=azure-devops#assign-basic-or-basic--test-plans) to the user whose token you utilise for this integration. ### Advanced configuration If your company runs a self-hosted Azure DevOps installation, you may need to override the default URL that Nx Cloud uses to connect to the Azure Devops API. To do so, check the box labeled "Override Azure DevOps API URL" and enter the correct URL for your organization. --- ## Bitbucket Integration The Nx Cloud + Bitbucket Integration lets you access the result of every run—with all its logs and build insights—straight from your PR. ### Using an API token API tokens can be generated on a user account level and can be scoped to specific applications like Bitbucket. This is the recommended approach for Nx Cloud integration. An API token is a secure credential that allows scripts and other processes to authenticate with Bitbucket Cloud applications. You should treat API tokens as securely as any other password. #### Creating an API token 1. First, navigate to your [BitBucket user security settings](https://id.atlassian.com/manage-profile/security/api-tokens) 2. Select "Create API token with scopes" ![Create API Token](../../../../../assets/nx-cloud/set-up/bitbucket-api-tokens-create-screen.jpg) 3. Give your token a name and proceed to the next step 4. When prompted to select the app, choose **Bitbucket**. This ensures the API token can only access Bitbucket APIs and perform git operations: ![Select Bitbucket App](../../../../../assets/nx-cloud/set-up/bitbucket-api-tokens-create-scope.jpg) 5. The required permissions for Nx Cloud are: - `read:pullrequest:bitbucket` - to read pull request information - `write:pullrequest:bitbucket` - to write comments on pull requests - `read:repository:bitbucket` - to read repository contents - `write:repository:bitbucket` - to write files to the repository - `read:user:bitbucket` - verify username from email address - `account:read` - verify username from email address 6. Click "Create token" and copy your newly created API token #### Configuring Nx Cloud with your API Token Once your API token is created, head back to your workspace settings on NxCloud to set up the BitBucket integration: ![Access VCS Setup](../../../../../assets/nx-cloud/set-up/access-vcs-setup.webp) 1. Fill-in all the required fields for selecting your BitBucket repository 2. Username is found on the [account settings](https://bitbucket.org/account/settings/) screen (it is not your email address) 3. Paste your API token created earlier into the API Token box 4. Click "Connect" to finish the setup ### Using an HTTP access token If you are using BitBucket Data Center (on-prem) you need to enable an [HTTP access token for authentication](https://confluence.atlassian.com/bitbucketserver/http-access-tokens-939515499.html). {% aside type="note" title="User linked Access Tokens" %} Due to the type of APIs NxCloud needs to call, we need to create an Access Token [**at the user level**](https://confluence.atlassian.com/bitbucketserver/http-access-tokens-939515499.html). Repo level access tokens will not work. {% /aside %} The minimum required permissions are write access to the repository: ![Create an Access Token](../../../../../assets/nx-cloud/set-up/bitbucket-data-center-access-token.png) Once the Access Token is created, save it in a secure location and then head back to your workspace settings on NxCloud and let's set up a BitBucket integration: ![Access VCS Setup](../../../../../assets/nx-cloud/set-up/access-vcs-setup.webp) 1. Fill-in all the required fields for selecting your Bitbucket repository 2. Username is found on the [account settings](https://your-bitbucket-instance.com/profile) screen (it is not your email address) 3. Paste your Access Token created earlier into the Access Token box 4. Make sure you give NxCloud the URL of your BitBucket instance (this can be in the simple form of `https://your-bitbucket-instance.com`) 5. Click "Connect" to finish the setup --- ## GitLab Integration The Nx Cloud GitLab Integration lets you access the result of every run—with all its logs and build insights—straight from your Merge Requests. ### Connecting your workspace ![Access VCS Setup](../../../../../assets/nx-cloud/set-up/access-vcs-setup.webp) Once on the VCS Integrations setup page, select "GitLab". You will be prompted to enter your project's ID. ![Locate Gitlab Project ID](../../../../../assets/nx-cloud/set-up/find-gitlab-project-id.avif) To locate the ID for your project, visit the home page of your repository on GitLab. Click the three-dot menu button in the upper right corner and select "Copy project ID" to copy the value to your clipboard. ![Add GitLab Repository](../../../../../assets/nx-cloud/set-up/add-gitlab-repository.webp) ### Using a personal access token To use a Personal Access Token for authentication, one must be generated with proper permissions. The minimum required permissions are shown in the screenshot below. ![Minimum GitLab Personal Access Token Permissions](../../../../../assets/nx-cloud/set-up/minimal-gitlab-access-token.webp) Once this token is created, select the radio button for providing a personal access token, paste the value, and then click "Connect". This will verify that Nx Cloud can connect to your repo. Upon a successful test, your configuration is saved, and setup is complete. ### Advanced configuration If your company runs a self-hosted GitLab installation, you may need to override the default URL that Nx Cloud uses to connect to the GitLab API. To do so, check the box labeled "Override GitLab API URL" and enter the correct URL for your organization. --- ## Nx Console {% index_page_cards path="guides/nx-console" /%} --- ## Nx Release {% index_page_cards path="guides/nx-release" /%} --- ## Automatically Version with Conventional Commits If you wish to bypass the versioning prompt, you can configure Nx Release to defer to the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) standard to determine the version bump automatically. This is useful for automating the versioning process in a CI/CD pipeline. See the [`nx release version`](/docs/reference/nx-commands#nx-release-version) CLI reference for all available versioning options. ## Enable automatic versioning To enable automatic versioning via conventional commits, set the `release.version.conventionalCommits` property to `true` in `nx.json`: ```json // nx.json { "release": { "version": { "conventionalCommits": true } } } ``` ## Determine the version bump Nx Release will use the commit messages since the last release to determine the version bump. It will look at the type of each commit and determine the highest version bump from the following list: - 'feat' -> minor - 'fix' -> patch For example, if the git history looks like this: ```text - fix(pkg-1): fix something - feat(pkg-2): add a new feature - chore(pkg-3): update docs - chore(release): 1.0.0 ``` then Nx Release will select the `minor` version bump and elect to release version 1.1.0. This is because there is a `feat` commit since the last release of 1.0.0. To customize the version bump for different types of commits, or to trigger a version bump with custom commit types, see the [Customize Conventional Commit Types](/docs/guides/nx-release/customize-conventional-commit-types) recipe. {% aside type="note" title="No changes detected" %} If Nx Release does not find any relevant commits since the last release, it will skip releasing a new version. This works with [independent releases](/docs/guides/nx-release/release-projects-independently) as well, allowing for only some projects to be released and some to be skipped. {% /aside %} ## Usage with independent releases If you are using [independent releases](/docs/guides/nx-release/release-projects-independently), Nx Release will determine the version bump for each project independently. For example, if the git history looks like this: ```text - fix(pkg-1): fix something - feat(pkg-2): add a new feature - chore(pkg-3): update docs - chore(release): publish ``` Nx Release will select the `patch` version bump for `pkg-1` and `minor` for `pkg-2`. `pkg-3` will be skipped entirely, since it has no `feat` or `fix` commits. {% aside type="note" title="Determining if a commit affects a project" %} Note that this determination is made based on files changed by each commit, _not_ by the scope of the commit message itself. This means that `feat(pkg-2): add a new feature` could trigger a version bump for a project other than `pkg-2` if it updated files in another project. {% /aside %} An example partial output of running Nx Release with independent releases and conventional commits enabled: ```plaintext {% frame="terminal" title="nx release" %} NX Running release version for project: pkg-1 pkg-1 🏷️ Resolved the current version as 0.4.0 from git tag "pkg-1@0.4.0", based on releaseTag.pattern "{projectName}@{version}" pkg-1 📄 Resolved the specifier as "patch" using git history and the conventional commits standard pkg-1 ❓ Applied semver relative bump "patch", derived from conventional commits data, to get new version 0.4.1 pkg-1 ✍️ New version 0.4.1 written to manifest: packages/pkg-1/package.json NX Running release version for project: pkg-2 pkg-2 🏷️ Resolved the current version as 0.4.0 from git tag "pkg-2@0.4.0", based on releaseTag.pattern "{projectName}@{version}" pkg-2 📄 Resolved the specifier as "minor" using git history and the conventional commits standard pkg-2 ❓ Applied semver relative bump "minor", derived from conventional commits data, to get new version 0.5.0 pkg-2 ✍️ New version 0.5.0 written to manifest: packages/pkg-2/package.json NX Running release version for project: pkg-3 pkg-3 🏷️ Resolved the current version as 0.4.0 from git tag "pkg-3@0.4.0", based on releaseTag.pattern "{projectName}@{version}" pkg-3 🚫 No changes were detected using git history and the conventional commits standard ``` --- ## Build Before Versioning In order to ensure that projects are built before the new version is applied to their package manifest, you can use the `preVersionCommand` property in `nx.json`: ```json // nx.json { "release": { "version": { "preVersionCommand": "npx nx run-many -t build" } } } ``` This command will run the `build` target for all projects before the version step of Nx Release. Any command can be specified, including non-nx commands. This step is often required when [publishing from a custom dist directory](/docs/guides/nx-release/updating-version-references#scenario-2-i-want-to-publish-from-a-custom-dist-directory-and-update-references-in-my-both-my-source-and-dist-packagejson-files), as the dist directory must be built before the version is applied to the dist directory's package manifest. When using release groups in which the member projects are versioned together, you can use `groupPreVersionCommand` and it will be executed before the versioning step for that release group. ```json // nx.json { "release": { "groups": { "my-group": { "projects": ["my-lib-one", "my-lib-two"], "version": { "groupPreVersionCommand": "npx nx run-many -t build -p my-lib-one,my-lib-two" } } } } } ``` The `groupPreVersionCommand` will run in addition to the global `preVersionCommand`. ## Build before Docker versioning In order to ensure that images are built before versioning, use the `preVersionCommand` property in the `docker` section of `nx.json`. ```jsonc // nx.json { "release": { "docker": { "preVersionCommand": "npx nx run-many -t docker:build", }, }, } ``` If `preVersionCommand` is not set, the default is `npx nx run-many -t docker:build`, which builds all projects with a `docker:build` target. You can customize this command to be anything that runs prior to Docker versioning. When using release groups with Docker, use the `groupPreVersionCommand` option to run a command before the versioning step for that group. ```jsonc // nx.json { "release": { "groups": { "my-group": { "projects": ["api", "microservice"], "docker": { "groupPreVersionCommand": "npx nx run-many -t docker:build -p api,microservice", }, }, }, }, } ``` The `groupPreVersionCommand` will run in addition to the global `preVersionCommand` for Docker. --- ## Configuring Version Prefix for Dependencies This guide explains how to configure a custom version prefix in Nx Release using the `versionPrefix` option. The version prefix allows you to automatically add a specific prefix format to dependencies, providing control over how dependency versions are specified in your project's manifest files (such as `package.json`, `Cargo.toml`, etc.). ## The `versionPrefix` option The `versionPrefix` option controls which prefix is applied to dependency versions during the versioning process. By default, `versionPrefix` is set to `"auto"`, which selects a prefix format (either `""`, `"~"`, `"^"`, or `"="`) by respecting what is already in the manifest file. For example, having the following `package.json` file as an example manifest: ```json { "name": "my-package", "version": "0.1.1", "dependencies": { "dependency-one": "~1.2.3", "dependency-two": "^2.3.4", "dependency-three": "3.0.0" } } ``` Then next patch bump will be: ```json { "name": "my-package", "version": "0.1.2", "dependencies": { "dependency-one": "~1.2.4", "dependency-two": "^2.3.4", "dependency-three": "3.0.0" } } ``` Preserving the prefix for `dependency-one` and `dependency-two` and continuing to use no prefix for `dependency-three`. ### Available prefix options You can set `versionPrefix` to one of the following values: - `"auto"`: Automatically chooses a prefix based on the existing declaration in the manifest file. This is the default value. - `""`: Uses the exact version without a prefix. - `"~"`: Specifies compatibility with patch-level updates. - `"^"`: Specifies compatibility with minor-level updates. - `"="`: Locks the version to an exact match (the `=` is not commonly used in the JavaScript ecosystem, but is in others such as Cargo for Rust). Example configuration: ```json // nx.json { "release": { "version": { "versionPrefix": "~" } } } ``` ## Configuring version prefix in `nx.json` or `project.json` To set the versionPrefix option globally or for a specific project, add it to either your `nx.json` or `project.json` configuration files: ```jsonc { "release": { "version": { "versionPrefix": "^", // or "", "~", "^", "=" depending on your preference }, }, } ``` With the `versionPrefix` option set to `^`, your `package.json` dependencies might look like this: ```json { "name": "my-package", "version": "0.1.1", "dependencies": { "dependency-one": "^1.0.0", "dependency-two": "^2.3.4", "dependency-three": "^3.0.0" } } ``` This configuration helps enforce a consistent approach to dependency management, allowing flexibility in how updates to dependencies are tracked and managed across your project. --- ## Configure Changelog Format The default changelog renderer for `nx release` generates a changelog entry for each released project similar to the following: ```md ## 7.9.0 (2024-05-13) ### 🚀 Features - **rule-tester:** check for missing placeholder data in the message ([#9039](https://github.com/typescript-eslint/typescript-eslint/pull/9039)) ### ❤️ Thank You - Kirk Waiblinger - Sheetal Nandi - Vinccool96 ``` ## Include all metadata There are a few options available to modify the default changelog renderer output. They can be applied to both `workspaceChangelog` and `projectChangelogs` in exactly the same way. All four options are true by default: ```json // nx.json { "release": { "changelog": { "projectChangelogs": { "renderOptions": { "authors": true, "applyUsernameToAuthors": true, "commitReferences": true, "versionTitleDate": true } } } } } ``` #### `authors` Whether the commit authors should be added to the bottom of the changelog in a "Thank You" section. Defaults to `true`. #### `applyUsernameToAuthors` If authors is enabled, controls whether or not to try to map the authors to their GitHub usernames using https://ungh.cc (from https://github.com/unjs/ungh) and the email addresses found in the commits. Defaults to `true`. You should disable this option if you don't want to make any external requests to https://ungh.cc NOTE: Prior to Nx v21, this option was named `mapAuthorsToGitHubUsernames`. #### `commitReferences` Whether the commit references (such as commit and/or PR links) should be included in the changelog. Defaults to `true`. #### `versionTitleDate` Whether to include the date in the version title. It can be set to `false` to disable it, or `true` to enable with the default of (YYYY-MM-DD). Defaults to `true`. ## Remove all metadata If you prefer a more minimalist changelog, you can set all the options to false, like this: ```json // nx.json { "release": { "changelog": { "projectChangelogs": { "renderOptions": { "authors": false, "applyUsernameToAuthors": false, "commitReferences": false, "versionTitleDate": false } } } } } ``` Which will generate a changelog that looks similar to the following: ```md ## 7.9.0 ### 🚀 Features - **rule-tester:** check for missing placeholder data in the message ``` ## Custom changelog renderer {% badge text="Nx 22+" /%} For complete control over changelog formatting, provide a workspace path or package specifier for a JavaScript or TypeScript module that exports a class extending `DefaultChangelogRenderer`. Use `module.exports` from a CommonJS module or a default export from an ECMAScript module (ESM). The examples use CommonJS JavaScript because it works without additional TypeScript tooling or ESM configuration. You can use ESM or TypeScript instead when your workspace is configured to load it. For workspace files, start the path with `{workspaceRoot}` because plain relative paths such as `./tools/custom-changelog-renderer.js` don't resolve from the workspace root. ```jsonc // nx.json { "release": { "changelog": { "projectChangelogs": { "renderer": "{workspaceRoot}/tools/custom-changelog-renderer.js", }, }, }, } ``` Override `render()` when you want to replace or wrap the complete changelog output. For a targeted change, override one of the protected rendering or formatting methods instead. The inherited `render()` method continues to assemble every section you don't customize. Common extension points include `renderVersionTitle()`, `renderChangesByType()`, `renderBreakingChanges()`, `renderDependencyBumps()`, `renderAuthors()`, and helpers such as `formatChange()`. ### Customize one rendering stage Override a protected method to change one part of the changelog while keeping the rest of the default output: ```javascript // tools/custom-changelog-renderer.js const DefaultChangelogRenderer = require('nx/release/changelog-renderer').default; module.exports = class CustomRenderer extends DefaultChangelogRenderer { renderVersionTitle() { return `# Release ${this.changelogEntryVersion}`; } }; ``` ### Replace the complete output Override `render()` without calling `super.render()` when you want to generate the complete entry: ```javascript // tools/custom-changelog-renderer.js const DefaultChangelogRenderer = require('nx/release/changelog-renderer').default; module.exports = class CustomRenderer extends DefaultChangelogRenderer { async render() { return `# ${this.changelogEntryVersion}\n\nGenerated by our release workflow`; } }; ``` ### Adjust the complete output Suppose your release process publishes extended notes to a separate internal site. This hypothetical renderer calls `super.render()` to keep the default changelog, then appends a link: ```javascript // tools/custom-changelog-renderer.js const DefaultChangelogRenderer = require('nx/release/changelog-renderer').default; module.exports = class CustomRenderer extends DefaultChangelogRenderer { async render() { const changelog = await super.render(); if (!changelog) return changelog; const releaseUrl = `https://releases.example.com/releases/v${this.changelogEntryVersion}`; return `${changelog}\n\nSee the [extended release notes](${releaseUrl}).`; } }; ``` The release URL uses the default `v{version}` tag pattern for fixed releases. Adjust it when you use independent releases or a custom release tag pattern. Some extension points run only when their corresponding gate returns `true`. For example, Nx calls `renderBreakingChanges()`, `renderDependencyBumps()`, and `renderAuthors()` only when `hasBreakingChanges()`, `hasDependencyBumps()`, or `shouldRenderAuthors()` returns `true`. The [typescript-eslint custom changelog renderer](https://github.com/typescript-eslint/typescript-eslint/blob/780a8c52d54fcb6420232b2f53974959a9b26dff/tools/release/changelog-renderer.js) is a real-world JavaScript example that follows this pattern. {% aside type="note" title="Programmatic API" %} When using the programmatic `ReleaseClient` API (Nx 22+), you can also pass the renderer implementation class directly instead of a file path, allowing for dynamic renderer selection without file system access. {% /aside %} --- ## Configure Custom Registries To publish JavaScript packages, Nx Release uses the `npm` CLI under the hood, which defaults to publishing to the `npm` registry (`https://registry.npmjs.org/`). If you need to publish to a different registry, you can configure the registry in the `.npmrc` file in the root of your workspace or at the project level in the project configuration. This covers private registries such as GitHub Packages, JFrog Artifactory, Azure Artifacts, and Verdaccio. ## Set the registry in the root .npmrc file The easiest way to configure a custom registry is to set it in the `npm` configuration via the root `.npmrc` file. This file is located in the root of your workspace, and Nx Release will use it for publishing all projects. To set the registry, add the 'registry' property to your root `.npmrc` file: ```bash // .npmrc registry=https://my-custom-registry.com/ ``` ### Authenticate to the Registry in CI To authenticate with a custom registry in CI, you can add authentication tokens to the `.npmrc` file: ```bash // .npmrc registry=https://my-custom-registry.com/ //my-custom-registry.com/:_authToken= ``` See the [npm documentation](https://docs.npmjs.com/cli/v11/configuring-npm/npmrc#auth-related-configuration) for more information. ## Configure multiple registries with npm scopes The recommended way to determine which registry packages are published to is by using [npm scopes](https://docs.npmjs.com/cli/v11/using-npm/scope). All packages with a name that starts with your scope will be published to the registry specified in the `.npmrc` file for that scope. Consider the following example: ```bash // .npmrc @my-scope:registry=https://my-custom-registry.com/ //my-custom-registry.com/:_authToken= @other-scope:registry=https://my-other-registry.com/ //my-other-registry.com/:_authToken= registry=https://my-default-registry.com/ //my-default-registry.com/:_authToken= ``` With the above `.npmrc`, the following packages would be published to the specified registries: - `@my-scope/pkg-1` -> `https://my-custom-registry.com/` - `@other-scope/pkg-2` -> `https://my-other-registry.com/` - `pkg-3` -> `https://my-default-registry.com/` ## Specify an alternate registry for a single package In some cases, you may want to configure the registry on a per-package basis instead of by scope. This can be done by setting options in the project's configuration. {% aside type="note" title="Authentication" %} All registries set for specific packages must still have authentication tokens set in the root `.npmrc` file for publishing in CI. See [Authenticate to the Registry in CI](#authenticate-to-the-registry-in-ci) for an example. {% /aside %} ### Set the registry in the project configuration The project configuration for Nx Release is in two parts - one for the version step and one for the publish step. #### Update the version step The version step of Nx Release is responsible for determining the new version of the package. If you have set the `version.currentVersionResolver` to 'registry', then Nx Release will check the remote registry for the current version of the package. **Note:** If you do not use the 'registry' current version resolver, then this step is not needed. To set custom registry options for the current version lookup, add the registry and/or tag to the `currentVersionResolverMetadata` in the project configuration: ```json // project.json { "name": "pkg-5", "sourceRoot": "...", "targets": { ... }, "release": { "version": { "currentVersionResolverMetadata": { "registry": "https://my-unique-registry.com/", "tag": "next" } } } } ``` #### Update the publish step The publish step of Nx Release is responsible for publishing the package to the registry. To set custom registry options for publishing, you can add the `registry` and/or `tag` options for the `nx-release-publish` target in the project configuration: ```json // project.json { "name": "pkg-5", "sourceRoot": "...", "targets": { ..., "nx-release-publish": { "options": { "registry": "https://my-unique-registry.com/", "tag": "next" } } } } ``` ### Set the registry in the package manifest It is not recommended to set the registry for a package in the `publishConfig` property of its `package.json` file. `npm publish` will always prefer the registry from the `publishConfig` over the `--registry` argument. Because of this, the `--registry` CLI and programmatic API options of Nx Release will no longer be able to override the registry for purposes such as publishing locally for end to end testing. {% aside type="tip" title="Test publishing against a local registry" %} To verify your registry configuration without publishing anything real, run a local Verdaccio registry and point `nx release` at it. See [update the local registry setup](/docs/guides/nx-release/update-local-registry-setup). {% /aside %} --- ## Customize Conventional Commit Types [Nx release](/docs/features/manage-releases) allows you to leverage the [conventional commits](/docs/guides/nx-release/automatically-version-with-conventional-commits) standard to automatically determine the next version increment. By default, this results in: - `feat(...)` triggering a minor version bump (`1.?.0`) - `fix(...)` triggering a patch version bump (`1.?.x`) - `BREAKING CHANGE` in the footer of the commit message or with an exclamation mark after the commit type (`fix(...)!`) triggers a major version bump (`?.0.0`) {% aside type="note" title="No changes detected" %} If Nx Release does not find any relevant commits since the last release, it will skip releasing a new version. This works with [independent releases](/docs/guides/nx-release/release-projects-independently) as well, allowing for only some projects to be released while others are skipped. {% /aside %} However, you can customize how Nx interprets these conventional commits, for both **versioning** and **changelog** generation. ## Disable a commit type for versioning and changelog generation To disable a commit type, set it to `false`. ```json // nx.json { "release": { "conventionalCommits": { "types": { // disable the docs type for versioning and in the changelog "docs": false, ... } } } } ``` If you just want to disable a commit type for versioning, but still want it to appear in the changelog, set `semverBump` to `none`. ```json // nx.json { "release": { "conventionalCommits": { "types": { // disable the docs type for versioning, but still include it in the changelog "docs": { "semverBump": "none", ... }, ... } } } } ``` ## Changing the type of semver version bump Assume you'd like `docs(...)` commit types to cause a `patch` version bump. You can define that as follows: ```json // nx.json { "release": { "conventionalCommits": { "types": { "docs": { "semverBump": "patch", ... }, } } } } ``` ## Renaming the changelog section for a commit type To rename the changelog section for a commit type, set the `title` property. ```json // nx.json { "release": { "conventionalCommits": { "types": { ... "docs": { ... "changelog": { "title": "Documentation Changes" } }, ... } } } } ``` ## Hiding a commit type from the changelog To hide a commit type from the changelog, set `changelog` to `false`. ```json // nx.json { "release": { "conventionalCommits": { "types": { ... "chore": { "changelog": false }, ... } } } } ``` Alternatively, you can set `hidden` to `true` to achieve the same result. ```json // nx.json { "release": { "conventionalCommits": { "types": { ... "chore": { "changelog": { "hidden": true } }, ... } } } } ``` ## Defining non-standard commit types If you want to use custom, non-standard conventional commit types, you can define them in the `types` object. If you don't specify a `semverBump`, Nx will default to `patch`. ```json // nx.json { "release": { "conventionalCommits": { "types": { "awesome": {} } } } } ``` ## Including invalid commits in the changelog Nx Release ignores all commits that do not conform to the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) standard by default. A special `__INVALID__` type is available in situations where you want to process invalid messages. {% aside type="caution" title="Invalid != Unmatched" %} This type will only include **invalid** commits. _e.g. those that do not follow the `: ...` format._ Commits that are otherwise valid, but with a type that is not enabled, will not be matched by this group. {% /aside %} This can be useful in cases where you have not managed to be consistent with your use of the Conventional Commits standard (e.g. when applying it retroactively to an existing codebase) but still want a changelog to be generated with the contents of each commit message and/or for invalid commits to still affect project versioning. {% aside type="note" title="Alternative to Conventional Commits" %} If you cannot adhere to the Conventional Commits standard for your commits, file based versioning via Nx Release Version Plans could be a good alternative for managing your releases. See our docs on [File Based Versioning](/docs/guides/nx-release/file-based-versioning-version-plans) for more information. {% /aside %} ```json // nx.json { "release": { "conventionalCommits": { "types": { "__INVALID__": { "semverBump": "patch", // Note: the default is "none" "changelog": { "title": "Uncategorized changes" } } } } } } ``` --- ## File Based Versioning (Version Plans) Tools such as Changesets and Beachball helped popularize the concept of tracking the desired semver version bump in a separate file on disk (which is committed to your repository alongside your code changes). This has the advantage of separating the desired bump from your git commits themselves, which can be very useful if you are not able to enforce that all contributors follow a strict commit message format ([e.g. Conventional Commits](/docs/guides/nx-release/automatically-version-with-conventional-commits)), or if you want multiple commits to be included in the same version bump and therefore not map commits 1:1 with changelog entries. Nx release supports file based versioning as a first class use-case through a feature called "version plans". The idea behind the name is that you are creating a _plan_ to version; a plan which will be _applied_ sometime in the future when you actually invoke the `nx release` CLI or programmatic API. Therefore you can think about version plans as having two main processes: - creating version plans and - applying version plans. Both in this recipe, but first we need to enable the feature itself. ## Enable version plans To enable version plans as a feature in your workspace, set `release.versionPlans` to `true` in `nx.json`: ```jsonc // nx.json { "release": { "versionPlans": true, // other release config such as projects to include in releases etc // projects: ["packages/**/*"] // ... }, } ``` You can also enable or disable this for specific release groups by setting the property at the group level if you don't want to apply it to all matching projects in your workspace. ## Create version plans Version plan files live in the `.nx/version-plans/` directory within your workspace (which needs to be tracked by git, so ensure that you are not ignoring the whole `.nx` directory, and instead only the `.nx/workspace-data` and `.nx/cache` directories). The files themselves are written in markdown (`.md` files) and contain Front Matter YAML metadata at the top of the file. The Front Matter YAML section is denoted via triple dashes `---` at the start and end of the section. For example: ```md ## // .nx/version-plans/version-plan-1723732065047.md # # FRONT MATTER YAML HERE # --- # # Regular markdown here # ``` We leverage the Front Matter YAML section to store a mapping of project or release group names to desired semver bump types. The general markdown section represents the description of the change(s) made that will be used in any relevant `CHANGELOG.md` files that are generated later at release time. For example, the following Front Matter YAML section specifies that the `my-app` project should have a `minor` version bump and describes the changes (again, note that there are no constraints on the format of the description, it can contain multiple lines, paragraphs etc): ```md ## // .nx/version-plans/version-plan-1723732065047.md ## my-app: minor This is an awesome change! A new paragraph describing the change in greater detail. All of this will be included in the CHANGELOG.md. All of this structure within the markdown section is optional and flexible. ``` Any number of different projects and different desired semver bumps can be combined within a single version plan file (which represents one change and therefore changelog entry, if applicable). For example: ```md ## // .nx/version-plans/version-plan-1723732065047.md my-app: minor my-lib: patch release-group-a: major --- One change that affects multiple projects and release groups. ``` The project or release group names specified in the Front Matter YAML section must match the names of the projects and/or release groups in your workspace. If a project or release group is not found, an error will be thrown when applying the version plan as part of running `nx release`. {% aside type="note" title="Single Version for All Packages" %} If you use a single version for all your packages (see [Release projects independetly](/docs/guides/nx-release/release-projects-independently)) your version plan file might look like this: ```md ## // .nx/version-plans/version-plan-1723732065047.md ## **default**: minor This is an awesome change! ``` While you could still specify the name of the project it is redundant in this case because all projects will be bumped to the same version. {% /aside %} Because these are just files, they can be created manually or by any custom scripts you may wish to write. They simply have to follow the guidance above around structure, location (`./.nx/version-plans/`) and naming (`.md` extension). The exact file name does not matter, it just needs to be unique within the `.nx/version-plans/` directory. To make things easier, Nx release comes with a built in command to help you generate valid version plan files. See the [`nx release plan`](/docs/reference/nx-commands#nx-release-plan) CLI reference for all available options. ```shell nx release plan ``` When you run this command you will receive a series of interactive prompts which guide you through the process of creating a version plan file. It will generate a unique name for you and ensure it is written to the correct location. ## Apply version plans at release time Using version plans does not change how versioning, changelog generation and publishing is invoked, you can still use the `nx release` CLI or programmatic API as you would for any other versioning strategy. The only difference is that Nx release will know to reference your version plan files as the source of truth for the desired version bumps. You still retain the same control around resolving the current version (disk vs registry vs git tags) however you want, and other configuration options around things like git operations are all still applicable. When you run `nx release` or use the programmatic API, Nx will look for version plan files in the `.nx/version-plans/` directory and apply the desired version bumps to the projects and release groups specified in the Front Matter YAML section of each file. If a project or release group is not found, an error will be thrown and the release will not proceed. Once a particular version plan has been applied it will be deleted from the `.nx/version-plans/` directory so that it does not inadvertently get applied again in the future. The deleted file will be staged and committed alongside your other changed files that were modified directly as part of the release command (depending on your Nx release configuration). ## Ensure that version plans exist for relevant changes When making changes to your codebase and using version plans as your versioning strategy it is likely that you will want to ensure that a version plan file exists for the changes you are making. Attempting to keep track of this manually as a part of pull request reviews can be error prone and time consuming, therefore Nx release provides a `nx release plan:check` command which can be used to ensure that a version plan file exists for the changes you are making. ```shell nx release plan:check ``` Running this command will analyze the changed files (supporting the same options you may be familiar with from `nx affected`, such as `--base`, `--head`, `--files`, `--uncommitted`, etc) and then determine which projects have been "touched" as a result. Note that it is specifically touched projects, and not affected in this case, because only directly changed projects are relevant for versioning. The side-effects of versioning independently released dependents are handled by the release process itself (controllable via the `version.updateDependents` option). ### Running release plan:check in CI As mentioned, `nx release plan:check` supports the same options as `nx affected` for determining the range of commits to consider. Therefore, in CI, you must also ensure that the base and head are set appropriately just like you would for `nx affected`. For GitHub Actions, we provide a utility action to do this for you: ```yaml # ...other steps - uses: nrwl/nx-set-shas@v5 # ...other steps including the use of `nx release plan:check` ``` For CircleCI, you can reference our custom orb as a step: ```yaml # ...other steps - nx/set-shas # ...other steps including the use of `nx release plan:check` ``` You can read more about these utilities and why they are needed on their respective READMEs: - https://github.com/nrwl/nx-set-shas?tab=readme-ov-file#background - https://github.com/nrwl/nx-orb#background Nx release will compare the touched projects to the projects and release groups that are specified in the version plan files in the `.nx/version-plans/` directory. If a version plan file does not exist, the command will print an error message and return a non-zero exit code, which can be used to fail CI builds or other automation. By default, all files that have changed are considered, but you may not want all files under a project to require a version plan be created for them. For example, you may wish to ignore test only files from consideration from this check. The way you can achieve this is by setting version plans to be a configuration object instead of a boolean, and set the `ignorePatternsForPlanCheck` property to an array of glob patterns that should be ignored when checking for version plans. For example: ```jsonc { "release": { "versionPlans": { "ignorePatternsForPlanCheck": ["**/*.spec.ts"], }, }, } ``` {% aside type="caution" title="Important: Pattern Syntax" %} The `ignorePatternsForPlanCheck` patterns follow [gitignore semantics](https://git-scm.com/docs/gitignore). When using negation patterns (patterns starting with `!`) to "un-ignore" certain files, be aware that: **Working patterns:** - `["**/*.spec.ts"]` - ignore all spec files - `["**/*.ts", "!**/src/**"]` - ignore all .ts files except those in src/ directories **Non-working patterns:** - `["*", "!src/"]` - this does NOT work as expected because `*` in gitignore matches at the root level and negation patterns cannot properly un-ignore nested paths If you want to ignore all files except those in specific directories, use file extension patterns instead of wildcards: ```jsonc { "release": { "versionPlans": { "ignorePatternsForPlanCheck": ["**/*.ts", "**/*.json", "!**/src/**"], }, }, } ``` {% /aside %} To see more details about the changed files that were detected and the filtering logic that was used to determine the ultimately changed projects behind the scenes, you can pass `--verbose` to the command: ```shell nx release plan:check --verbose ``` --- ## Programmatic API {% aside type="note" title="Learn about Nx Release" %} Be sure to read our introduction to [Nx Release](/docs/features/manage-releases) to understand the basics of how Nx Release works and the different phases of a release before moving onto the programmatic API. {% /aside %} A powerful feature of Nx Release is the fact that it is designed to be used via a Node.js programmatic API in addition to the [`nx release`](/docs/reference/nx-commands#nx-release) CLI. Releases are a hugely complex and nuanced process, filled with many special cases and idiosyncratic preferences, and it is impossible for a CLI to be able to support all of them out of the box. By having a first-class programmatic API, you can go beyond the CLI and create custom release workflows that are highly dynamic and tailored to your specific needs. Just as with the CLI, the programmatic API is broken up into the distinct phases of a release: versioning, changelog generation, and publishing. These are available via the `releaseChangelog`, `releasePublish`, and `releaseVersion` functions, which are importable from the `nx/release` entrypoint. These functions are the exact functions used behind the scenes by the CLI, and so they will read from the "release" config in `nx.json` in just the same way. If you need even more fine grained control over configuration via the programmatic API, see the section on using the [`ReleaseClient` class](#using-the-releaseclient-class) below. ## Using the programmatic API ```ts import { releaseChangelog, releasePublish, releaseVersion } from 'nx/release'; ``` The functions are all asynchronous and return data relevant to their specific phase of the release process. They all support `dryRun` and `verbose` boolean options. For `releaseVersion` and `releaseChangelog`, `dryRun` prevents changes from being applied. For `releasePublish`, `dryRun` is forwarded to the underlying executor (see the [releasePublish section](#releasepublish) for important details). You can inspect their types to see what each one supports in terms of additional config options. {% aside type="caution" title="Project or Group Filtering" %} If you apply project or group filtering to the programmatic API via the `projects` or `groups` options that each function supports, be sure to pass the same filters to all three functions. {% /aside %} ### releaseVersion `releaseVersion` will return a `NxReleaseVersionResult` object containing the following properties: - `workspaceVersion`: The new overall version of the workspace. This is only applicable in cases where all projects are versioned together in a single release group. In all other cases, this will be `null`. - `projectsVersionData`: A map of project names to their version data. The version data is a `VersionData` object, which contains the following properties: - `currentVersion`: The current version of the project. This is the version that the project was at before the versioning process began. - `newVersion`: The new version of the project. - `dockerVersion`: The new version of the project if it is a docker project. - `dependentProjects`: A list of projects that depend on the current project. - `releaseGraph`: The release graph that was generated for the nx release config and workspace data. This can be passed to subsequent operations (changelog, publish) to avoid recomputing and improve performance. ```ts const { workspaceVersion, projectsVersionData, releaseGraph } = await releaseVersion({ // E.g. if releasing a specific known version // otherwise if using e.g. conventional-commits this is not needed specifier: '1.0.0', dryRun: true, verbose: true, // ... other options }); console.log(workspaceVersion); console.log(projectsVersionData); console.log(releaseGraph); ``` ### releaseChangelog `releaseChangelog` will return a `NxReleaseChangelogResult` object containing the following properties: - `workspaceChangelog`: The changelog data for the workspace, if applicable based on the nx release config. - `releaseVersion`: Relevant version data for the new changelog entry. - `contents`: The changelog entry contents. - `projectChangelogs`: A map of project names to their changelog data, if applicable based on the nx release config. - `releaseVersion`: Relevant version data for the new changelog entry. - `contents`: The changelog entry contents. ```ts const { workspaceChangelog, projectChangelogs } = await releaseChangelog({ // Re-use the existing release graph from the releaseVersion // call (if applicable) to avoid recomputing in each subcommand releaseGraph, // NOTE: One of either version or versionData must be provided versionData: projectsVersionData, // Pass the detailed project version data from the releaseVersion call version: workspaceVersion, // Pass the new workspace version from the releaseVersion call dryRun: true, verbose: true, // ... other options }); ``` ### releasePublish `releasePublish` will return a `PublishProjectsResult` object, which is a map of project names to their publish result which is a simple object with a `code` property representing the exit code of the publish operation for the project. {% aside type="caution" title="dryRun Behavior" %} Unlike `releaseVersion` and `releaseChangelog`, the `dryRun` option for `releasePublish` does **not** prevent the underlying commands from being executed. Instead, the `dryRun` flag is forwarded to the `nx-release-publish` executor as an option, and the `NX_DRY_RUN` environment variable is set to `'true'`. The built-in `@nx/js:release-publish` executor handles this correctly and will skip actual publishing when `dryRun` is `true`. However, **custom `nx-release-publish` executors must implement `dryRun` support themselves** by checking either the `dryRun` option or the `NX_DRY_RUN` environment variable. {% /aside %} ```ts const publishResults = await releasePublish({ // Re-use the existing release graph from the releaseVersion // call (if applicable) to avoid recomputing in each subcommand releaseGraph, dryRun: true, verbose: true, // ... other options }); ``` You can optionally pass through the version data (e.g. if you are using a custom publish executor that needs to be aware of versions). It will then be provided to the publish executor options as `nxReleaseVersionData` and can be accessed in the publish executor options like any other option. ```ts const publishResults = await releasePublish({ releaseGraph, versionData: projectsVersionData, dryRun: true, verbose: true, }); ``` NOTE: Passing `versionData` to `releasePublish` is not required for the default @nx/js publish executor. It is recommended to use the publishResults to determine the overall success or failure of the release process, for example: ```ts process.exit( Object.values(publishResults).every((result) => result.code === 0) ? 0 : 1 ); ``` ## Example release script How you compose these functions in your release script is of course entirely up to you, and you may even want to break them up into multiple files depending on your use-case. The below is purely an example of you might compose them together into one holistic release script which uses `yargs` to parse script options from the command line (nx release does not require `yargs`, it is simply a common choice for this use-case, you can parse arguments however you wish). ```ts // scripts/release.ts import { releaseChangelog, releasePublish, releaseVersion } from 'nx/release'; import * as yargs from 'yargs'; (async () => { const options = await yargs .version(false) // don't use the default meaning of version in yargs .option('version', { description: 'Explicit version specifier to use, if overriding conventional commits', type: 'string', }) .option('dryRun', { alias: 'd', description: 'Whether or not to perform a dry-run of the release process, defaults to true', type: 'boolean', default: true, }) .option('verbose', { description: 'Whether or not to enable verbose logging, defaults to false', type: 'boolean', default: false, }) .parseAsync(); const { workspaceVersion, projectsVersionData, releaseGraph } = await releaseVersion({ specifier: options.version, dryRun: options.dryRun, verbose: options.verbose, }); await releaseChangelog({ releaseGraph, // Re-use the existing release graph to avoid recomputing in each subcommand versionData: projectsVersionData, version: workspaceVersion, dryRun: options.dryRun, verbose: options.verbose, }); // publishResults contains a map of project names and their exit codes const publishResults = await releasePublish({ releaseGraph, // Re-use the existing release graph to avoid recomputing in each subcommand dryRun: options.dryRun, verbose: options.verbose, }); process.exit( Object.values(publishResults).every((result) => result.code === 0) ? 0 : 1 ); })(); ``` To perform a dry-run of version `1.0.0`, you would therefore run the script like so: ```sh npx tsx scripts/release.ts --version 1.0.0 ``` (Or by using `ts-node` or any other tool you prefer to run TS scripts.) ## Using the `ReleaseClient` class The standalone functions that we covered in the previous sections are actually just bound methods of the `ReleaseClient` class that has been pre-instantiated for you. For an extra layer of control, you can import the `ReleaseClient` directly instead of the standalone functions and use your instance's methods for versioning, changelog generation, and publishing. The reason you might want to do this is configuration. The `ReleaseClient` constructor allows you to either override release configuration found in `nx.json`, or completely replace it. ```ts import { ReleaseClient } from 'nx/release'; const releaseClientWithMergedConfig = new ReleaseClient( { projects: ['project-1', 'project-2'], // ... more nx release config options }, false // Do NOT ignore nx.json config, merge whatever was given in the first parameter with it ); const releaseClientWithIsolatedConfig = new ReleaseClient( { projects: ['project-1', 'project-2'], // ... more nx release config options }, true // Ignore nx.json config, only the configuration given in the first parameter will be used ); ``` Ignoring the Nx Release configurations in `nx.json` can be useful for cases where you have a large, complex workspace and your script only needs to focus on a specific subset in a granular way. One such example would be a script that only focuses on changelog generation for a specific project or projects and does not want to invoke any versioning or publishing logic. ### Using the `ReleaseClient` Once you have the instantiated `ReleaseClient` class, the usage pattern is the same as with the standalone functions. ```ts import { ReleaseClient } from 'nx/release'; const releaseClient = new ReleaseClient({}); const { workspaceVersion, projectsVersionData, releaseGraph } = await releaseClient.releaseVersion({ // ... options }); const { workspaceChangelog, projectChangelogs } = await releaseClient.releaseChangelog({ releaseGraph, // ... other options }); const publishResults = await releaseClient.releasePublish({ releaseGraph, // ... other options }); ``` --- ## Publish in CI/CD Nx Release makes it easy to move your publishing process into your CI/CD pipeline across different package ecosystems. ## General concepts ### Automatically skip publishing locally When running `nx release`, after the version updates and changelog generation, you will be prompted with the following question: ```text {% frame="terminal" title="nx release" %} ... ? Do you want to publish these versions? (y/N) › ``` To move publishing into an automated pipeline, you will want to skip publishing when running [`nx release`](/docs/reference/nx-commands#nx-release) locally. To do this automatically, use the `--skip-publish` flag: ```text {% frame="terminal" title="nx release --skip-publish" %} ... Skipped publishing packages. ``` ### Use the publish subcommand Nx Release provides a publishing subcommand ([`nx release publish`](/docs/reference/nx-commands#nx-release-publish)) that performs just the publishing step. Use this in your CI/CD pipeline to publish the packages. ```text {% frame="terminal" title="nx release publish" %} NX Running target nx-release-publish for 3 projects: - pkg-1 - pkg-2 - pkg-3 ... ``` ## Publishing npm packages ### Example npm publish output ```text {% frame="terminal" title="nx release publish" %} NX Running target nx-release-publish for 3 projects: - pkg-1 - pkg-2 - pkg-3 ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— > nx run pkg-1:nx-release-publish 📦 @myorg/pkg-1@0.0.2 === Tarball Contents === 233B README.md 277B package.json 53B src/index.ts 61B src/lib/pkg-1.ts === Tarball Details === name: @myorg/pkg-1 version: 0.0.2 filename: testorg-pkg-1-0.0.2.tgz package size: 531 B unpacked size: 624 B shasum: {shasum} integrity: {integrity} total files: 12 Published to https://registry.npmjs.org with tag "latest" > nx run pkg-2:nx-release-publish 📦 @myorg/pkg-2@0.0.2 === Tarball Contents === 233B README.md 277B package.json 53B src/index.ts 61B src/lib/pkg-2.ts === Tarball Details === name: @myorg/pkg-2 version: 0.0.2 filename: testorg-pkg-2-0.0.2.tgz package size: 531 B unpacked size: 624 B shasum: {shasum} integrity: {integrity} total files: 12 Published to https://registry.npmjs.org with tag "latest" > nx run pkg-3:nx-release-publish 📦 @myorg/pkg-3@0.0.2 === Tarball Contents === 233B README.md 277B package.json 53B src/index.ts 61B src/lib/pkg-3.ts === Tarball Details === name: @myorg/pkg-3 version: 0.0.2 filename: testorg-pkg-3-0.0.2.tgz package size: 531 B unpacked size: 624 B shasum: {shasum} integrity: {integrity} total files: 12 Published to https://registry.npmjs.org with tag "latest" ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— NX Successfully ran target nx-release-publish for 3 projects ``` ### npm publishing in GitHub actions A common way to automate publishing NPM packages is via GitHub Actions. An example of a publish workflow is as follows: ```yaml // ./.github/workflows/publish.yml name: Publish on: push: tags: - v*.*.* jobs: test: name: Publish runs-on: ubuntu-latest permissions: contents: read id-token: write # needed for provenance data generation timeout-minutes: 10 steps: - name: Checkout repository uses: actions/checkout@v7 with: fetch-depth: 0 filter: tree:0 - name: Install Node uses: actions/setup-node@v6 with: node-version: 24 registry-url: https://registry.npmjs.org/ - name: Install dependencies run: npm install shell: bash - name: Print Environment Info run: npx nx report shell: bash - name: Publish packages run: npx nx release publish shell: bash env: NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} NPM_CONFIG_PROVENANCE: true ``` This workflow will install node, install npm dependencies, then run `nx release publish` to publish the packages. It will run on every push to the repository that creates a tag that matches the pattern `v*.*.*`. A release process using this workflow is as follows: 1. Run `nx release --skip-publish` locally. This will create a commit with the version and changelog updates, then create a tag for the new version. 2. Push the changes (including the new tag) to the remote repository with `git push && git push --tags`. 3. The publish workflow will automatically trigger and publish the packages to the npm registry. {% aside type="note" title="This template is designed for fixed versioning" %} The example workflow above triggers on a single tag pattern (`v*.*.*`), which works best when all packages share the same version (**fixed versioning strategy**). With fixed versioning, `nx release` creates one tag per release. If you're using **independent versioning** (where each project has its own version), see the [Considerations for Independent Versioning](#considerations-for-independent-versioning) section below. {% /aside %} ### Considerations for independent versioning When using independent versioning, `nx release` creates a separate tag for each project being released (e.g., `pkg-1@1.0.0`, `pkg-2@2.1.0`). This introduces some challenges with tag-triggered workflows: #### GitHub tag event limitation GitHub Actions has an important limitation: **workflows triggered by the `push` event will not run if more than 3 tags are created at once**. This means if you run `nx release` and it creates tags for 4 or more projects, the publish workflow will not be triggered. This limitation is documented in [GitHub workflow events documentation](https://docs.github.com/en/actions/reference/events-that-trigger-workflows#create). #### Alternative approaches for independent versioning There are several ways to work around this limitation: **Option 1: Use `workflow_dispatch` with Manual Trigger** Instead of triggering on tags, use a manual workflow dispatch after pushing your release: ```yaml // .github/workflows/publish.yml name: Publish on: workflow_dispatch: inputs: dry-run: description: 'Run in dry-run mode (no actual publishing)' required: false default: 'false' type: boolean jobs: publish: name: Publish runs-on: ubuntu-latest permissions: contents: read id-token: write steps: - name: Checkout repository uses: actions/checkout@v7 with: fetch-depth: 0 - name: Install Node uses: actions/setup-node@v6 with: node-version: 24 registry-url: https://registry.npmjs.org/ - name: Install dependencies run: npm install - name: Publish packages run: npx nx release publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} NPM_CONFIG_PROVENANCE: true ``` With this approach: 1. Run `nx release --skip-publish` locally and push the commits and tags. 2. Manually trigger the workflow from the GitHub Actions UI after the push completes. **Option 2: Trigger on Push to Main Branch** Trigger the workflow when the release commit is pushed to your main branch: ```yaml on: push: branches: - main paths: - '**/package.json' ``` This approach requires additional logic in your workflow to determine if a release was just made (e.g., by checking the commit message or comparing versions). **Option 3: Push Tags in Batches** If you prefer tag-triggered workflows, push tags in smaller batches (3 or fewer at a time) to ensure each push triggers the workflow. This can be automated with a script that pushes tags individually or in small groups. ### Configure the NODE_AUTH_TOKEN The `NODE_AUTH_TOKEN` environment variable is needed to authenticate with the npm registry. In the above workflow, it is passed into the Publish packages step via a [GitHub Secret](https://docs.github.com/en/actions/reference/encrypted-secrets). #### Generate a NODE_AUTH_TOKEN for npm To generate the correct `NODE_AUTH_TOKEN` for the npmJS registry specifically, first login to [https://www.npmjs.com/](https://www.npmjs.com/). Select your profile icon, then navigate to "Access Tokens". Generate a new Granular Access Token. Ensure that the token has read and write access to both the packages you are publishing and their organization (if applicable). Copy the generated token and add it as a secret to your GitHub repository. #### Add the NODE_AUTH_TOKEN to GitHub secrets To add the token as a secret to your GitHub repository, navigate to your repository, then select "Settings" > "Secrets and Variables" > "Actions". Add a new Repository Secret with the name `NPM_ACCESS_TOKEN` and the value of the token you generated in the previous step. Note: The `NPM_ACCESS_TOKEN` name is not important other than that it matches the usage in the workflow: ```yaml - name: Publish packages run: npx nx release publish shell: bash env: NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} NPM_CONFIG_PROVENANCE: true ``` ### npm provenance To verify your packages with [npm provenance](https://docs.npmjs.com/generating-provenance-statements), set the `NPM_CONFIG_PROVENANCE` environment variable to `true` in the step where `nx release publish` is performed. The workflow will also need the `id-token: write` permission to generate the provenance data: ```yaml jobs: test: name: Publish runs-on: ubuntu-latest permissions: contents: read id-token: write # needed for provenance data generation ``` ```yaml - name: Publish packages run: npx nx release publish shell: bash env: NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} NPM_CONFIG_PROVENANCE: true ``` ## Publishing Docker Images {% badge variant="caution" text="experimental" /%} Docker support in Nx is currently experimental and may undergo breaking changes without following semantic versioning. {% aside type="note" title="Nx Cloud Agents Compatibility" %} Docker operations in `nx release` are currently supported in standard CI/CD environments like GitHub Actions, GitLab CI, and Jenkins. For Nx Cloud Agents compatibility, please contact [Nx Enterprise support](https://nx.dev/contact/sales) to explore available options for your team. {% /aside %} When using Nx Release with Docker images, the publishing process differs from npm packages. Docker images are built with the `npx nx run-many -t docker:build` command, which is the default for [`preVersionCommand`](/docs/guides/nx-release/build-before-versioning#build-before-docker-versioning) in `nx.json`. You may also run the build command manually before running `nx release`. After the images are built, they are tagged during the versioning phase, then pushed to a registry during the publish phase. ### Docker registry authentication Before publishing Docker images, ensure you're authenticated with your Docker registry: ```yaml // .github/workflows/publish.yml - name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} - name: Build and tag Docker images run: npx nx release version --dockerVersionScheme=production - name: Publish Docker images run: npx nx release publish ``` See the [`nx release version`](/docs/reference/nx-commands#nx-release-version) and [`nx release publish`](/docs/reference/nx-commands#nx-release-publish) CLI references for all available options. For changelogs, you can run [`nx release changelog`](/docs/reference/nx-commands#nx-release-changelog) `` locally with the new version from the pipeline. For example, if the new version is `2501.01.be49ad6` you would run `npx nx release changelog 2501.01.be49ad6`. This will create or update the `CHANGELOG.md` files in your projects. ### Using different registries Configure alternative registries in your `nx.json`: ```jsonc // nx.json { "release": { "docker": { "registryUrl": "ghcr.io", // GitHub Container Registry }, }, } ``` ### Example GitHub Actions Workflow for Docker ```yaml // .github/workflows/docker-publish.yml name: Docker Publish on: push: branches: [main] jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: 24 - name: Install dependencies run: npm ci - name: Build applications run: npx nx run-many -t build - name: Login to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} - name: Build and tag Docker images run: npx nx release version --dockerVersionScheme=production - name: Publish Docker images run: npx nx release publish ``` --- ## Release Groups Nx supports workspaces of any size and scale, and that means that projects can often be worked on in the same Nx workspace that have very different release requirements. Nx release supports the concept of release groups to allow you to configure different subsets of projects in different ways. Importantly, projects in different release groups can still depend on each other, and nx release can automatically handle updating dependencies and dependents across any number of group boundaries. {% aside type="note" title="Default Group" %} Technically, even if you have no nx release configuration at all, you are always dealing with release groups. Behind the scenes, if no release groups are configured, Nx will automatically create an implicit `__default__` release group for you that includes all projects in the workspace that are able to be released. When you use the `"projects"` property in the `"release"` config at the top level, you are really adjusting the projects that are present in that `__default__` group. {% /aside %} ## Understanding release groups Release groups provide a way to organize your projects based on their release requirements. Each group can have its own: - **Projects Relationship** - Whether projects within the group are versioned independently or in lock step (fixed) - **Version configuration** - Custom conventional commits, version prefixes, and more - **Changelog configuration** - Different changelog formats and locations - **Release tag** - Various options related to the git tags that influence the release process - **Docker configuration** - Specific Docker versioning schemes ## Creating release groups To create release groups, define them in your `nx.json` file under the `release.groups` property: ```jsonc // nx.json { "release": { "groups": { "backend": { "projects": ["api", "auth-service", "payment-service"], "projectsRelationship": "fixed", }, "frontend": { "projects": ["web-app", "mobile-app"], "projectsRelationship": "independent", }, }, }, } ``` ### Project selection You can specify projects for a group using project matchers that you may already be familiar with from `nx run-many` command filters: - **Explicit project names**: `"projects": ["project-a", "project-b"]` - **Glob patterns**: `"projects": ["packages/shared-*"]` - **Tag references**: `"projects": ["tag:npm-public"]` - **Negation**: `"projects": ["!ignore-me"]` {% aside type="note" title="Project Uniqueness" %} Each project can only belong to one release group. If you try to assign a project to multiple groups, Nx will throw an error during configuration validation. {% /aside %} ## Projects relationship The `projectsRelationship` property determines how projects within a group are released: ### Fixed When `projectsRelationship` is set to `"fixed"` (the default): - All projects in the group share the same version number - When one project requires a version bump, all projects in the group are versioned together - Dependencies between projects in the group are always automatically updated - Each project will receive a changelog entry, with a specific (configurable) message for those projects that were only bumped to align with the group version ```jsonc // nx.json { "release": { "groups": { "shared-libraries": { "projects": ["ui-components", "utils", "data-access"], // this is also the default and can be omitted "projectsRelationship": "fixed", // ... other group configuration options ... }, }, }, } ``` ### Independent When `projectsRelationship` is set to `"independent"`: - Each project in the group maintains its own version - Projects are versioned only when they have changes (apart from the influence of ["updateDependents" configuration](#update-dependents), learn more below) - Changelog entries are generated only when there are direct or indirect ("updateDependents") changes to the project ```jsonc // nx.json { "release": { "groups": { "microservices": { "projects": ["user-service", "order-service", "inventory-service"], "projectsRelationship": "independent", // ... other group configuration options ... }, }, }, } ``` ## Group-specific configuration Each release group can override the root `"release"` configuration with its own settings: ### Version configuration Customize versioning behavior per group: ```jsonc { "release": { "version": { "conventionalCommits": false, "versionPrefix": "~", "updateDependents": "always", }, "groups": { "npm-packages": { "projects": ["package-*"], "version": { // These properties override the root version //configuration for this specific group "conventionalCommits": true, "versionPrefix": "^", "updateDependents": "auto", }, }, }, }, } ``` ### Changelog configuration Configure changelog generation per group: ```jsonc { "release": { "changelog": { "projectChangelogs": { "file": false, // ... other changelog configuration options ... }, }, "groups": { "public-apis": { "projects": ["api-*"], // overrides at the group level "changelog": { "file": "{projectRoot}/CHANGELOG.md", "createRelease": "github", "renderer": "@my-org/custom-changelog-renderer", }, }, }, }, } ``` ### Release tag patterns Customize how relevant git tags should be discovered and created for each group independently: ```jsonc { "release": { "groups": { "backend": { "projects": ["backend-*"], "releaseTag": { "pattern": "backend-{version}", "requireSemver": true, }, }, "frontend": { "projects": ["frontend-*"], "releaseTag": { "pattern": "frontend-{version}", }, }, }, }, } ``` ## Update dependents For independently versioned projects, regardless of whether they are in the same release group or not, we will have to consider what happens when those projects depend on one another. ```jsonc // nx.json { "release": { "groups": { "group1": { // project-a depends on project-b, even though it // happens to be in a different release group here "projects": ["project-a"], }, "group2": { // project-a depends on project-b, even though it // happens to be in a different release group here "projects": ["project-b"], }, }, }, } ``` Nx can handle this cascade of updates automatically, across any number of release group boundaries, and this behavior is configurable via the `version.updateDependents` option. See the [Update Dependents](/docs/guides/nx-release/update-dependents) guide for more details. ## More examples ### Mixed relationship groups You can have different relationship types for different groups in the same workspace: ```jsonc { "release": { "groups": { "platform": { "projects": ["core", "common", "shared"], "projectsRelationship": "fixed", }, "applications": { "projects": ["app-*"], "projectsRelationship": "independent", }, }, }, } ``` ### Group-specific pre-version commands Run different build or preparation commands for each group: ```jsonc { "release": { "groups": { "compiled-packages": { "projects": ["lib-*"], "version": { "groupPreVersionCommand": "nx run-many -t build --projects=...", }, }, "documentation": { "projects": ["docs-*"], "version": { "groupPreVersionCommand": "nx run-many -t generate-docs --projects=...", }, }, }, }, } ``` ### Version plans with groups When using [version plans](/docs/guides/nx-release/file-based-versioning-version-plans), you can target specific groups: ```jsonc { "release": { "groups": { "backend": { "projects": ["api-*"], "versionPlans": true, }, "frontend": { "projects": ["ui-*"], // version plans is not enabled here and // it's not set at the root level either }, }, }, } ``` ## Processing order Nx Release processes groups in topological order based on their dependencies: 1. Groups with no dependencies are processed first 2. Groups that depend on already-processed groups are processed next 3. Within each group, projects are also processed in topological order This ensures that: - Dependencies are always versioned before their dependents - Version updates cascade correctly through the `ReleaseGraph` that is constructed behind the scenes NOTE: Circular dependencies are not recommended, but can be tolerated by nx release, as it can sometimes be unavoidable. ## Using filters with groups You can filter which groups or projects to release. See the [`nx release`](/docs/reference/nx-commands#nx-release) CLI reference for all available options. ```bash # Release only a specific group nx release --groups=backend # Release specific independent projects across any groups nx release --projects=api,web-app # Combine with dry-run to preview nx release --groups=frontend --dry-run ``` {% aside type="caution" title="Fixed Groups and Filters" %} When filtering projects in a fixed release group, you must include all projects in that group. You cannot release a subset of projects from a fixed group as they must be versioned together. {% /aside %} ## Next steps - Learn about [conventional commits](/docs/guides/nx-release/automatically-version-with-conventional-commits) for automatic versioning - Explore [version plans](/docs/guides/nx-release/file-based-versioning-version-plans) for file based versioning - Configure [custom registries](/docs/guides/nx-release/configure-custom-registries) for publishing - Set up [CI/CD integration](/docs/guides/nx-release/publish-in-ci-cd) for automated releases --- ## Release Projects Independently Nx Release supports releasing projects independently. This is useful when you have a monorepo with projects that are not released on the same schedule. You can also group projects into multiple release groups for increased flexibility and control. Learn more about [release groups](/docs/guides/nx-release/release-groups). ## Configure independent releases To configure independent releases, add the following property to your `nx.json` file: ```json // nx.json { "release": { "projectsRelationship": "independent" } } ``` ## Differences from fixed releases Nx release will behave differently when configured for independent releases. ### Prompt for multiple version bumps When configured for independent releases, Nx Release will prompt for a version bump for each project that is being released. This allows the version of each project to differ over time. ### Create a git tag for each project Since each project can have a different version, Nx Release will create a git tag for each project that is being released. By default, the tag for each project will follow the pattern `{projectName}@{version}`. For example, if the `pkg-1` project is being released with version `1.1.0`, its git tag will be `pkg-1@1.1.0`. This can still be changed with the `release.releaseTag.pattern` property (Nx 22+) or `release.releaseTagPattern` (Nx < 22) in `nx.json`, but be sure to include `{projectName}` in the pattern so that each generated tag is unique. For example, to generate the tags `release/pkg-1/1.1.0` and `release/pkg-2/1.2.1` for the `pkg-1` and `pkg-2` projects respectively, you would use the following configuration in nx.json: {% tabs syncKey="nx-release-configuration" %} {% tabitem label="Nx 22+" %} ```json // nx.json { "release": { "releaseTag": { "pattern": "release/{projectName}/{version}" } } } ``` {% /tabitem %} {% tabitem label="Nx < 22" %} ```json // nx.json { "release": { "releaseTagPattern": "release/{projectName}/{version}" } } ``` {% /tabitem %} {% /tabs %} See the [`releaseTag.pattern` documentation](/docs/reference/nx-json#release-tag) for more details on how to customize the tag pattern. ### Different commit message structure Even though Nx Release creates a git tag for each project, it will still create a single commit for the entire release. The commit message will still include all of the projects being released with their corresponding version. For example: ```text chore(release): publish - project: pkg-1 1.1.0 - project: pkg-2 1.2.1 - project: pkg-3 2.5.7 ``` ### Changelogs Nx Release will no longer generate and update a workspace level `CHANGELOG.md` file when configured for independent releases. If you still want changelog generation, you will need to enable project level changelogs. These are similar to the workspace level changelog, but they are generated for each project individually and only contain changes for that specific project. They can be configured with the `release.changelog.projectChangelogs` property in `nx.json`. ```json // nx.json { "release": { "changelog": { "projectChangelogs": true } } } ``` Just like with [fixed releases](/docs/kb/release-npm-packages), you can preview changes to the changelog files by running Nx Release with the `--dry-run` option. ## Use the projects filter One of the key benefits of independent releases is the ability to release only a subset of projects. Nx Release supports this with the `--projects` option. See the [`nx release`](/docs/reference/nx-commands#nx-release) CLI reference for all available options. The value is an array of strings, and you can use any of the same specifiers that are supported by `nx run-many`'s [projects filtering](/docs/reference/nx-commands#nx-run-many), such as explicit project names, Nx tags, directories and glob patterns, including negation using the `!` character. A few examples: Release only the `pkg-1` and `pkg-2` projects: ```shell nx release --projects=pkg-1,pkg-2 ``` Release all projects in the `server` directory: ```shell nx release --projects=server/* ``` Release all projects except those in the `ui` directory: ```shell nx release --projects='!ui/*' ``` All other projects in the workspace will be ignored and only those that match the filter will be versioned, have their changelogs updated, and published. ## Update dependents For independently versioned projects, we will have to consider what happens when those projects depend on one another. ```json // nx.json { "release": { "projectsRelationship": "independent", // The projects are independently versioned, but project-a depends on // project-b, so we need to consider the side-effects of updating project-b "projects": ["project-a", "project-b"] } } ``` Nx can handle this cascade of updates automatically, across any number of release group boundaries, and this behavior is configurable via the `version.updateDependents` option. See the [Update Dependents](/docs/guides/nx-release/update-dependents) guide for more details. --- ## Update Dependents When versioning independently maintained projects, we will have to consider what happens when those projects depend on one another. For example, `project-a` might be version `1.0.0` and `project-b` might be version `2.0.0`, where `project-a` depends on `project-b`: `project-a -> project-b`. **In other words, `project-a` is a dependent of `project-b`, and `project-b` is a dependency of `project-a`.** This means that whenever we update `project-b` we need to consider the side-effects of that on `project-a`. There will now be a dependency reference, e.g. in a manifest file such as `package.json` for the TypeScript/JavaScript ecosystem, that needs to be updated to reflect the new version of `project-b`. Nx can handle this cascade of updates automatically, across any number of [Release Group](/docs/guides/nx-release/release-groups) boundaries, and this behavior is configurable via the `version.updateDependents` option. ### Update dependents configuration - **`"always"`** (introduced in v22 and now the default): Always update dependents when a dependency is versioned, regardless of which group they belong to, or what filters are applied to the release command/programmatic API. - **`"auto"`**: (old default) Update dependents within the same release group, and only when not filtered out by `--projects` or `--groups` - **`"never"`**: Never automatically update dependents When Nx release detects that a side-effectful bump needs to be made, e.g. in our example to `project-a`, it will update any dependency references in manifest files (e.g. `project-a/package.json`), and bump `project-a`'s own version to the next appropriate patch version. For example, if `project-b` is bumped to version `2.1.0`, `project-a` will be updated to depend on `project-b@2.1.0`, and `project-a` will be bumped to version `1.0.1`. ```json // Before { "name": "project-a", "version": "1.0.0", "dependencies": { "project-b": "2.0.0" } } ``` ```json // After { "name": "project-a", "version": "1.0.1", // Side-effectful patch of project-a "dependencies": { "project-b": "2.1.0" // New version of project-b } } ``` ### Propagating `--preid` to dependent bumps Side-effectful bumps are always plain patch versions by default, even when the project that triggered them is being released as a prerelease. For example, run: ```shell nx release version prepatch --preid rc --projects project-b ``` `project-b` moves from `2.0.0` to `2.0.1-rc.0`, but `project-a` still receives a stable side-effectful patch to `1.0.1`: ```json // project-a after (default behavior) { "name": "project-a", "version": "1.0.1", "dependencies": { "project-b": "2.0.1-rc.0" } } ``` That default reflects a deliberate tradeoff. Consuming a prerelease build of a dependency doesn't necessarily mean the dependent itself is a prerelease, and keeping side-effectful bumps on stable versions allows them to ship without promoting every project up the graph into a prerelease. To propagate the `--preid` value through to dependents, enable `version.applyPreidToDependents` in `nx.json`: ```json { "release": { "version": { "updateDependents": "always", "applyPreidToDependents": true } } } ``` With this option enabled and `--preid rc` set, `project-a` receives a `prepatch` bump using the same preid: ```json // project-a after (applyPreidToDependents: true) { "name": "project-a", "version": "1.0.1-rc.0", "dependencies": { "project-b": "2.0.1-rc.0" } } ``` #### When to enable it Enable `applyPreidToDependents` when: - You publish a set of packages that release together (even if versioned independently) and want a single `nx release ... --preid rc` invocation to produce a consistent prerelease across all of them. - Your downstream users install all affected packages as a set and would be surprised to see a stable-version dependent pick up a prerelease dependency. Leave it off (the default) when a stable `1.0.1` of `project-a` depending on an `rc` of `project-b` is a valid, intentional release for you. Prerelease dependencies shouldn't automatically promote their consumers into prereleases. The option can also be set per release group under `release.groups..version.applyPreidToDependents` to scope the behavior to only certain groups. --- ## Update Your Local Registry Setup to use Nx Release Nx will create a `tools/start-local-registry.ts` script for starting a local registry and publishing packages to it in preparation for running end to end tests. If you have an existing `tools/start-local-registry.ts` script from a previous version of Nx, you should update it to use Nx Release to publish packages to the local registry. This will ensure that newly generated libraries are published appropriately when running end to end tests. ## The previous version The previous version of the `tools/start-local-registry.ts` script used publish targets on each project to publish the packages to the local registry. This is no longer necessary with Nx Release. You can identify the previous version by the `nx run-many` command that publishes the packages: ```typescript /** * This script starts a local registry for e2e testing purposes. * It is meant to be called in jest's globalSetup. */ import { startLocalRegistry } from '@nx/js/plugins/jest/local-registry'; import { execFileSync } from 'child_process'; export default async () => { // local registry target to run const localRegistryTarget = '@demo-plugin-1800/source:local-registry'; // storage folder for the local registry const storage = './tmp/local-registry/storage'; global.stopLocalRegistry = await startLocalRegistry({ localRegistryTarget, storage, verbose: false, }); const nx = require.resolve('nx/bin/nx'); execFileSync( nx, ['run-many', '--targets', 'publish', '--ver', '0.0.0-e2e', '--tag', 'e2e'], { env: process.env, stdio: 'inherit' } ); }; ``` If your script looks like this, you should update it. ## The updated version The updated version of the `tools/start-local-registry.ts` script uses Nx Release to publish the packages to the local registry. This is done by running `releaseVersion` and `releasePublish` functions from `nx/release`. Your updated script should look like this: ```typescript /** * This script starts a local registry for e2e testing purposes. * It is meant to be called in jest's globalSetup. */ import { startLocalRegistry } from '@nx/js/plugins/jest/local-registry'; import { execFileSync } from 'child_process'; import { releasePublish, releaseVersion } from 'nx/release'; export default async () => { // local registry target to run const localRegistryTarget = '@demo-plugin-1800/source:local-registry'; // storage folder for the local registry const storage = './tmp/local-registry/storage'; global.stopLocalRegistry = await startLocalRegistry({ localRegistryTarget, storage, verbose: false, }); await releaseVersion({ specifier: '0.0.0-e2e', stageChanges: false, gitCommit: false, gitTag: false, firstRelease: true, versionActionsOptionsOverrides: { skipLockFileUpdate: true, }, }); await releasePublish({ tag: 'e2e', firstRelease: true, }); }; ``` --- ## Updating Version References in Manifest Files The versioning stage of Nx Release is customizable and programming language agnostic, but some of its capabilities are dictated by the tooling you are using. This is particularly true when it comes to updating version references in manifest files, such as `package.json`. Nx provides the TypeScript/JavaScript (and therefore `package.json`) functionality out of the box, so that is what will be covered in more detail in this recipe. For other ecosystems, please see the documentation of the respective plugins. An important characteristic of Nx release is that it does not directly manipulate your packages in memory before releasing them. This maintains complete transparency between you and the tooling being leveraged to publish your packages, such as `npm publish` or `pnpm publish`, which are leveraged automatically by Nx Release during its publishing phase. The relevance of this will become clear for [Scenario 4 below](#scenario-4-i-want-to-update-package-versions-directly-in-my-source-files-but-use-local-dependency-references-via-fileworkspace). {% aside type="note" title="Breaking Changes in Nx v21" %} In Nx v21, the implementation details of versioning were rewritten to enhance flexibility and allow for better cross-ecosystem support. An automated migration was provided in Nx v21 to update your configuration to the new format when running `nx migrate`. The following examples shows the Nx v21 and later configuration format, you can view the v20 version of the website to see the legacy format. {% /aside %} ## Scenario 1: I want to update semantic version numbers directly in my source package.json files This is the simplest scenario, and default behavior of Nx Release. If you have a TypeScript/JavaScript project which lives at e.g. `packages/my-project` with its package.json at the root of the project, you can run `nx release` or use the programmatic API and it will update the version number and all relevant intra-workspace dependency references in `packages/my-project/package.json` to the new version(s). For example, with the following project structure: {% filetree %} - packages/ - my-project/ - package.json - my-other-project-in-the-monorepo/ - package.json {% /filetree %} And starting point for package.json sources: ```json // packages/my-project/package.json { "name": "my-project", "version": "0.1.1", "dependencies": { "my-other-project-in-the-monorepo": "0.1.1" } } ``` ```json // packages/my-other-project-in-the-monorepo/package.json { "name": "my-other-project-in-the-monorepo", "version": "0.1.1" } ``` When running `nx release` and applying a patch release, the following changes will be made to the source package.json files: ```json // packages/my-project/package.json { "name": "my-project", "version": "0.1.2", "dependencies": { "my-other-project-in-the-monorepo": "0.1.2" } } ``` ```json // packages/my-other-project-in-the-monorepo/package.json { "name": "my-other-project-in-the-monorepo", "version": "0.1.2" } ``` By default, the changes will be staged and committed unless git operations are disabled. ## Scenario 2: I want to publish from a custom dist directory and update references in my both my source and dist package.json files Nx Release has the concept of a "manifest root", which is different than the project root. The manifest root is the directory from which the project is versioned. By default, the manifest root is the project root detected by Nx as we have seen in Scenario 1 above, but the manifest root can be configured independently to be one or more other locations than the project root. As of Nx v21, multiple manifest roots can be configured using the `release.version.manifestRootsToUpdate` option, resulting in multiple manifest files (such as `package.json`) being updated at once for a single project during a the versioning phase. If, for example, we want to build our projects to a centralized `dist/` directory in the Nx workspace, and update both the source and dist package.json files when versioning, we can tell Nx Release to discover it for the versioning and publishing steps by adding the following configuration to the `nx.json` file, or the `project.json` file of relevant projects: ```jsonc // nx.json { "release": { // Ensure that versioning works in both the source and dist directories "version": { // path structures for both the source and dist directories, where {projectRoot} and {projectName} are available placeholders that will be interpolated by Nx "manifestRootsToUpdate": [ "{projectRoot}", // We use the object form of the manifestRootsToUpdate to specify that we want to update the dist package.json files and not preserve the local dependency references (if not using pnpm or bun) { "path": "dist/packages/{projectName}", "preserveLocalDependencyProtocols": false, // (NOT NEEDED WHEN USING pnpm or bun) because we need to ensure our dist package.json files are valid for publishing and the local dependency references such as "workspace:" and "file:" are removed }, ], }, }, "targetDefaults": { // Ensure that publishing works from the dist directory // The nx-release-publish target is added implicitly behind the scenes by Nx Release, and we can therefore configure it in targetDefaults "nx-release-publish": { "options": { // the packageRoot property is specific the TS/JS nx-release-publish implementation, other ecosystem plugins may have different options "packageRoot": "dist/packages/{projectName}", // path structure for your dist directory, where {projectRoot} and {projectName} are available placeholders that will be interpolated by Nx }, }, }, } ``` ## Scenario 3: I want to publish from a custom dist directory and not update references in my source package.json files A slight modification of Scenario 2 above, where we want to publish from a custom dist directory and not update references in our source package.json files. {% aside type="caution" title="The source control tracked package.json files are no longer the source of truth for the package version" %} Because we are no longer updating the version references in the source package.json files, the source control tracked package.json files are no longer the source of truth for the package version. We need to reference git tags or the latest value in the registry as the source of truth for the package version instead. We will also need to handle intra-workspace dependency references in the source package.json files differently using file/workspace references, which will be covered below. {% /aside %} Because our source package.json files are no longer updated during versioning, we will need to handle intra-workspace dependency references in the source package.json files differently. The way to achieve this is by using local `file:` or `workspace:` references in the source package.json files. For example, using our packages from Scenario 1 above, if we want to reference the `my-other-project-in-the-monorepo` project from `my-project`, we can update the source package.json file as follows: ```jsonc // packages/my-project/package.json { "name": "my-project", // note there is no version number in the source package.json file because it will never be updated "dependencies": { "my-other-project-in-the-monorepo": "workspace:*", // or "file:../my-other-project-in-the-monorepo", depending on your preference and which package manager you are using }, } ``` If the package manager we are using is not using pnpm or bun (See Scenario 4 below), we will need to let Nx release know that we want to overwrite the workspace reference with the actual version number when publishing, because since Nx v21 it will preserve them by default. We can do this by setting the `release.version.preserveLocalDependencyProtocols` option to `false` in the `nx.json` file: ```jsonc // nx.json { "release": { // Ensure that versioning works only in the dist directory "version": { "manifestRootsToUpdate": ["dist/packages/{projectName}"], // path structure for your dist directory, where {projectRoot} and {projectName} are available placeholders that will be interpolated by Nx "currentVersionResolver": "git-tag", // or "registry", because we are no longer referencing our source package.json as the source of truth for the current version "preserveLocalDependencyProtocols": false, // (NOT NEEDED WHEN USING pnpm or bun) because we need to ensure our dist package.json files are valid for publishing and the local dependency references are removed }, }, "targetDefaults": { // Ensure that publishing works from the dist directory // The nx-release-publish target is added implicitly behind the scenes by Nx Release, and we can therefore configure it in targetDefaults "nx-release-publish": { "options": { "packageRoot": "dist/packages/{projectName}", // path structure for your dist directory, where {projectRoot} and {projectName} are available placeholders that will be interpolated by Nx }, }, }, } ``` After applying a patch version, our dist package.json will therefore ultimately look like this: ```jsonc // dist/packages/my-project/package.json { "name": "my-project", "version": "0.1.2", // the version number is applied "dependencies": { "my-other-project-in-the-monorepo": "0.1.2", // the dependency reference is updated from the workspace reference to the actual version number (if not using pnpm or bun) }, } ``` This package.json is now valid and ready to be published to the registry with any package manager. ## Scenario 4: I want to update package versions directly in my source files, but use local dependency references via file/workspace {% aside type="caution" title="This scenario is currently only fully supported when your package manager is pnpm or bun" %} pnpm and bun are the only package managers that provide a publish command that both supports dynamically swapping the `file:` and `workspace:*` references with the actual version number at publish time, and provides the customization needed for us to wrap it. `yarn npm publish` does support the replacements but is very limited on customization options. {% /aside %} This is a more advanced scenario because it removes the clean separation of concerns between versioning and publishing. The reason for this is that the `file:` and `workspace:*` references simply have to be replaced with actual version numbers before they are written to the registry, otherwise they will break when a user tries to install the package. If versioning does not replace them, publishing needs to. As mentioned at the start of this recipe, Nx Release intentionally does not manipulate your packages in memory during publishing, so this scenario is only supported when your package manager provides publishing functionality which dynamically swaps the local references. **Currently this is only supported by pnpm and bun.** As of Nx v21, by default, `release.version.preserveLocalDependencyProtocols` is set to `true`, which means that `file:` and `workspace:*` references are preserved. For example, using this source package.json file, when applying a patch release: ```jsonc // packages/my-project/package.json { "name": "my-project", "version": "0.1.2", "dependencies": { "my-other-project-in-the-monorepo": "workspace:*", }, } ``` Nx release will see this and update the "version" number to `0.1.3`, and leave the `workspace:*` reference alone: ```jsonc // packages/my-project/package.json { "name": "my-project", "version": "0.1.3", // our version number is updated as expected "dependencies": { // our workspace dependency reference is preserved "my-other-project-in-the-monorepo": "workspace:*", }, } ``` Again, this is not in a valid state to be published to the registry, and so the publishing step will need to handle this. **This is only supported by pnpm and bun**, in which case Nx Release invokes `pnpm publish` or `bun publish` instead of `npm publish` behind the scenes during publishing, and you will receive a clear error if you attempt to use such a package.json with npm or yarn. --- ## Tasks & Caching {% index_page_cards path="guides/tasks--caching" /%} --- ## Tips and Tricks {% index_page_cards path="guides/tips-n-tricks" /%} --- ## Advanced Update Process This guide describes advanced scenarios when it comes to updating Nx and the workspaces dependencies. It starts with a summary of the [standard update process](/docs/features/automate-updating-dependencies) and continues with those advanced use cases. ## Updating to the latest Nx version The following steps are a summary of the [standard update process](/docs/features/automate-updating-dependencies). For more information on each step, please visit that page. ### Step 1: Updating dependencies and generating migrations First, run the `migrate` command: ```shell nx migrate ``` This performs the following changes: - Updates the versions of the relevant packages in the `package.json` file. - Generates a `migrations.json` if there are pending migrations. ### Step 2: Running migrations The next step in the process involves using the `migrate` command to apply the migrations that were generated in the `migrations.json` file in the previous step. You can do so by running: ```shell nx migrate --run-migrations ``` All changes to your source code will be unstaged and ready for you to review and commit yourself. ### Step 3: Cleaning up After you run all the migrations, you can remove `migrations.json` and commit any outstanding changes. ## Recommendations ### One major version at a time, small steps Migrating Jest, Cypress, ESLint, React, Angular, Next, and more is a difficult task. All the tools change at different rates, and they can conflict with each other. In addition, every workspace is different. Even though our goal is for you to update any version of Nx to a newer version of Nx in a single go, sometimes it doesn't work. The recommended process is to update, at most, one major version at a time. Say you want to migrate from Nx 22.1.0 to Nx 23.0.0. The following steps are more likely to work comparing to `nx migrate 23.0.0`. - Run `nx migrate 22.7.5` to update the latest version in the 22.x branch. - Run `nx migrate --run-migrations`. - Next, run `nx migrate 23.0.0`. - Run `nx migrate --run-migrations`. {% aside type="caution" title="Angular updates" %} If your workspace uses Angular, this becomes a requirement rather than a recommendation. The Angular packages maintain migrations for a single major version at a time. If you try to update over multiple major versions, only the migrations for the latest major version will be applied. This can lead to issues in your workspace. {% /aside %} ## Crossing multiple major versions When the target is more than one major version ahead of your installed version, `nx migrate` prompts you to choose how far to jump: migrate to the latest in your current major (recommended), step into the next major, or go directly to the target. To skip the prompt, use `--multi-major-mode`: - `--multi-major-mode=gradual` migrates to the smallest recommended step (typically the latest in your current major), then tells you to re-run to continue. - `--multi-major-mode=direct` migrates straight to the target. The `NX_MULTI_MAJOR_MODE` environment variable is equivalent and takes precedence over a `multiMajorMode` value set in `nx.json`. In non-interactive environments there is no prompt - Nx warns that updating one major at a time is recommended and proceeds directly to the target. ## Choosing which packages to migrate While in most cases you want to be up to date with Nx and the dependencies it manages, sometimes you might need to stay on an older version of such a dependency. For example, you might want to update Nx to the latest version but keep Angular on **v21.x.x** and not update it to **v22.x.x**. The `--include` flag controls which packages are updated: `required` (the target package and the packages it ships with), `optional` (the dependency updates those packages recommend), or `all` (the default). The interactive `nx migrate` flow prompts for this. Pass `--include` to set it ahead of time. `--include` applies only when the target package opts into package selection. Nx and its official plugins do starting in Nx 23, and other plugin authors can opt in as well. For targets that don't, Nx uses `all` without prompting, and passing `--include` explicitly errors. Non-interactive runs also default to `all`. When unsure, prefer `--include=required` and follow up with `--include=optional`. Updating only Nx and its plugins keeps the PR scope small and has less chance of introducing issues, which matters most in large workspaces. Use `--include=all` when you're okay with doing everything in one PR. {% aside type="note" title="Optional package updates" %} You can't choose to skip any arbitrary package update. To ensure that a plugin works well with older versions of a given package, the plugin must support it. Therefore, Nx plugin authors define what package updates are optional. {% /aside %} {% aside type="caution" title="Taking control of package updates" %} While opting out of applying some package updates is supported by Nx, please keep in mind that you are effectively taking control of those package updates and opting out of Nx managing them. This means you'll need to keep up with the version requirements for those packages and those that depend on them. You'll also need to consider more things when updating them at some point [as explained later](#updating-dependencies-that-are-behind-the-versions-nx-manages). {% /aside %} ### Skipping optional package updates To skip the optional package updates, generate the migration with `--include=required`: ```shell nx migrate --include=required ``` Only the target package and the packages it ships with are updated. The `package.json` and the `migrations.json` are generated without the optional dependency updates, and you can catch up on those later. ### Updating dependencies that are behind the versions Nx manages Once you have skipped some optional updates, there'll come a time when you'll want to update those packages. Run `nx migrate --include=optional` to collect the optional dependency updates recommended for your installed version. It anchors to your installed version, so the target package must be installed and you can't migrate to a version higher than what's installed. The catch-up can be scoped to a single plugin by naming it as the target. For example, `nx migrate @nx/vite --include=optional` collects only the optional updates `@nx/vite` recommends, such as `vite` itself. {% aside type="caution" title="Scoping to a single plugin" %} Some plugin updates depend on other plugins running their updates too. For example, `@nx/angular` sometimes requires the `@nx/js` updates for TypeScript. Scoping the catch-up to a single plugin skips those, so prefer the full `nx migrate --include=optional` when unsure. {% /aside %} ## Managing migration steps When you run into problems running the `nx migrate --run-migrations` command, here are some solutions to break the process down into manageable steps. ### Make changes easier to review by committing after each migration runs Depending on the size of the update (e.g. migrating between major versions is likely to require more significant changes than migrating between feature releases), and the size of the workspace, the overall `nx migrate` process may generate a lot of changes which then need to be reviewed. Particularly if there are then manual changes which need to be made in addition to those made by `nx migrate`, it can make the associated PR harder to review because of not being able to distinguish between what was changed automatically and what was changed manually. If you pass `--create-commits` to the `--run-migrations` command, Nx will automatically create a dedicated commit for each successfully completed migration, for example: ```shell nx migrate --run-migrations --create-commits ``` Your git history will then look something like the following: ```text {% title="nx migrate --run-migrations --create-commits" frame="terminal" %} git log commit 8c862c780106ab8736985c01de1477309a403548 Author: YOUR_GIT_USERNAME Date: Thu Apr 14 18:35:44 2022 +0400 chore: [nx migration] name-of-the-second-migration-which-ran commit eb83bca97927af26aae731a2cf51ad62cc75efa3 Author: YOUR_GIT_USERNAME Date: Thu Apr 14 18:35:44 2022 +0400 chore: [nx migration] name-of-the-first-migration-which-ran etc ``` By default, nx will apply the prefix of `chore: [nx migration] ` to each commit in order to clearly identify it, but you can also customize this prefix by passing `--commit-prefix` to the command: ```shell nx migrate --run-migrations --create-commits --commit-prefix="chore(core): AUTOMATED - " ``` ```text {% title="git log" frame="terminal" %} commit 8c862c780106ab8736985c01de1477309a403548 Author: YOUR_GIT_USERNAME Date: Thu Apr 14 18:35:44 2022 +0400 chore(core): AUTOMATED - name-of-the-second-migration-which-ran commit eb83bca97927af26aae731a2cf51ad62cc75efa3 Author: YOUR_GIT_USERNAME Date: Thu Apr 14 18:35:44 2022 +0400 chore(core): AUTOMATED - name-of-the-first-migration-which-ran etc ``` ### Customizing which migrations run by altering `migrations.json` For small projects, running all the migrations at once often succeeds without any issues. For large projects, more flexibility is sometimes needed, and this is where having the separation between generating the migrations to be run, and actually running them, really shines. All you need to do is amend the JSON file in whatever way makes sense based on your circumstances, for example: - You may have to skip a migration. - You may want to run one migration at a time to address minor issues. - You may want to reorder migrations. - You may want to run the same migration multiple time if the process takes a long time and you had to rebase. Because you can run `nx migrate --run-migrations` as many times as you want, you can achieve all of that by commenting out and reordering items in `migrations.json`. The migration process can take a long time, depending on the number of migrations, so it is useful to commit the migrations file with the partially-updated repo alongside any changes which were created by previously completed migrations. You can even provide a custom location for the migrations file if you wish, you simply pass it to the `--run-migrations` option: ```shell nx migrate --run-migrations=migrations.json ``` ## Using an AI agent to apply migrations Some migrations ship an AI prompt that an installed agent applies for you. The [agentic flow](/docs/features/automate-updating-dependencies#step-2-run-migrations) is interactive by default, but you can control it with flags: - `--agentic` enables it and resolves the installed agent. Use `--agentic=claude-code` (or `codex`, `opencode`) to pin one, or `--no-agentic` to disable it. - `--validate` / `--no-validate` toggles agent validation of generator-only migrations - on by default when the agentic flow is enabled. A non-interactive `--agentic` run warns and continues without the agent, since the flow requires an interactive terminal. When the flow is enabled, Nx commits each migration separately by default so the agent can review an isolated diff. To always use the agentic flow (or a specific agent) without passing the flag, set the `agentic` and `validate` options in the [`migrate` section of `nx.json`](/docs/reference/nx-json#migrate). ## Workspace-wide migrate defaults To avoid passing the same flags on every run, set defaults in the [`migrate` section of `nx.json`](/docs/reference/nx-json#migrate) - `createCommits`, `commitPrefix`, `include`, `multiMajorMode`, `agentic`, and `validate`. A command-line flag always overrides the `nx.json` value, which in turn overrides the built-in Nx defaults. ## Other advanced capabilities ### Overriding versions Sometimes, you may want to use a different version of a package than what Nx recommends. To do that, specify the package and version: ```shell nx migrate --to="jest@30.0.0,cypress@15.0.0" ``` By default, Nx uses currently installed packages to calculate what migrations need to run. To override them, override the version: ```shell nx migrate --to="@nx/jest@12.0.0" ``` {% aside type="caution" title="Overriding versions" %} By choosing a version different of what Nx recommend you might use a package version that might have not been tested for a given Nx version. This might lead to unexpected issues. {% /aside %} ### Reverting a failed update Updates are best done on a clean git history so that it can be easily reversed if something fails. We try our best to make sure migrations do not fail but if one does, **please report it** on [GitHub](https://www.github.com/nrwl/nx/issues/new/). If an update fails for any reason, you can revert it as you do any other set of changes: ```shell git reset --hard # Reset any changes git clean -fd # Delete newly added files and directories ``` {% aside type="caution" title="--create-commits" %} If using `--create-commits`, you will need to first retrieve the SHA of the commit before your first automated migration commit in order to jump back to the point before the migrations ran, e.g. `git reset --hard YOUR_APPROPRIATE_SHA_HERE`) {% /aside %} # Extending Nx --- ## Extending Nx {% index_page_cards path="extending-nx" /%} # Technologies and Tools --- ## Technologies & Tools {% sidebar_group_cards group="Technologies & tools" /%} --- ## Build Tools {% sidebar_group_cards group="Technologies & tools/Build tools" /%} --- ## .NET {% sidebar_group_cards group="Technologies & tools/.NET" /%} --- ## Frameworks & Libraries {% sidebar_group_cards group="Technologies & tools/Frameworks & libraries" /%} --- ## Java (JVM) {% sidebar_group_cards group="Technologies & tools/Java (JVM)" /%} --- ## Node {% sidebar_group_cards group="Technologies & tools/Node" /%} --- ## Test Tools {% sidebar_group_cards group="Technologies & tools/Test tools" /%} # Technologies --- ## Technologies Guides and API references for all supported technologies. {% index_page_cards path="technologies" /%} --- ## Angular Get started with [Nx with Angular](/docs/technologies/angular/introduction), or browse the topics below. {% index_page_cards path="technologies/angular" /%} --- ## Angular with Rsbuild Get started with [Nx with Angular Rsbuild](/docs/technologies/angular/angular-rsbuild/introduction), or browse the topics below. {% index_page_cards path="technologies/angular/angular-rsbuild" /%} --- ## createServer - @nx/angular-rsbuild/ssr ```shell import { createServer } from '@nx/angular-rsbuild/ssr'; ``` The `createServer` function is used to setup Angular `CommonEngine` using an `express` server. It takes the bootstrap function as an argument, which is the function that bootstraps the Angular server application. This is usually` main.server.ts`. It returns `RsbuildAngularServer` which contains the server instance to allow further modifications as well as the listen method to start the server. ```ts function createServer(bootstrap: any): RsbuildAngularServer; ``` --- ## Examples {% tabs %} {% tabitem label="Standard Express Server Usage" %} The following example shows how to create a standard express server: ```ts // myapp/src/server.ts import { createServer } from '@nx/angular-rsbuild/ssr'; import bootstrap from './main.server'; const server = createServer(bootstrap); /** Add your custom server logic here * * For example, you can add a custom static file server: * * server.app.use('/static', express.static(staticFolder)); * * Or add additional api routes: * * server.app.get('/api/hello', (req, res) => { * res.send('Hello World!'); * }); * * Or add additional middleware: * * server.app.use((req, res, next) => { * res.send('Hello World!'); * }); */ server.listen(); ``` {% /tabitem %} {% /tabs %} --- ## RsbuildAngularServer ```ts export interface RsbuildAngularServer { app: express.Express; listen: (port?: number) => void; } ``` --- ### `app` `express.Express` The express application instance. ### `listen` `(port?: number) => void` Starts the express application on the specified port. If no port is provided, the default port (4000) is used. --- ## Nx with Angular Rsbuild The `@nx/angular-rsbuild` package provides configuration utilities for building Angular applications with [Rsbuild](https://rsbuild.dev). Rsbuild is built on top of Rspack and offers a streamlined development experience with fast builds and hot module replacement. ## Requirements The `@nx/angular-rsbuild` plugin supports the following package versions. | Package | Supported Versions | | --------------- | ---------------------------------------------------------------- | | `@rsbuild/core` | ^1.1.0 | | `@angular/core` | See [Angular version matrix](/docs/kb/angular-nx-version-matrix) | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Usage ```shell import { createConfig } from '@nx/angular-rsbuild'; ``` The `createConfig` function is used to create an Rsbuild configuration object setup for Angular applications. It takes an optional `RsbuildConfig` object as an argument, which allows for customization of the Rsbuild configuration. ```ts async function createConfig( defaultOptions: { options: PluginAngularOptions; rsbuildConfigOverrides?: Partial; }, configurations: Record< string, { options: Partial; rsbuildConfigOverrides?: Partial; } > = {}, configEnvVar = 'NGRS_CONFIG' ); ``` --- ## Examples {% tabs %} {% tabitem label="Server-Side Rendering (SSR)" %} The following example shows how to create a configuration for a SSR application: ```ts // myapp/rsbuild.config.ts import { createConfig } from '@nx/angular-rsbuild'; export default createConfig({ options: { browser: './src/main.ts', server: './src/main.server.ts', ssrEntry: './src/server.ts', }, }); ``` {% /tabitem %} {% tabitem label="Client-Side Rendering (CSR)" %} The following example shows how to create a configuration for a CSR application: ```ts // myapp/rsbuild.config.ts import { createConfig } from '@nx/angular-rsbuild'; export default createConfig({ options: { browser: './src/main.ts', }, }); ``` {% /tabitem %} {% tabitem label="Modify Rsbuild Configuration" %} The following example shows how to modify the base Rsbuild configuration: ```ts // myapp/rsbuild.config.ts import { createConfig } from '@nx/angular-rsbuild'; export default createConfig({ options: { browser: './src/main.ts', server: './src/main.server.ts', ssrEntry: './src/server.ts', }, rsbuildConfigOverrides: { mode: 'development', }, }); ``` {% /tabitem %} {% tabitem label="File Replacements" %} The following example shows how to use file replacements: ```ts // myapp/rsbuild.config.ts import { createConfig } from '@nx/angular-rsbuild'; export default createConfig({ options: { browser: './src/main.ts', server: './src/main.server.ts', ssrEntry: './src/server.ts', fileReplacements: [ { replace: './src/environments/environment.ts', with: './src/environments/environment.prod.ts', }, ], }, }); ``` {% /tabitem %} {% /tabs %} --- ## PluginAngularOptions The `PluginAngularOptions` object is an object that contains the following properties: ```ts export interface PluginAngularOptions extends PluginUnsupportedOptions { aot?: boolean; assets?: AssetElement[]; browser?: string; commonChunk?: boolean; devServer?: DevServerOptions; extractLicenses?: boolean; fileReplacements?: FileReplacement[]; index?: IndexElement; inlineStyleLanguage?: InlineStyleLanguage; namedChunks?: boolean; optimization?: boolean | OptimizationOptions; outputHashing?: OutputHashing; outputPath?: string | (Required> & Partial); polyfills?: string[]; root?: string; scripts?: ScriptOrStyleEntry[]; server?: string; skipTypeChecking?: boolean; sourceMap?: boolean | Partial; ssr?: | boolean | { entry: string; experimentalPlatform?: 'node' | 'neutral'; }; stylePreprocessorOptions?: StylePreprocessorOptions; styles?: ScriptOrStyleEntry[]; tsConfig?: string; useTsProjectReferences?: boolean; vendorChunk?: boolean; } export interface DevServerOptions extends DevServerUnsupportedOptions { port?: number; ssl?: boolean; sslKey?: string; sslCert?: string; proxyConfig?: string; } export interface OptimizationOptions { scripts?: boolean; styles?: boolean; fonts?: boolean; } export type OutputHashing = 'none' | 'all' | 'media' | 'bundles'; export type HashFormat = { chunk: string; extract: string; file: string; script: string; }; export interface OutputPath { base: string; browser: string; server: string; media: string; } export type AssetExpandedDefinition = { glob: string; input: string; ignore?: string[]; output?: string; }; export type AssetElement = AssetExpandedDefinition | string; export type NormalizedAssetElement = AssetExpandedDefinition & { output: string; }; export type ScriptOrStyleEntry = | string | { input: string; bundleName?: string; inject?: boolean; }; export type GlobalEntry = { name: string; files: string[]; initial: boolean; }; export type IndexExpandedDefinition = { input: string; output?: string; preloadInitial?: boolean; }; export type IndexElement = IndexExpandedDefinition | string | false; export type IndexHtmlTransform = (content: string) => Promise; export type NormalizedIndexElement = | (IndexExpandedDefinition & { insertionOrder: [string, boolean][]; transformer: IndexHtmlTransform | undefined; }) | false; export interface SourceMap { scripts: boolean; styles: boolean; hidden: boolean; vendor: boolean; } export type InlineStyleExtension = 'css' | 'scss' | 'sass' | 'less'; export interface FileReplacement { replace: string; with: string; } export interface StylePreprocessorOptions { includePaths?: string[]; sass?: Sass; } export interface Sass { fatalDeprecations?: DeprecationOrId[]; futureDeprecations?: DeprecationOrId[]; silenceDeprecations?: DeprecationOrId[]; } ``` --- ### aot `boolean` `default: true` Enables or disables Ahead-of-Time compilation for Angular applications. ### assets `AssetElement[]` Array of static assets to include in the build output. Can be either a string path or an object with glob patterns. ### browser `string` The entry point file for the browser bundle (e.g., 'src/main.ts'). ### commonChunk `boolean` `default: true` Controls whether to create a separate bundle containing shared code between multiple chunks. ### devServer `DevServerOptions` RsbuildConfig options for the development server including port, SSL settings, and proxy configuration. ### extractLicenses `boolean` `default: false` When true, extracts all license information from dependencies into a separate file. ### fileReplacements `FileReplacement[]` List of files to be replaced during the build process, typically used for environment-specific configurations. ### index `IndexElement` Configuration for the index.html file. Can be a string path, an object with specific settings, or false to disable. ### inlineStyleLanguage `InlineStyleLanguage` Specifies the default language to use for inline styles in components. ### namedChunks `boolean` `default: true` When true, generates named chunks instead of numerical IDs. ### optimization `boolean | OptimizationOptions` `default: true` Controls build optimization settings for scripts, styles, and fonts. ### outputHashing `OutputHashing` `default: 'none'` Defines the hashing strategy for output files. Can be 'none', 'all', 'media', or 'bundles'. ### outputPath `string | OutputPath` Specifies the output directory for built files. Can be a string or an object defining paths for browser, server, and media files. ### polyfills `string[]` Array of polyfill files to include in the build. ### root `string` The root directory of the project where the rsbuild.config.ts file is located. ### scripts `ScriptOrStyleEntry[]` Array of global scripts to include in the build, with options for bundling and injection. ### server `string` The entry point file for the server bundle in SSR applications. ### skipTypeChecking `boolean` `default: false` When true, skips TypeScript type checking during the build process. ### sourceMap `boolean | Partial` `default: true` Controls generation of source maps for debugging. Can be boolean or detailed configuration object. ### ssr `boolean | { entry: string; experimentalPlatform?: 'node' | 'neutral' }` Configuration for Server-Side Rendering. Can be boolean or object with specific SSR settings. ### stylePreprocessorOptions `StylePreprocessorOptions` Options for style preprocessors, including include paths and Sass-specific configurations. ### styles `ScriptOrStyleEntry[]` Array of global styles to include in the build, with options for bundling and injection. ### tsConfig `string` Path to the TypeScript configuration file. ### useTsProjectReferences `boolean` `default: false` Enables usage of TypeScript project references. ### vendorChunk `boolean` `default: true` When true, creates a separate bundle for vendor (third-party) code. ## Set up CI In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## Angular with Rspack Get started with [Nx with Angular Rspack](/docs/technologies/angular/angular-rspack/introduction), or browse the topics below. {% index_page_cards path="technologies/angular/angular-rspack" /%} --- ## Angular Rspack Compiler {% index_page_cards path="technologies/angular/angular-rspack-compiler" /%} --- ## Angular Rspack Compiler Compilation utilities for Angular with Rspack and Rsbuild. ## Requirements The `@nx/angular-rspack-compiler` package supports the following package versions. | Package | Supported Versions | | ----------------------- | ---------------------------------------------------------------- | | `@angular/build` | See [Angular version matrix](/docs/kb/angular-nx-version-matrix) | | `@angular/compiler-cli` | See [Angular version matrix](/docs/kb/angular-nx-version-matrix) | | `@rsbuild/core` | >= 1.0.5 < 2.0.0 | This package is consumed by [`@nx/angular-rspack`](/docs/technologies/angular/angular-rspack/introduction); the same supported version window applies to workspaces using Angular Rspack. --- ## Guides {% index_page_cards path="technologies/angular/angular-rspack/guides" /%} --- ## Nx with Angular Rspack ## Requirements The `@angular-rspack/nx` plugin supports the following package versions. | Package | Supported Versions | | --------------- | ---------------------------------------------------------------- | | `@rspack/core` | ^2.0.0 \|\| >=1.3.5 <1.7.0 | | `@angular/core` | See [Angular version matrix](/docs/kb/angular-nx-version-matrix) | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. Angular compilation has always been a black box hidden behind layers of abstraction and configuration, exposed via the Angular Builders from the Angular CLI packages. Originally, the underlying tool that bundled the Angular application was [Webpack](https://webpack.js.org). This was great as teams were able to extend their builds by leveraging the vast Webpack ecosystem and plugins that are available. Over time, it became clear that the inherit slowness with Webpack build speeds was becoming more and more of an issue for Angular developers. The Angular Team decided to address this build speed issue by building out a new build pipeline that used [Esbuild](https://esbuild.github.io/). This succeeded in reducing the build times for Angular applications, however, it made one crucial mistake. It left the existing Angular applications that relied on the Webpack ecosystem behind, with either a difficult migration path or none at all. --- ## Rspack The solution to this problem was to create a new build pipeline that could use the existing Webpack ecosystem and plugins while also providing faster builds for Angular applications. This is where [Rspack](https://rspack.dev) come into play. Rspack is a high performance JavaScript bundler written in Rust. It offers strong compatibility with the Webpack ecosystem, and can serve as a near drop-in replacement for webpack with significantly faster build speeds. Because it supports the existing Webpack ecosystem, it provides an answer to teams that maintain Angular applications using Webpack and want to migrate to a faster build pipeline. This makes it a great solution for teams that want to migrate to a faster build pipeline, but still want the ability to extend their builds and use [Module Federation](https://module-federation.io). {% aside type="caution" title="Angular Rspack Status" %} Please note that Angular Rspack support is still experimental and is not yet considered production ready. We are actively working on improving the experience and stability of Angular Rspack, and we will continue to update this page as we make progress. {% /aside %} ## Known limitations and missing features The following are known limitations and missing features of Angular Rspack: - Server Routing is not supported - still experimental in Angular currently. - App Engine APIs are not supported - still experimental in Angular currently. If you have any other missing features or limitations, please [let us know](https://github.com/nrwl/angular-rspack/issues/new). ## Benchmarks ![Benchmarks](../../../../../assets/guides/angular-rspack/bundler-build-times.png) Below is a table of benchmarks for different bundlers, tested on an application with ~800 lazy-loaded routes and ~10 components per route—totaling ~8000 components. **System Info** - MacBook Pro (macOS 15.3.1) - Processor: M2 Max - Memory: 96 GB | Build/Bundler | Prod SSR (s) | Prod (s) | Dev (s) | | ------------- | ------------ | -------- | ------- | | Webpack | 198.614 | 154.339 | 159.436 | | esbuild | 23.701 | 19.569 | 15.358 | | Rspack | 30.589 | 19.269 | 19.940 | You can find the benchmarks and run them yourself: [https://github.com/nrwl/ng-bundler-benchmark](https://github.com/nrwl/ng-bundler-benchmark) ## Set up CI In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/angular Executors The @nx/angular plugin provides various executors to help you create and configure angular projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/angular introduction](/docs/technologies/angular/introduction). ### `application` Builds an Angular application using [esbuild](https://esbuild.github.io/) with integrated SSR and prerendering capabilities. This executor is a drop-in replacement for the `@angular-devkit/build-angular:application` builder provided by the Angular CLI. It builds an Angular application using [esbuild](https://esbuild.github.io/) with integrated SSR and prerendering capabilities. In addition to the features provided by the Angular CLI builder, the `@nx/angular:application` executor also supports the following: - Providing esbuild plugins - Providing a function to transform the application's `index.html` file - Incremental builds :::tip[Dev Server] The [`@nx/angular:dev-server` executor](/nx-api/angular/executors/dev-server) is required to serve your application when using the `@nx/angular:application` to build it. It is a drop-in replacement for the Angular CLI's `@angular-devkit/build-angular:dev-server` builder and ensures the application is correctly served with Vite when using the `@nx/angular:application` executor. ::: ### Examples ###### Providing esbuild plugins The executor accepts a `plugins` option that allows you to provide esbuild plugins that will be used when building your application. It allows providing a path to a plugin file or an object with a `path` and `options` property to provide options to the plugin. ```json title="apps/my-app/project.json" {8-16} { ... "targets": { "build": { "executor": "@nx/angular:application", "options": { ... "plugins": [ "apps/my-app/plugins/plugin1.js", { "path": "apps/my-app/plugins/plugin2.js", "options": { "someOption": "some value" } } ] } } ... } } ``` ```ts title="apps/my-app/plugins/plugin1.js" const plugin1 = { name: 'plugin1', setup(build) { const options = build.initialOptions; options.define.PLUGIN1_TEXT = '"Value was provided at build time"'; }, }; module.exports = plugin1; ``` ```ts title="apps/my-app/plugins/plugin2.js" function plugin2({ someOption }) { return { name: 'plugin2', setup(build) { const options = build.initialOptions; options.define.PLUGIN2_TEXT = JSON.stringify(someOption); }, }; } module.exports = plugin2; ``` Additionally, we need to inform TypeScript of the defined variables to prevent type-checking errors during the build. We can achieve this by creating or updating a type definition file included in the TypeScript build process (e.g. `src/types.d.ts`) with the following content: ```ts title="apps/my-app/src/types.d.ts" declare const PLUGIN1_TEXT: number; declare const PLUGIN2_TEXT: string; ``` ###### Transforming the 'index.html' file The executor accepts an `indexHtmlTransformer` option to provide a path to a file with a default export for a function that receives the application's `index.html` file contents and outputs the updated contents. ```json title="apps/my-app/project.json" {8} { ... "targets": { "build": { "executor": "@nx/angular:application", "options": { ... "indexHtmlTransformer": "apps/my-app/index-html.transformer.ts" } } ... } } ``` ```ts title="apps/my-app/index-html.transformer.ts" export default function (indexContent: string) { return indexContent.replace( 'my-app', 'my-app (transformed)' ); } ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `outputPath` | string [**required**] | Specify the output path relative to workspace root. | | | `tsConfig` | string [**required**] | The full path for the TypeScript configuration file, relative to the current workspace. | | | `allowedCommonJsDependencies` | array | A list of CommonJS or AMD packages that are allowed to be used without a build time warning. Use `'*'` to allow all. | `[]` | | `aot` | boolean | Build using Ahead of Time compilation. | `true` | | `appShell` | boolean | Generates an application shell during build time. | | | `assets` | array | List of static application assets. | `[]` | | `baseHref` | string | Base url for the application being built. | | | `browser` | string | The full path for the browser entry point to the application, relative to the current workspace. | | | `budgets` | array | Budget thresholds to ensure parts of your application stay within boundaries which you set. | `[]` | | `buildLibsFromSource` | boolean | Read buildable libraries from source instead of building them separately. | `true` | | `clearScreen` | boolean | Automatically clear the terminal screen during rebuilds. | `false` | | `conditions` | array | Custom package resolution conditions used to resolve conditional exports/imports. Defaults to ['module', 'development'/'production']. The following special conditions are always present if the requirements are satisfied: 'default', 'import', 'require', 'browser', 'node'. | | | `crossOrigin` | string | Define the crossorigin attribute setting of elements that provide CORS support. | `"none"` | | `define` | object | Defines global identifiers that will be replaced with a specified constant value when found in any JavaScript or TypeScript code including libraries. The value will be used directly. String values must be put in quotes. Identifiers within Angular metadata such as Component Decorators will not be replaced. | | | `deleteOutputPath` | boolean | Delete the output path before building. | `true` | | `deployUrl` | string | Customize the base path for the URLs of resources in 'index.html' and component stylesheets. This option is only necessary for specific deployment scenarios, such as with Angular Elements or when utilizing different CDN locations. | | | `externalDependencies` | array | Exclude the listed external dependencies from being bundled into the bundle. Instead, the created bundle relies on these dependencies to be available during runtime. | `[]` | | `extractLicenses` | boolean | Extract all licenses in a separate file. | `true` | | `fileReplacements` | array | Replace compilation source files with other compilation source files in the build. | `[]` | | `i18nDuplicateTranslation` | string | How to handle duplicate translations for i18n. | `"warning"` | | `i18nMissingTranslation` | string | How to handle missing translations for i18n. | `"warning"` | | `index` | string | Configures the generation of the application's HTML index. | | | `indexHtmlTransformer` | string | Path to a file exposing a default function to transform the `index.html` file. | | | `inlineStyleLanguage` | string | The stylesheet language to use for the application's inline component styles. | `"css"` | | `loader` | object | Defines the type of loader to use with a specified file extension when used with a JavaScript `import`. `text` inlines the content as a string; `binary` inlines the content as a Uint8Array; `file` emits the file and provides the runtime location of the file; `dataurl` inlines the content as a data URL with best guess of MIME type; `base64` inlines the content as a Base64-encoded string; `empty` considers the content to be empty and not include it in bundles. _Note: `dataurl` and `base64` are only supported in Angular versions >= 20.1.0_. | | | `localize` | string | Translate the bundles in one or more locales. | | | `namedChunks` | boolean | Use file name for lazy loaded chunks. | `false` | | `optimization` | string | Enables optimization of the build output. Including minification of scripts and styles, tree-shaking, dead-code elimination, inlining of critical CSS and fonts inlining. For more information, see https://angular.dev/reference/configs/workspace-config#optimization-configuration. | `true` | | `outputHashing` | string | Define the output filename cache-busting hashing mode. | `"none"` | | `outputMode` | string | Defines the build output target. 'static': Generates a static site for deployment on any static hosting service. 'server': Produces an application designed for deployment on a server that supports server-side rendering (SSR). | | | `plugins` | array | A list of ESBuild plugins. | | | `poll` | number | Enable and define the file watching poll time period in milliseconds. | | | `polyfills` | array | A list of polyfills to include in the build. Can be a full path for a file, relative to the current workspace or module specifier. Example: 'zone.js'. | `[]` | | `prerender` | string | Prerender (SSG) pages of your application during build time. | | | `preserveSymlinks` | boolean | Do not use the real path when resolving modules. If unset then will default to `true` if NodeJS option --preserve-symlinks is set. | | | `progress` | boolean | Log progress to the console while building. | `true` | | `scripts` | array | Global scripts to be included in the build. | `[]` | | `security` | object | Security features to protect against XSS and other common attacks | | | `server` | string | The full path for the server entry point to the application, relative to the current workspace. | | | `serviceWorker` | string | Generates a service worker configuration. | `false` | | `sourceMap` | string | Output source maps for scripts and styles. For more information, see https://angular.dev/reference/configs/workspace-config#source-map-configuration. | `false` | | `ssr` | string | Server side render (SSR) pages of your application during runtime. | `false` | | `statsJson` | boolean | Generates a 'stats.json' file which can be analyzed with https://esbuild.github.io/analyze/. | `false` | | `stylePreprocessorOptions` | object | Options to pass to style preprocessors. | | | `styles` | array | Global styles to be included in the build. | `[]` | | `subresourceIntegrity` | boolean | Enables the use of subresource integrity validation. | `false` | | `verbose` | boolean | Adds more details to output logging. | `false` | | `watch` | boolean | Run build when files change. | `false` | | `webWorkerTsConfig` | string | TypeScript configuration for Web Worker modules. | | ### `browser-esbuild` Builds an Angular application using [esbuild](https://esbuild.github.io/). This executor is a drop-in replacement for the `@angular-devkit/build-angular:browser-esbuild` builder provided by the Angular CLI. It builds an Angular application using esbuild. In addition to the features provided by the Angular CLI builder, the `@nx/angular:browser-esbuild` executor also supports the following: - Providing esbuild plugins - Incremental builds :::tip[Dev Server] The [`@nx/angular:dev-server` executor](/nx-api/angular/executors/dev-server) is required to serve your application when using the `@nx/angular:browser-esbuild` to build it. It is a drop-in replacement for the Angular CLI's `@angular-devkit/build-angular:dev-server` builder and ensures the application is correctly served with Vite when using the `@nx/angular:browser-esbuild` executor. ::: ### Examples ###### Providing esbuild plugins The executor accepts a `plugins` option that allows you to provide esbuild plugins that will be used when building your application. It allows providing a path to a plugin file or an object with a `path` and `options` property to provide options to the plugin. ```json title="apps/my-app/project.json" {8-16} { ... "targets": { "build": { "executor": "@nx/angular:browser-esbuild", "options": { ... "plugins": [ "apps/my-app/plugins/plugin1.js", { "path": "apps/my-app/plugins/plugin2.js", "options": { "someOption": "some value" } } ] } } ... } } ``` ```ts title="apps/my-app/plugins/plugin1.js" const plugin1 = { name: 'plugin1', setup(build) { const options = build.initialOptions; options.define.PLUGIN1_TEXT = '"Value was provided at build time"'; }, }; module.exports = plugin1; ``` ```ts title="apps/my-app/plugins/plugin2.js" function plugin2({ someOption }) { return { name: 'plugin2', setup(build) { const options = build.initialOptions; options.define.PLUGIN2_TEXT = JSON.stringify(someOption); }, }; } module.exports = plugin2; ``` Additionally, we need to inform TypeScript of the defined variables to prevent type-checking errors during the build. We can achieve this by creating or updating a type definition file included in the TypeScript build process (e.g. `src/types.d.ts`) with the following content: ```ts title="apps/my-app/src/types.d.ts" declare const PLUGIN1_TEXT: number; declare const PLUGIN2_TEXT: string; ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `index` | string [**required**] | Configures the generation of the application's HTML index. | | | `main` | string [**required**] | The full path for the main entry point to the app, relative to the current workspace. | | | `outputPath` | string [**required**] | The full path for the new output directory, relative to the current workspace. | | | `tsConfig` | string [**required**] | The full path for the TypeScript configuration file, relative to the current workspace. | | | `allowedCommonJsDependencies` | array | A list of CommonJS or AMD packages that are allowed to be used without a build time warning. Use `'*'` to allow all. | `[]` | | `aot` | boolean | Build using Ahead of Time compilation. | `true` | | `assets` | array | List of static application assets. | `[]` | | `baseHref` | string | Base url for the application being built. | | | `budgets` | array | Budget thresholds to ensure parts of your application stay within boundaries which you set. | `[]` | | `buildLibsFromSource` | boolean | Read buildable libraries from source instead of building them separately. | `true` | | `buildOptimizer` | boolean | Enables advanced build optimizations when using the 'aot' option. | `true` | | `commonChunk` | boolean | Generate a separate bundle containing code used across multiple bundles. | `true` | | `crossOrigin` | string | Define the crossorigin attribute setting of elements that provide CORS support. | `"none"` | | `deleteOutputPath` | boolean | Delete the output path before building. | `true` | | `deployUrl` | string | Customize the base path for the URLs of resources in 'index.html' and component stylesheets. This option is only necessary for specific deployment scenarios, such as with Angular Elements or when utilizing different CDN locations. | | | `externalDependencies` | array | Exclude the listed external dependencies from being bundled into the bundle. Instead, the created bundle relies on these dependencies to be available during runtime. | `[]` | | `extractLicenses` | boolean | Extract all licenses in a separate file. | `true` | | `fileReplacements` | array | Replace compilation source files with other compilation source files in the build. | `[]` | | `i18nDuplicateTranslation` | string | How to handle duplicate translations for i18n. | `"warning"` | | `i18nMissingTranslation` | string | How to handle missing translations for i18n. | `"warning"` | | `inlineStyleLanguage` | string | The stylesheet language to use for the application's inline component styles. | `"css"` | | `localize` | string | Translate the bundles in one or more locales. | | | `namedChunks` | boolean | Use file name for lazy loaded chunks. | `false` | | `ngswConfigPath` | string | Path to ngsw-config.json. | | | `optimization` | string | Enables optimization of the build output. Including minification of scripts and styles, tree-shaking, dead-code elimination, inlining of critical CSS and fonts inlining. For more information, see https://angular.dev/reference/configs/workspace-config#optimization-configuration. | `true` | | `outputHashing` | string | Define the output filename cache-busting hashing mode. | `"none"` | | `plugins` | array | A list of ESBuild plugins. | | | `poll` | number | Enable and define the file watching poll time period in milliseconds. | | | `polyfills` | string | Polyfills to be included in the build. | | | `preserveSymlinks` | boolean | Do not use the real path when resolving modules. If unset then will default to `true` if NodeJS option --preserve-symlinks is set. | | | `progress` | boolean | Log progress to the console while building. | `true` | | `resourcesOutputPath` | string | The path where style resources will be placed, relative to outputPath. | | | `scripts` | array | Global scripts to be included in the build. | `[]` | | `serviceWorker` | boolean | Generates a service worker config for production builds. | `false` | | `sourceMap` | string | Output source maps for scripts and styles. For more information, see https://angular.dev/reference/configs/workspace-config#source-map-configuration. | `false` | | `statsJson` | boolean | Generates a 'stats.json' file which can be analyzed using tools such as 'webpack-bundle-analyzer'. | `false` | | `stylePreprocessorOptions` | object | Options to pass to style preprocessors. | | | `styles` | array | Global styles to be included in the build. | `[]` | | `subresourceIntegrity` | boolean | Enables the use of subresource integrity validation. | `false` | | `vendorChunk` | boolean | Generate a separate bundle containing only vendor libraries. This option should only be used for development to reduce the incremental compilation time. | `false` | | `verbose` | boolean | Adds more details to output logging. | `false` | | `watch` | boolean | Run build when files change. | `false` | | `webWorkerTsConfig` | string | TypeScript configuration for Web Worker modules. | | ### `delegate-build` Delegates the build to a different target while supporting incremental builds. ### Examples ###### Basic Usage Delegate the build of the project to a different target. ```json { "prod-build": { "executor": "@nx/angular:delegate-build", "options": { "buildTarget": "app:build:production", "outputPath": "dist/apps/app/production", "tsConfig": "apps/app/tsconfig.json", "watch": false } } } ``` ###### Watch for build changes Delegate the build of the project to a different target. ```json { "prod-build": { "executor": "@nx/angular:delegate-build", "options": { "buildTarget": "app:build:production", "outputPath": "dist/apps/app/production", "tsConfig": "apps/app/tsconfig.json", "watch": true } } } ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `buildTarget` | string [**required**] | Build target used for building the application after its dependencies have been built. | | | `outputPath` | string [**required**] | The full path for the output directory, relative to the workspace root. | | | `tsConfig` | string [**required**] | The full path for the TypeScript configuration file, relative to the workspace root. | | | `watch` | boolean | Whether to run a build when any file changes. | `false` | ### `extract-i18n` Extracts i18n messages from source code. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `buildTarget` | string [**required**] | A builder target to extract i18n messages in the format of `project:target[:configuration]`. You can also pass in more than one configuration name as a comma-separated list. Example: `project:target:production,staging`. | | | `format` | string | Output format for the generated file. | `"xlf"` | | `i18nDuplicateTranslation` | string | How to handle duplicate translations. _Note: this is only available in Angular 20.0.0 and above._ | | | `outFile` | string | Name of the file to output. | | | `outputPath` | string | Path where output will be placed. | | | `progress` | boolean | Log progress to the console. | `true` | ### `module-federation-dev-server` Serves host [Module Federation](https://module-federation.io/) applications ([webpack](https://webpack.js.org/)-based) allowing to specify which remote applications should be served with the host. ### Examples ###### Basic Usage The Module Federation Dev Server will serve a host application and find the remote applications associated with the host and serve them statically also. See an example set up of it below: ```json { "serve": { "executor": "@nx/angular:module-federation-dev-server", "configurations": { "production": { "buildTarget": "host:build:production" }, "development": { "buildTarget": "host:build:development" } }, "defaultConfiguration": "development", "options": { "port": 4200, "publicHost": "http://localhost:4200" } } } ``` ###### Serve host with remotes that can be live reloaded The Module Federation Dev Server will serve a host application and find the remote applications associated with the host and serve a set selection with live reloading enabled also. See an example set up of it below: ```json { "serve-with-hmr-remotes": { "executor": "@nx/angular:module-federation-dev-server", "configurations": { "production": { "buildTarget": "host:build:production" }, "development": { "buildTarget": "host:build:development" } }, "defaultConfiguration": "development", "options": { "port": 4200, "publicHost": "http://localhost:4200", "devRemotes": [ "remote1", { "remoteName": "remote2", "configuration": "development" } ] } } } ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `buildTarget` | string [**required**] | A build builder target to serve in the format of `project:target[:configuration]`. | | | `allowedHosts` | array | List of hosts that are allowed to access the dev server. | `[]` | | `buildLibsFromSource` | boolean | Read buildable libraries from source instead of building them separately. If not set, it will take the value specified in the `buildTarget` options, or it will default to `true` if it's also not set in the `buildTarget` options. | | | `devRemotes` | array | List of remote applications to run in development mode (i.e. using serve target). | | | `disableHostCheck` | boolean | Don't verify connected clients are part of allowed hosts. | `false` | | `headers` | object | Custom HTTP headers to be added to all responses. | | | `hmr` | boolean | Enable hot module replacement. | `false` | | `host` | string | Host to listen on. | `"localhost"` | | `isInitialHost` | boolean | Whether the host that is running this executor is the first in the project tree to do so. | `true` | | `liveReload` | boolean | Whether to reload the page on change, using live-reload. | `true` | | `open` | boolean | Opens the url in default browser. | `false` | | `parallel` | number | Max number of parallel processes for building static remotes | | | `pathToManifestFile` | string | Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic remote applications relative to the workspace root. | | | `poll` | number | Enable and define the file watching poll time period in milliseconds. | | | `port` | number | Port to listen on. | `4200` | | `proxyConfig` | string | Proxy configuration file. For more information, see https://angular.dev/tools/cli/serve#proxying-to-a-backend-server. | | | `publicHost` | string | The URL that the browser client (or live-reload client, if enabled) should use to connect to the development server. Use for a complex dev server setup, such as one with reverse proxies. | | | `servePath` | string | The pathname where the app will be served. | | | `skipRemotes` | array | List of remote applications to not automatically serve, either statically or in development mode. This will not remove the remotes from the `module-federation.config` file, and therefore the application may still try to fetch these remotes. This option is useful if you have other means for serving the `remote` application(s). **NOTE:** Remotes that are not in the workspace will be skipped automatically. | | | `ssl` | boolean | Serve using HTTPS. | `false` | | `sslCert` | string | SSL certificate to use for serving HTTPS. | | | `sslKey` | string | SSL key to use for serving HTTPS. | | | `static` | boolean | Whether to use a static file server instead of the webpack-dev-server. This should be used for remote applications that are also host applications. | | | `staticRemotesPort` | number | The port at which to serve the file-server for the static remotes. | | | `verbose` | boolean | Adds more details to output logging. | | | `watch` | boolean | Rebuild on change. | `true` | ### `module-federation-dev-ssr` The module-federation-ssr-dev-server executor is reserved exclusively for use with host SSR Module Federation applications. It allows the user to specify which remote applications should be served with the host. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `browserTarget` | string [**required**] | Browser target to build. | | | `serverTarget` | string [**required**] | Server target to build. | | | `devRemotes` | array | List of remote applications to run in development mode (i.e. using serve target). | | | `host` | string | Host to listen on. | `"localhost"` | | `inspect` | boolean | Launch the development server in inspector mode and listen on address and port '127.0.0.1:9229'. | `false` | | `isInitialHost` | boolean | Whether the host that is running this executor is the first in the project tree to do so. | `true` | | `open` | boolean | Opens the url in default browser. | `false` | | `parallel` | number | Max number of parallel processes for building static remotes | | | `pathToManifestFile` | string | Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic remote applications relative to the workspace root. | | | `port` | number | Port to start the development server at. Default is 4200. Pass 0 to get a dynamically assigned port. | `4200` | | `progress` | boolean | Log progress to the console while building. | | | `proxyConfig` | string | Proxy configuration file. | | | `publicHost` | string | The URL that the browser client should use to connect to the development server. Use for a complex dev server setup, such as one with reverse proxies. | | | `skipRemotes` | array | List of remote applications to not automatically serve, either statically or in development mode. | | | `ssl` | boolean | Serve using HTTPS. | `false` | | `sslCert` | string | SSL certificate to use for serving HTTPS. | | | `sslKey` | string | SSL key to use for serving HTTPS. | | | `staticRemotesPort` | number | The port at which to serve the file-server for the static remotes. | | | `verbose` | boolean | Adds more details to output logging. | `false` | ### `ng-packagr-lite` Builds an Angular library with support for incremental builds. This executor is meant to be used with buildable libraries in an incremental build scenario. It is similar to the `@nx/angular:package` executor but it only produces ESM2022 bundles. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `poll` | number | Enable and define the file watching poll time period in milliseconds. | | | `project` | string | The file path for the ng-packagr configuration file, relative to the workspace root. | | | `tsConfig` | string | The full path for the TypeScript configuration file, relative to the workspace root. | | | `watch` | boolean | Whether to run a build when any file changes. | `false` | ### `package` Builds and packages an Angular library producing an output following the Angular Package Format (APF) to be distributed as an NPM package. This executor is a drop-in replacement for the `@angular-devkit/build-angular:ng-packagr` and `@angular/build:ng-packagr` builders, with additional support for incremental builds. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `poll` | number | Enable and define the file watching poll time period in milliseconds. | | | `project` | string | The file path for the ng-packagr configuration file, relative to the workspace root. | | | `tsConfig` | string | The full path for the TypeScript configuration file, relative to the workspace root. | | | `watch` | boolean | Whether to run a build when any file changes. | `false` | ### `unit-test` Run application unit tests. _Note: this is only supported in Angular versions >= 21.0.0_. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `browsers` | array | Specifies the browsers to use for test execution. When not specified, tests are run in a Node.js environment using jsdom. For both Vitest and Karma, browser names ending with 'Headless' (e.g., 'ChromeHeadless') will enable headless mode. | | | `browserViewport` | string | Specifies the browser viewport dimensions for browser-based tests in the format `widthxheight`. | | | `buildTarget` | string | Specifies the build target to use for the unit test build in the format `project:target[:configuration]`. This defaults to the `build` target of the current project with the `development` configuration. You can also pass a comma-separated list of configurations. Example: `project:target:production,staging`. | | | `coverage` | boolean | Enables coverage reporting for tests. | `false` | | `coverageExclude` | array | Specifies glob patterns of files to exclude from the coverage report. | | | `coverageInclude` | array | Specifies glob patterns of files to include in the coverage report. | | | `coverageReporters` | array | Specifies the reporters to use for coverage results. Each reporter can be a string representing its name, or a tuple containing the name and an options object. Built-in reporters include 'html', 'lcov', 'lcovonly', 'text', 'text-summary', 'cobertura', 'json', and 'json-summary'. | | | `coverageThresholds` | object | Specifies minimum coverage thresholds that must be met. If thresholds are not met, the builder will exit with an error. | | | `coverageWatermarks` | object | Specifies coverage watermarks for the HTML reporter. These determine the color coding for high, medium, and low coverage. | | | `debug` | boolean | Enables debugging mode for tests, allowing the use of the Node Inspector. | `false` | | `dumpVirtualFiles` | boolean | Dumps build output files to the `.angular/cache` directory for debugging purposes. | `false` | | `exclude` | array | Specifies glob patterns of files to exclude from testing, relative to the project root. | | | `filter` | string | Specifies a regular expression pattern to match against test suite and test names. Only tests with a name matching the pattern will be executed. For example, `^App` will run only tests in suites beginning with 'App'. | | | `headless` | boolean | Forces all configured browsers to run in headless mode. When using the Vitest runner, this option is ignored if no browsers are configured. The Karma runner does not support this option. _Note: this is only supported in Angular versions >= 21.2.0_. | | | `include` | array | Specifies glob patterns of files to include for testing, relative to the project root. This option also has special handling for directory paths (includes all test files within) and file paths (includes the corresponding test file if one exists). | `["**/*.spec.ts","**/*.test.ts"]` | | `indexHtmlTransformer` | string | Path to a file exposing a default function to transform the `index.html` file. | | | `isolate` | boolean | Enables isolation for test execution. When true, Vitest runs tests in separate threads or processes. This option is only available for the Vitest runner. Defaults to false to align with the Karma/Jasmine experience. _Note: this is only supported in Angular versions >= 22.0.0_. | | | `listTests` | boolean | Lists all discovered test files and exits the process without building or executing the tests. | `false` | | `outputFile` | string | Specifies a file path for the test report, applying only to the first reporter. To configure output files for multiple reporters, use the tuple format `['reporter-name', { outputFile: '...' }]` within the `reporters` option. When not provided, output is written to the console. | | | `plugins` | array | A list of ESBuild plugins. | | | `progress` | boolean | Shows build progress information in the console. Defaults to the `progress` setting of the specified `buildTarget`. | | | `providersFile` | string | Specifies the path to a TypeScript file that provides an array of Angular providers for the test environment. The file must contain a default export of the provider array. | | | `quiet` | boolean | Suppresses the verbose build summary and stats table on each rebuild. Defaults to `true` locally and `false` in CI environments. _Note: this is only supported in Angular versions >= 22.0.0_. | | | `reporters` | array | Specifies the reporters to use during test execution. Each reporter can be a string representing its name, or a tuple containing the name and an options object. Built-in reporters include 'default', 'verbose', 'dots', 'json', 'junit', 'tap', 'tap-flat', and 'html'. You can also provide a path to a custom reporter. | | | `runner` | string | Specifies the test runner to use for test execution. | `"vitest"` | | `runnerConfig` | string | boolean | Specifies the configuration file for the selected test runner. If a string is provided, it will be used as the path to the configuration file. If `true`, the builder will search for a default configuration file (e.g., `vitest.config.ts` or `karma.conf.js`). If `false`, no external configuration file will be used.\nFor Vitest, this enables advanced options and the use of custom plugins. Please note that while the file is loaded, the Angular team does not provide direct support for its specific contents or any third-party plugins used within it. | `false` | | `setupFiles` | array | A list of paths to global setup files that are executed before the test files. The application's polyfills and the Angular TestBed are always initialized before these files. | | | `tsConfig` | string | The path to the TypeScript configuration file, relative to the workspace root. Defaults to `tsconfig.spec.json` in the project root if it exists. If not specified and the default does not exist, the `tsConfig` from the specified `buildTarget` will be used. | | | `ui` | boolean | Enables the Vitest UI for interactive test execution. This option is only available for the Vitest runner. | | | `watch` | boolean | Enables watch mode, which re-runs tests when source files change. Defaults to `true` in TTY environments and `false` otherwise. | | --- ## @nx/angular Generators The @nx/angular plugin provides various generators to help you create and configure angular projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/angular introduction](/docs/technologies/angular/introduction). ## `application` Creates an Angular application. ### Examples ###### Simple Application Create an application named `my-app`: ```bash nx g @nx/angular:application apps/my-app ``` ###### Specify style extension Create an application named `my-app` in the `my-dir` directory and use `scss` for styles: ```bash nx g @nx/angular:app my-dir/my-app --style=scss ``` ###### Single File Components application Create an application with Single File Components (inline styles and inline templates): ```bash nx g @nx/angular:app apps/my-app --inlineStyle --inlineTemplate ``` ###### Set custom prefix and tags Set the prefix to apply to generated selectors and add tags to the application (used for linting). ```bash nx g @nx/angular:app apps/my-app --prefix=admin --tags=scope:admin,type:ui ``` **Usage:** ```bash nx generate @nx/angular:application [options] ``` **Aliases:** `app` **Arguments:** ```bash nx generate @nx/angular:application [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--backendProject` | string | Backend project that provides data to this application. This sets up `proxy.config.json`. | | | `--bundler` | string | Bundler to use to build the application. | `"esbuild"` | | `--e2eTestRunner` | string | Test runner to use for end to end (E2E) tests. | `"playwright"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--formatter` | string | The tool to use for code formatting. | | | `--inlineStyle` | boolean | Specifies if the style will be in the ts file. | `false` | | `--inlineTemplate` | boolean | Specifies if the template will be in the ts file. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--minimal` | boolean | Generate a Angular app with a minimal setup. | `false` | | `--name` | string | The name of the application. | | | `--port` | number | The port at which the remote application should be served. | | | `--prefix` | string | The prefix to apply to generated selectors. | `"app"` | | `--rootProject` | boolean | Create an application at the root of the workspace. | `false` | | `--routing` | boolean | Enable routing for the application. | `true` | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--skipTests` | boolean | Skip creating spec files. | `false` | | `--ssr` | boolean | Creates an application with Server-Side Rendering (SSR) and Static Site Generation (SSG/Prerendering) enabled. | `false` | | `--standalone` | boolean | Generate an application that is setup to use standalone components. | `true` | | `--strict` | boolean | Create an application with stricter type checking and build optimization options. | `true` | | `--style` | string | The file extension to be used for style files. | `"css"` | | `--tags` | string | Add tags to the application (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests. `vitest-angular` uses the `@angular/build:unit-test` executor (requires Angular v21+ and the `esbuild` bundler). `vitest-analog` uses AnalogJS-based setup with `@nx/vitest`. It defaults to `vitest-angular` when using the `esbuild` bundler for Angular versions >= 21.0.0, `vitest-analog` when using other bundlers on Angular >= 21.0.0, otherwise `jest`. | | | `--viewEncapsulation` | string | Specifies the view encapsulation strategy. | | | `--zoneless` | boolean | Generate an application that does not use `zone.js`. It defaults to `true`. _Note: this is only supported in Angular versions >= 21.0.0_ | | ## `component` Creates a new Angular component. ### Examples ###### Simple Component Generate a component named `Card` at `apps/my-app/src/lib/card/card.ts`: ```bash nx g @nx/angular:component apps/my-app/src/lib/card/card.ts ``` ###### Without Providing the File Extension Generate a component named `Card` at `apps/my-app/src/lib/card/card.ts`: ```bash nx g @nx/angular:component apps/my-app/src/lib/card/card ``` ###### With Different Symbol Name Generate a component named `Custom` at `apps/my-app/src/lib/card/card.ts`: ```bash nx g @nx/angular:component apps/my-app/src/lib/card/card --name=custom ``` ###### With a Component Type Generate a component named `CardComponent` at `apps/my-app/src/lib/card/card.component.ts`: ```bash nx g @nx/angular:component apps/my-app/src/lib/card/card --type=component ``` ###### Single File Component Create a component named `Card` with inline styles and inline template: ```bash nx g @nx/angular:component apps/my-app/src/lib/card/card --inlineStyle --inlineTemplate ``` ###### Component with OnPush Change Detection Strategy Create a component named `Card` with `OnPush` Change Detection Strategy: ```bash nx g @nx/angular:component apps/my-app/src/lib/card/card --changeDetection=OnPush ``` **Usage:** ```bash nx generate @nx/angular:component [options] ``` **Aliases:** `c` **Arguments:** ```bash nx generate @nx/angular:component [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--changeDetection` | string | The change detection strategy to use in the new component. It defaults to `OnPush` for Angular versions >= 22.0.0 and `Default` for lower versions. _Note: the `Eager` value is only supported in Angular versions >= 22.0.0_. | | | `--displayBlock` | boolean | Specifies if the style will contain `:host { display: block; }`. | `false` | | `--export` | boolean | Specifies if the component should be exported in the declaring `NgModule`. Additionally, if the project is a library, the component will be exported from the project's entry point (normally `index.ts`) if the module it belongs to is also exported or if the component is standalone. | `false` | | `--exportDefault` | boolean | Use default export for the component instead of a named export. | `false` | | `--inlineStyle` | boolean | Include styles inline in the component.ts file. Only CSS styles can be included inline. By default, an external styles file is created and referenced in the component.ts file. | `false` | | `--inlineTemplate` | boolean | Include template inline in the component.ts file. By default, an external template file is created and referenced in the component.ts file. | `false` | | `--module` | string | The filename or path to the NgModule that will declare this component. | | | `--name` | string | The component symbol name. Defaults to the last segment of the file path. | | | `--ngHtml` | boolean | Generate component template files with an '.ng.html' file extension instead of '.html'. | `false` | | `--prefix` | string | The prefix to apply to the generated component selector. | | | `--selector` | string | The HTML selector to use for this component. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipImport` | boolean | Do not import this component into the owning NgModule. | `false` | | `--skipSelector` | boolean | Specifies if the component should have a selector or not. | `false` | | `--skipTests` | boolean | Do not create `spec.ts` test files for the new component. | `false` | | `--standalone` | boolean | Whether the generated component is standalone. | `true` | | `--style` | string | The file extension or preprocessor to use for style files, or `none` to skip generating the style file. | `"css"` | | `--type` | string | Append a custom type to the component's filename. No type is appended unless specified. | | | `--viewEncapsulation` | string | The view encapsulation strategy to use in the new component. | | ## `component-test` Create a `*.cy.ts` file for Cypress component testing for an Angular component. ### Examples :::caution[Can I use component testing?] Angular component testing with Nx requires **Cypress version 10.7.0** and up. You can migrate with to v11 via the [migrate-to-cypress-11 generator](/nx-api/cypress/generators/migrate-to-cypress-11). ::: This generator is used to create a Cypress component test file for a given Angular component. ```shell nx g @nx/angular:component-test --project=my-cool-angular-project --componentName=CoolBtnComponent --componentDir=src/cool-btn --componentFileName=cool-btn.component ``` Test file are generated with the `.cy.ts` suffix. this is to prevent colliding with any existing `.spec.` files contained in the project. It's currently expected the generated `.cy.ts` file will live side by side with the component. It is also assumed the project is already setup for component testing. If it isn't, then you can run the [cypress-component-project generator](/nx-api/angular/generators/cypress-component-configuration) to set up the project for component testing. **Usage:** ```bash nx generate @nx/angular:component-test [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--componentDir` | string [**required**] | Relative path to the folder that contains the component from the project root. | | | `--componentFileName` | string [**required**] | File name that contains the component without the `.ts` extension. | | | `--componentName` | string [**required**] | Class name of the component to create a test for. | | | `--project` | string [**required**] | The name of the project where the component is located. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | ## `convert-to-application-executor` Converts a project or all projects using one of the `@angular-devkit/build-angular:browser`, `@angular-devkit/build-angular:browser-esbuild`, `@nx/angular:browser` and `@nx/angular:browser-esbuild` executors to use the `@nx/angular:application` executor or the `@angular-devkit/build-angular:application` builder. If the converted target is using one of the `@nx/angular` executors, the `@nx/angular:application` executor will be used. Otherwise, the `@angular-devkit/build-angular:application` builder will be used. **Usage:** ```bash nx generate @nx/angular:convert-to-application-executor [options] ``` **Arguments:** ```bash nx generate @nx/angular:convert-to-application-executor [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--skipFormat` | boolean | Skip formatting files. | `false` | ## `convert-to-rspack` Creates an Angular application. **Usage:** ```bash nx generate @nx/angular:convert-to-rspack [options] ``` **Arguments:** ```bash nx generate @nx/angular:convert-to-rspack [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipInstall` | boolean | Skip installing dependencies. | `false` | ## `convert-to-with-mf` Converts an old micro frontend configuration to use the new withModuleFederation helper. It will run successfully if the following conditions are met: - Is either a host or remote application - Shared npm package configurations have not been modified - Name used to identify the Micro Frontend application matches the project name {% callout type="warning" title="Overrides" %}This generator will overwrite your webpack config. If you have additional custom configuration in your config file, it will be lost!{% /callout %}. **Usage:** ```bash nx generate @nx/angular:convert-to-with-mf [options] ``` **Arguments:** ```bash nx generate @nx/angular:convert-to-with-mf [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--skipFormat` | boolean | Skip formatting files. | `false` | ## `cypress-component-configuration` Add a Cypress component testing configuration to an existing project. Cypress v10.7.0 or higher is required. :::caution[Can I use component testing?] Angular component testing with Nx requires **Cypress version 10.7.0** and up. You can migrate to v11 via the [migrate-to-cypress-11 generator](/nx-api/cypress/generators/migrate-to-cypress-11). This generator is for Cypress based component testing. ::: This generator is designed to get your Angular project up and running with Cypress Component Testing. ```shell nx g @nx/angular:cypress-component-configuration --project=my-cool-angular-project ``` Running this generator, adds the required files to the specified project with a preconfigured `cypress.config.ts` designed for Nx workspaces. ```ts title="cypress.config.ts" import { defineConfig } from 'cypress'; import { nxComponentTestingPreset } from '@nx/angular/plugins/component-testing'; export default defineConfig({ component: nxComponentTestingPreset(__filename), }); ``` Here is an example on how to add custom options to the configuration ```ts title="cypress.config.ts" import { defineConfig } from 'cypress'; import { nxComponentTestingPreset } from '@nx/angular/plugins/component-testing'; export default defineConfig({ component: { ...nxComponentTestingPreset(__filename), // extra options here }, }); ``` ### Specifying a Build Target Component testing requires a _build target_ to correctly run the component test dev server. This option can be manually specified with `--build-target=some-angular-app:build`, but Nx will infer this usage from the [project graph](/concepts/mental-model#the-project-graph) if one isn't provided. For Angular projects, the build target needs to be using the `@nx/angular:webpack-browser` or `@angular-devkit/build-angular:browser` executor. The generator will throw an error if a build target can't be found and suggest passing one in manually. Letting Nx infer the build target by default ```shell nx g @nx/angular:cypress-component-configuration --project=my-cool-angular-project ``` Manually specifying the build target ```shell nx g @nx/angular:cypress-component-configuration --project=my-cool-angular-project --build-target:some-angular-app:build --generate-tests ``` :::note[Build Target with Configuration] If you're wanting to use a build target with a specific configuration. i.e. `my-app:build:production`, then manually providing `--build-target=my-app:build:production` is the best way to do that. ::: ### Auto Generating Tests You can optionally use the `--generate-tests` flag to generate a test file for each component in your project. ```shell nx g @nx/angular:cypress-component-configuration --project=my-cool-angular-project --generate-tests ``` ### Running Component Tests A new `component-test` target will be added to the specified project to run your component tests. ```shell nx g component-test my-cool-angular-project ``` Here is an example of the project configuration that is generated. The `--build-target` option is added as the `devServerTarget` which can be changed as needed. ```json title="project.json" { "targets" { "component-test": { "executor": "@nx/cypress:cypress", "options": { "cypressConfig": "/cypress.config.ts", "testingType": "component", "devServerTarget": "some-angular-app:build", "skipServe": true } } } } ``` ### What is bundled When the project being tested is a dependent of the specified `--build-target`, then **assets, scripts, and styles** are applied to the component being tested. You can determine if the project is dependent by using the [project graph](/features/explore-graph). If there is no link between the two projects, then the **assets, scripts, and styles** won't be included in the build; therefore, they will not be applied to the component. To have a link between projects, you can import from the project being tested into the specified `--build-target` project, or set the `--build-target` project to [implicitly depend](/reference/project-configuration#implicitdependencies) on the project being tested. Nx also supports [React component testing](/nx-api/react/generators/cypress-component-configuration). **Usage:** ```bash nx generate @nx/angular:cypress-component-configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string [**required**] | The name of the project to add cypress component testing configuration to | | | `--buildTarget` | string | A build target used to configure Cypress component testing in the format of `project:target[:configuration]`. The build target should be an angular app. If not provided we will try to infer it from your projects usage. | | | `--generateTests` | boolean | Generate default component tests for existing components in the project | `false` | | `--skipFormat` | boolean | Skip formatting files | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | ## `directive` Creates a new Angular directive. **Usage:** ```bash nx generate @nx/angular:directive [options] ``` **Aliases:** `d` **Arguments:** ```bash nx generate @nx/angular:directive [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--export` | boolean | The declaring NgModule exports this directive. | `false` | | `--module` | string | The filename of the declaring NgModule. | | | `--name` | string | The directive symbol name. Defaults to the last segment of the file path. | | | `--prefix` | string | A prefix to apply to generated selectors. | | | `--selector` | string | The HTML selector to use for this directive. | | | `--skipFormat` | boolean | Skip formatting of files. | `false` | | `--skipImport` | boolean | Do not import this directive into the owning NgModule. | `false` | | `--skipTests` | boolean | Do not create "spec.ts" test files for the new class. | `false` | | `--standalone` | boolean | Whether the generated directive is standalone. | `true` | | `--type` | string | Append a custom type to the directive's filename. No type is appended unless specified. | | ## `federate-module` Create a federated module, which is exposed by a Producer (remote) and can be subsequently loaded by a Consumer (host). **Usage:** ```bash nx generate @nx/angular:federate-module [options] ``` **Arguments:** ```bash nx generate @nx/angular:federate-module [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--name` | string [**required**] | The name of the module. | | | `--remote` | string [**required**] | The name of the Producer (remote). | | | `--e2eTestRunner` | string | Test runner to use for end to end (e2e) tests of the Producer (remote) if it needs to be created. | `"cypress"` | | `--host` | string | The Consumer (host) application for this Producer (remote). | | | `--remoteDirectory` | string | The directory of the new Producer (remote) application if one needs to be created. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--standalone` | boolean | Whether to generate the Producer (remote) application with standalone components if it needs to be created. | `true` | | `--style` | string | The file extension to be used for style files for the Producer (remote) if one needs to be created. | `"css"` | | `--unitTestRunner` | string | Test runner to use for unit tests of the Producer (remote) if it needs to be created. `vitest-analog` uses AnalogJS-based setup with `@nx/vitest`. It defaults to `vitest-analog` for Angular versions >= 21.0.0, otherwise `jest`. | | ## `host` Create an Angular Consumer (Host) Module Federation Application. **Usage:** ```bash nx generate @nx/angular:host [options] ``` **Aliases:** `consumer` **Arguments:** ```bash nx generate @nx/angular:host [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--backendProject` | string | Backend project that provides data to this application. This sets up `proxy.config.json`. | | | `--bundler` | string | The bundler to use for the host application. | `"webpack"` | | `--dynamic` | boolean | Should the host application use dynamic federation? | `false` | | `--e2eTestRunner` | string | Test runner to use for end to end (E2E) tests. | `"playwright"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--inlineStyle` | boolean | Specifies if the style will be in the ts file. | `false` | | `--inlineTemplate` | boolean | Specifies if the template will be in the ts file. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | The name to give to the Consumer (host) Angular application. | | | `--port` | number | The port at which the Consumer (host) application should be served. Defaults to 4200. | | | `--prefix` | string | The prefix to apply to generated selectors. | | | `--remotes` | array | The names of the Producers (remote) applications to add to the Consumer (host). | | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--skipPostInstall` | boolean | Do not add or append `ngcc` to the `postinstall` script in `package.json`. | `false` | | `--skipTests` | boolean | Skip creating spec files. | `false` | | `--ssr` | boolean | Whether to configure SSR for the Consumer (host) application | `false` | | `--standalone` | boolean | Whether to generate a Consumer (host) application that uses standalone components. | `true` | | `--strict` | boolean | Create an application with stricter type checking and build optimization options. | `true` | | `--style` | string | The file extension to be used for style files. | `"css"` | | `--tags` | string | Add tags to the application (used for linting). | | | `--typescriptConfiguration` | boolean | Whether the module federation configuration and webpack configuration files should use TS. | `true` | | `--unitTestRunner` | string | Test runner to use for unit tests. `vitest-analog` uses AnalogJS-based setup with `@nx/vitest`. It defaults to `vitest-analog` for Angular versions >= 21.0.0, otherwise `jest`. | | | `--viewEncapsulation` | string | Specifies the view encapsulation strategy. | | | `--zoneless` | boolean | Generate an application that does not use `zone.js`. It defaults to `true`. _Note: this is only supported in Angular versions >= 21.0.0_ | | ## `library` Creates an Angular library. ### Examples ###### Simple Library Creates the `my-ui-lib` library with an `ui` tag: ```bash nx g @nx/angular:library libs/my-ui-lib --tags=ui ``` ###### Publishable Library Creates the `my-lib` library that can be built producing an output following the Angular Package Format (APF) to be distributed as an NPM package: ```bash nx g @nx/angular:library libs/my-lib --publishable --import-path=@my-org/my-lib ``` ###### Buildable Library Creates the `my-lib` library with support for incremental builds: ```bash nx g @nx/angular:library libs/my-lib --buildable ``` ###### Nested Folder & Import Creates the `my-lib` library in the `nested` directory and sets the import path to `@myorg/nested/my-lib`: ```bash nx g @nx/angular:library libs/nested/my-lib --importPath=@myorg/nested/my-lib ``` **Usage:** ```bash nx generate @nx/angular:library [options] ``` **Aliases:** `lib` **Arguments:** ```bash nx generate @nx/angular:library [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--addModuleSpec` | boolean | Add a module spec file. | `false` | | `--buildable` | boolean | Generate a buildable library. | `false` | | `--changeDetection` | string | The change detection strategy to use in the new component. It defaults to `OnPush` for Angular versions >= 22.0.0 and `Default` for lower versions. _Note: the `Eager` value is only supported in Angular versions >= 22.0.0_. Disclaimer: This option is only valid when `--standalone` is set to `true`. | | | `--compilationMode` | string | Specifies the compilation mode to use. If not specified, it will default to `partial` for publishable libraries and to `full` for buildable libraries. The `full` value can not be used for publishable libraries. | | | `--displayBlock` | boolean | Specifies if the component generated style will contain `:host { display: block; }`. Disclaimer: This option is only valid when `--standalone` is set to `true`. | `false` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--flat` | boolean | Ensure the generated standalone component is not placed in a subdirectory. Disclaimer: This option is only valid when `--standalone` is set to `true`. | `false` | | `--importPath` | string | The library name used to import it, like `@myorg/my-awesome-lib`. Must be a valid npm name. | | | `--inlineStyle` | boolean | Include styles inline in the component.ts file. Only CSS styles can be included inline. By default, an external styles file is created and referenced in the component.ts file. Disclaimer: This option is only valid when `--standalone` is set to `true`. | `false` | | `--inlineTemplate` | boolean | Include template inline in the component.ts file. By default, an external template file is created and referenced in the component.ts file. Disclaimer: This option is only valid when `--standalone` is set to `true`. | `false` | | `--lazy` | boolean | Add `RouterModule.forChild` when set to true, and a simple array of routes when set to false. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | The name of the library. | | | `--parent` | string | Path to the parent route configuration using `loadChildren` or `children`, depending on what `lazy` is set to. | | | `--prefix` | string | The prefix to apply to generated selectors. | | | `--publishable` | boolean | Generate a publishable library. | `false` | | `--routing` | boolean | Add router configuration. See `lazy` for more information. | `false` | | `--selector` | string | The HTML selector to use for this component. Disclaimer: This option is only valid when `--standalone` is set to `true`. | | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipModule` | boolean | Whether to skip the creation of a default module when generating the library. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--skipSelector` | boolean | Specifies if the component should have a selector or not. Disclaimer: This option is only valid when `--standalone` is set to `true`. | `false` | | `--skipTests` | boolean | Do not create `spec.ts` test files for the new component. Disclaimer: This option is only valid when `--standalone` is set to `true`. | `false` | | `--skipTsConfig` | boolean | Do not update `tsconfig.json` for development experience. | `false` | | `--standalone` | boolean | Generate a library that uses a standalone component instead of a module as the entry point. | `true` | | `--strict` | boolean | Create a library with stricter type checking and build optimization options. | `true` | | `--style` | string | The file extension or preprocessor to use for style files, or `none` to skip generating the style file. Disclaimer: This option is only valid when `--standalone` is set to `true`. | `"css"` | | `--tags` | string | Add tags to the library (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests. `vitest-angular` uses the `@nx/angular:unit-test` executor (requires Angular v21+ and a buildable/publishable library). `vitest-analog` uses AnalogJS-based setup with `@nx/vitest`. It defaults to `vitest-angular` for buildable/publishable libraries on Angular >= 21.0.0, `vitest-analog` for non-buildable libraries on Angular >= 21.0.0, otherwise `jest`. | | | `--viewEncapsulation` | string | The view encapsulation strategy to use in the new component. Disclaimer: This option is only valid when `--standalone` is set to `true`. | | ## `library-secondary-entry-point` Creates a secondary entry point for an Angular publishable library. ### Examples ###### Basic Usage Create a secondary entrypoint named `button` in the `ui` library. ```bash nx g @nx/angular:library-secondary-entry-point --library=ui --name=button ``` ###### Skip generating module Create a secondary entrypoint named `button` in the `ui` library but skip creating an NgModule. ```bash nx g @nx/angular:library-secondary-entry-point --library=ui --name=button --skipModule ``` **Usage:** ```bash nx generate @nx/angular:library-secondary-entry-point [options] ``` **Aliases:** `secondary-entry-point`, `entry-point` **Arguments:** ```bash nx generate @nx/angular:library-secondary-entry-point [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--library` | string [**required**] | The name of the library to create the secondary entry point for. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipModule` | boolean | Skip generating a module for the secondary entry point. | `false` | ## `ngrx-feature-store` Add an NgRx Feature Store to an application or library. **Usage:** ```bash nx generate @nx/angular:ngrx-feature-store [options] ``` **Arguments:** ```bash nx generate @nx/angular:ngrx-feature-store [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--barrels` | boolean | Use barrels to re-export actions, state and selectors. | `false` | | `--directory` | string | The name of the folder used to contain/group the generated NgRx files. | `"+state"` | | `--facade` | boolean | Create a Facade class for the the feature. | `false` | | `--minimal` | boolean | Only register the feature state. | `false` | | `--parent` | string | The path to the file where the state will be registered. For NgModule usage, this will be your Feature Module. For Standalone API usage, this will be your Routes definition file for your feature state. The host directory will create/use the new state directory. | | | `--route` | string | The route that the Standalone NgRx Providers should be added to. | `"''"` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipImport` | boolean | Generate NgRx feature files without registering the feature in the NgModule. | `false` | | `--skipPackageJson` | boolean | Do not update the `package.json` with NgRx dependencies. | `false` | ## `ngrx-root-store` Adds NgRx support to an application. **Usage:** ```bash nx generate @nx/angular:ngrx-root-store [options] ``` **Arguments:** ```bash nx generate @nx/angular:ngrx-root-store [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--addDevTools` | boolean | Instrument the Store Devtools. | `false` | | `--directory` | string | The name of the folder used to contain/group the generated NgRx files. | `"+state"` | | `--facade` | boolean | Create a Facade class for the the feature. | `false` | | `--minimal` | boolean | Only register the root state management setup or also generate a global feature state. | `true` | | `--name` | string | Name of the NgRx state, such as `products` or `users`. Recommended to use the plural form of the name. | | | `--route` | string | The route that the Standalone NgRx Providers should be added to. | `"''"` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipImport` | boolean | Generate NgRx feature files without registering the feature in the NgModule. | `false` | | `--skipPackageJson` | boolean | Do not update the `package.json` with NgRx dependencies. | `false` | ## `pipe` Creates an Angular pipe. **Usage:** ```bash nx generate @nx/angular:pipe [options] ``` **Aliases:** `p` **Arguments:** ```bash nx generate @nx/angular:pipe [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--export` | boolean | The declaring NgModule exports this pipe. | `false` | | `--module` | string | The filename of the declaring NgModule. | | | `--name` | string | The pipe symbol name. Defaults to the last segment of the file path. | | | `--skipFormat` | boolean | Skip formatting of files. | `false` | | `--skipImport` | boolean | Do not import this pipe into the owning NgModule. | `false` | | `--skipTests` | boolean | Do not create "spec.ts" test files for the new pipe. | `false` | | `--standalone` | boolean | Whether the generated pipe is standalone. | `true` | | `--typeSeparator` | string | The separator character to use before the type within the generated file's name. For example, if you set the option to `.`, the file will be named `example.pipe.ts`. It defaults to '-'. | | ## `remote` Create an Angular Producer (Remote) Module Federation Application. **Usage:** ```bash nx generate @nx/angular:remote [options] ``` **Aliases:** `producer` **Arguments:** ```bash nx generate @nx/angular:remote [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--backendProject` | string | Backend project that provides data to this application. This sets up `proxy.config.json`. | | | `--bundler` | string | The bundler to use for the remote application. | `"webpack"` | | `--e2eTestRunner` | string | Test runner to use for end to end (E2E) tests. | `"playwright"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--host` | string | The name of the Consumer (host) app to attach this Producer (remote) app to. | | | `--inlineStyle` | boolean | Specifies if the style will be in the ts file. | `false` | | `--inlineTemplate` | boolean | Specifies if the template will be in the ts file. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | The name to give to the Producer (remote) Angular app. | | | `--port` | number | The port on which this app should be served. | | | `--prefix` | string | The prefix to apply to generated selectors. | | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--skipTests` | boolean | Skip creating spec files. | `false` | | `--ssr` | boolean | Whether to configure SSR for the Producer (remote) application to be consumed by a Consumer (host) application using SSR. | `false` | | `--standalone` | boolean | Whether to generate a Producer (remote) application with standalone components. | `true` | | `--strict` | boolean | Create an application with stricter type checking and build optimization options. | `true` | | `--style` | string | The file extension to be used for style files. | `"css"` | | `--tags` | string | Add tags to the application (used for linting). | | | `--typescriptConfiguration` | boolean | Whether the module federation configuration and webpack configuration files should use TS. | `true` | | `--unitTestRunner` | string | Test runner to use for unit tests. `vitest-analog` uses AnalogJS-based setup with `@nx/vitest`. It defaults to `vitest-analog` for Angular versions >= 21.0.0, otherwise `jest`. | | | `--viewEncapsulation` | string | Specifies the view encapsulation strategy. | | | `--zoneless` | boolean | Generate an application that does not use `zone.js`. It defaults to `true`. _Note: this is only supported in Angular versions >= 21.0.0_ | | ## `scam` Creates a new Angular SCAM. **Usage:** ```bash nx generate @nx/angular:scam [options] ``` **Arguments:** ```bash nx generate @nx/angular:scam [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--changeDetection` | string | The change detection strategy to use in the new component. It defaults to `OnPush` for Angular versions >= 22.0.0 and `Default` for lower versions. _Note: the `Eager` value is only supported in Angular versions >= 22.0.0_. | | | `--displayBlock` | boolean | Specifies if the style will contain `:host { display: block; }`. | `false` | | `--export` | boolean | Specifies if the SCAM should be exported from the project's entry point (normally `index.ts`). It only applies to libraries. | `true` | | `--inlineScam` | boolean | Create the `NgModule` in the same file as the component. | `true` | | `--inlineStyle` | boolean | Include styles inline in the `component.ts` file. Only CSS styles can be included inline. By default, an external styles file is created and referenced in the `component.ts` file. | `false` | | `--inlineTemplate` | boolean | Include template inline in the `component.ts` file. By default, an external template file is created and referenced in the `component.ts` file. | `false` | | `--name` | string | The component symbol name. Defaults to the last segment of the file path. | | | `--prefix` | string | The prefix to apply to the generated component selector. | | | `--selector` | string | The `HTML` selector to use for this component. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipSelector` | boolean | Specifies if the component should have a selector or not. | `false` | | `--skipTests` | boolean | Do not create `spec.ts` test files for the new component. | `false` | | `--style` | string | The file extension or preprocessor to use for style files, or 'none' to skip generating the style file. | `"css"` | | `--type` | string | Append a custom type to the component's filename. No type is appended unless specified. | | | `--viewEncapsulation` | string | The view encapsulation strategy to use in the new component. | | ## `scam-directive` Creates a new, generic Angular directive definition in the given or default project. **Usage:** ```bash nx generate @nx/angular:scam-directive [options] ``` **Arguments:** ```bash nx generate @nx/angular:scam-directive [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--export` | boolean | Specifies if the SCAM should be exported from the project's entry point (normally `index.ts`). It only applies to libraries. | `true` | | `--inlineScam` | boolean | Create the `NgModule` in the same file as the Directive. | `true` | | `--name` | string | The directive symbol name. Defaults to the last segment of the file path. | | | `--prefix` | string | The prefix to apply to the generated directive selector. | | | `--selector` | string | The `HTML` selector to use for this directive. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipTests` | boolean | Do not create `spec.ts` test files for the new directive. | `false` | | `--type` | string | Append a custom type to the directive's filename. No type is appended unless specified. | | ## `scam-pipe` Creates a new, generic Angular pipe definition in the given or default project. **Usage:** ```bash nx generate @nx/angular:scam-pipe [options] ``` **Arguments:** ```bash nx generate @nx/angular:scam-pipe [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--export` | boolean | Specifies if the SCAM should be exported from the project's entry point (normally `index.ts`). It only applies to libraries. | `true` | | `--inlineScam` | boolean | Create the NgModule in the same file as the Pipe. | `true` | | `--name` | string | The pipe symbol name. Defaults to the last segment of the file path. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipTests` | boolean | Do not create `spec.ts` test files for the new pipe. | `false` | | `--typeSeparator` | string | The separator character to use before the type within the generated file's name. For example, if you set the option to `.`, the file will be named `example.pipe.ts`. It defaults to '-'. | | ## `scam-to-standalone` Convert an Inline SCAM to a Standalone Component. ### Examples ###### Basic Usage This generator allows you to convert an Inline SCAM to a Standalone Component. It's important that the SCAM you wish to convert has it's NgModule within the same file for the generator to be able to correctly convert the component to Standalone. ```bash nx g @nx/angular:scam-to-standalone --component=libs/mylib/src/lib/myscam/myscam.ts --project=mylib ``` **Usage:** ```bash nx generate @nx/angular:scam-to-standalone [options] ``` **Arguments:** ```bash nx generate @nx/angular:scam-to-standalone [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project containing the SCAM. | | | `--skipFormat` | boolean | Skip formatting the workspace after the generator completes. | | ## `setup-mf` Create Module Federation configuration files for given Angular Application. ### Examples The `setup-mf` generator is used to add Module Federation support to existing applications. ###### Convert to Host To convert an existing application to a host application, run the following ```bash nx g setup-mf myapp --mfType=host --routing=true ``` ###### Convert to Remote To convert an existing application to a remote application, run the following ```bash nx g setup-mf myapp --mfType=remote --routing=true ``` ###### Convert to Remote and attach to a host application To convert an existing application to a remote application and attach it to an existing host application name `myhostapp`, run the following ```bash nx g setup-mf myapp --mfType=remote --routing=true --host=myhostapp ``` ###### Convert to Host and attach to existing remote applications To convert an existing application to a host application and attaching existing remote applications named `remote1` and `remote2`, run the following ```bash nx g setup-mf myapp --mfType=host --routing=true --remotes=remote1,remote2 ``` **Usage:** ```bash nx generate @nx/angular:setup-mf [options] ``` **Arguments:** ```bash nx generate @nx/angular:setup-mf [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--mfType` | string [**required**] | Type of application to generate the Module Federation configuration for. | `"remote"` | | `--e2eProjectName` | string | The project name of the associated E2E project for the application. This is only required for Cypress E2E projects that do not follow the naming convention `-e2e`. | | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--federationType` | string | Use either Static or Dynamic Module Federation pattern for the application. | `"static"` | | `--host` | string | The name of the host application that the remote application will be consumed by. | | | `--port` | number | The port at which the remote application should be served. | | | `--prefix` | string | The prefix to use for any generated component. | | | `--remotes` | array | A list of remote application names that the Consumer (host) application should consume. | | | `--routing` | boolean | Generate a routing setup to allow a Consumer (host) application to route to the Producer (remote) application. | | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipE2E` | boolean | Do not set up E2E related config. | `false` | | `--skipFormat` | boolean | Skip formatting the workspace after the generator completes. | | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--standalone` | boolean | Whether the application is a standalone application. | `true` | | `--typescriptConfiguration` | boolean | Whether the module federation configuration and webpack configuration files should use TS. | `true` | ## `setup-ssr` Create the additional configuration required to enable SSR via Angular Universal for an Angular application. **Usage:** ```bash nx generate @nx/angular:setup-ssr [options] ``` **Arguments:** ```bash nx generate @nx/angular:setup-ssr [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--hydration` | boolean | Set up Hydration for the SSR application. | `true` | | `--main` | string | The name of the main entry-point file. | `"main.server.ts"` | | `--rootModuleClassName` | string | The name of the root module class. | `"AppServerModule"` | | `--rootModuleFileName` | string | The name of the root module file | `"app.server.module.ts"` | | `--serverFileName` | string | The name of the Express server file. | `"server.ts"` | | `--serverPort` | number | The port for the Express server. | `4000` | | `--skipFormat` | boolean | Skip formatting the workspace after the generator completes. | | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--standalone` | boolean | Use Standalone Components to bootstrap SSR. | | ## `stories` Creates Storybook stories/specs for all Angular components declared in a project. This generator will generate stories for all your components in your project. The stories will be generated using [Component Story Format 3 (CSF3)](https://storybook.js.org/blog/storybook-csf3-is-here/). ```bash nx g @nx/angular:stories project-name ``` You can read more about how this generator works, in the [Storybook for Angular overview page](/recipes/storybook/overview-angular#auto-generate-stories). When running this generator, you will be prompted to provide the following: - The `name` of the project you want to generate the configuration for. - Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, a `play` function will be added to your stories, and all the necessary dependencies will be installed. You can read more about this in the [Nx Storybook interaction tests documentation page](/recipes/storybook/storybook-interaction-tests#setup-storybook-interaction-tests). You must provide a `name` for the generator to work. By default, this generator will also set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing). If you don't want to set up Storybook interaction tests, you can pass the `--interactionTests=false` option, but it's not recommended. There are a number of other options available. Let's take a look at some examples. ### Examples #### Ignore certain paths when generating stories ```bash nx g @nx/angular:stories ui --ignorePaths=libs/ui/src/not-stories/**,**/**/src/**/*.other.* ``` This will generate stories for all the components in the `ui` project, except for the ones in the `libs/ui/src/not-stories` directory, and also for components that their file name is of the pattern `*.other.*`. This is useful if you have a project that contains components that are not meant to be used in isolation, but rather as part of a larger component. By default, Nx will ignore the following paths: ```text *.stories.ts, *.stories.tsx, *.stories.js, *.stories.jsx, *.stories.mdx ``` but you can change this behaviour easily, as explained above. **Usage:** ```bash nx generate @nx/angular:stories [options] ``` **Arguments:** ```bash nx generate @nx/angular:stories [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--ignorePaths` | array | Paths to ignore when looking for components. | `["*.stories.ts,*.stories.tsx,*.stories.js,*.stories.jsx,*.stories.mdx"]` | | `--interactionTests` | boolean | Set up Storybook interaction tests. | `true` | | `--skipFormat` | boolean | Skip formatting files. | `false` | ## `storybook-configuration` Adds Storybook configuration to a project to be able to use and create stories. This generator will set up Storybook for your **Angular** project. By default, Storybook v10 is used. ```bash nx g @nx/angular:storybook-configuration project-name ``` You can read more about how this generator works, in the [Storybook for Angular overview page](/recipes/storybook/overview-angular#generate-storybook-configuration-for-an-angular-project). When running this generator, you will be prompted to provide the following: - The `name` of the project you want to generate the configuration for. - Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, a `play` function will be added to your stories, and all the necessary dependencies will be installed. Also, a `test-storybook` target will be generated in your project's `project.json`, with a command to invoke the [Storybook `test-runner`](https://storybook.js.org/docs/angular/writing-tests/test-runner). You can read more about this in the [Nx Storybook interaction tests documentation page](/recipes/storybook/storybook-interaction-tests#setup-storybook-interaction-tests). - Whether you want to `generateStories` for the components in your project. If you choose `yes`, a `.stories.ts` file will be generated next to each of your components in your project. You must provide a `name` for the generator to work. By default, this generator will also set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing). If you don't want to set up Storybook interaction tests, you can pass the `--interactionTests=false` option, but it's not recommended. There are a number of other options available. Let's take a look at some examples. ### Examples #### Generate Storybook configuration ```bash nx g @nx/angular:storybook-configuration ui ``` This will generate Storybook configuration for the `ui` project using TypeScript for the Storybook configuration files (the files inside the `.storybook` directory, eg. `.storybook/main.ts`). #### Ignore certain paths when generating stories ```bash nx g @nx/angular:storybook-configuration ui --generateStories=true --ignorePaths=libs/ui/src/not-stories/**,**/**/src/**/*.other.*,apps/my-app/**/*.something.ts ``` This will generate a Storybook configuration for the `ui` project and generate stories for all components in the `libs/ui/src/lib` directory, except for the ones in the `libs/ui/src/not-stories` directory, and the ones in the `apps/my-app` directory that end with `.something.ts`, and also for components that their file name is of the pattern `*.other.*`. This is useful if you have a project that contains components that are not meant to be used in isolation, but rather as part of a larger component. By default, Nx will ignore the following paths: ```text *.stories.ts, *.stories.tsx, *.stories.js, *.stories.jsx, *.stories.mdx ``` but you can change this behaviour easily, as explained above. #### Generate Storybook configuration using JavaScript ```bash nx g @nx/angular:storybook-configuration ui --tsConfiguration=false ``` By default, our generator generates TypeScript Storybook configuration files. You can choose to use JavaScript for the Storybook configuration files of your project (the files inside the `.storybook` directory, eg. `.storybook/main.js`). **Usage:** ```bash nx generate @nx/angular:storybook-configuration [options] ``` **Arguments:** ```bash nx generate @nx/angular:storybook-configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--configureStaticServe` | boolean | Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times. | `true` | | `--generateStories` | boolean | Specifies whether to automatically generate `*.stories.ts` files for components declared in this project or not. | `true` | | `--ignorePaths` | array | Paths to ignore when looking for components. | `["*.stories.ts,*.stories.tsx,*.stories.js,*.stories.jsx,*.stories.mdx"]` | | `--interactionTests` | boolean | Set up Storybook interaction tests. | `true` | | `--linter` | string | The tool to use for running lint checks. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--tsConfiguration` | boolean | Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js. | `true` | ## `web-worker` Creates a new, generic web worker definition in the given or default project. ### Examples ###### Simple Usage The basic usage of the `web-worker` generator is defined below. You must provide a name for the web worker and the project to assign it to. ```bash nx g @nx/angular:web-worker myWebWorker --project=myapp ``` **Usage:** ```bash nx generate @nx/angular:web-worker [options] ``` **Arguments:** ```bash nx generate @nx/angular:web-worker [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string [**required**] | The name of the project. | | | `--path` | string | The path at which to create the worker file, relative to the current workspace. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--snippet` | boolean | Add a worker creation snippet in a sibling file of the same name. | `true` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/angular: --help ``` --- ## Guides {% index_page_cards path="technologies/angular/guides" /%} --- ## Nx with Angular [Angular](https://angular.dev) is a framework for building web applications, and Nx is the platform for scaling your Angular monorepo. Nx has first party support for Angular projects, so if you're familiar with the Angular CLI, `ng`, then the Nx CLI will feel very familiar. The `@nx/angular` plugin adds [generators](#local-development), Angular CLI builder integration (executors) and migrations so you can run Angular tasks through Nx. With Nx, each project has its own configuration instead of a single `angular.json` file, making it more scalable for Angular monorepos. You can use Angular with Nx without the plugin and still get [task caching](/docs/features/cache-task-results), [task orchestration](/docs/features/run-tasks), and the [project graph](/docs/features/explore-graph). ## Why Nx instead of the Angular CLI? Nx does everything the Angular CLI does, and adds monorepo support, enforced module boundaries, task caching, and CI acceleration through [Nx Cloud](/docs/getting-started/nx-cloud). | Feature | Angular CLI | Nx | | ---------------------------------------------------------------------- | ----------- | ------------- | | Generate apps, components, services | ✅ | ✅ | | Build, serve, and test projects | ✅ | ✅ | | Automated updates with migrations | ✅ | ✅ (Enhanced) | | First-class monorepo support | ❌ | ✅ | | [Enforced module boundaries](/docs/features/enforce-module-boundaries) | ❌ | ✅ | | Interactive project graph | ❌ | ✅ | | Build and test only what is affected | ❌ | ✅ | | Local and remote caching | ❌ | ✅ | | Distributed task execution on CI | ❌ | ✅ | | Self-healing CI | ❌ | ✅ | For a detailed comparison and migration notes, see [Nx and the Angular CLI](/docs/kb/nx-and-angular). ## Requirements The `@nx/angular` plugin supports the following package versions. | Package | Supported Versions | | --------------- | ------------------ | | `@angular/core` | >= 20.0.0 < 23.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. For older Nx versions, see the full [Nx and Angular versions matrix](/docs/kb/angular-nx-version-matrix). ## Setup ### Add to an existing workspace {% aside type="note" title="Keep Nx package versions in sync" %} Install the `@nx/angular` version that matches your `nx` version to avoid hard-to-debug errors. See [Keep Nx versions in sync](/docs/kb/keep-nx-versions-in-sync). Using `nx add` helps ensure the correct version is installed and configured. {% /aside %} ```shell nx add @nx/angular ``` Verify the plugin is active by inspecting an Angular project: ```shell nx show project ``` Look for `build`, `serve`, `test`, `lint`, or `e2e` targets generated from `angular.json`. ### Create an Angular monorepo Create a new workspace with an Angular application already set up: ```shell npx create-nx-workspace@latest my-org --template=nrwl/angular-template ``` This scaffolds an Nx monorepo with an Angular app and Nx preconfigured for caching, task running, and CI. You grow the monorepo by generating more [applications](#generate-an-angular-application) and [libraries](#generate-an-angular-library), organized by application and by domain: {% filetree %} - my-org/ - apps/ - store/ - store-e2e/ - libs/ - products/ - feature-product-list/ - data-access/ - shared/ - ui/ - nx.json - package.json - tsconfig.base.json {% /filetree %} {% aside type="tip" title="Learn Nx" %} For a guided, step-by-step walkthrough of building a monorepo with Nx, follow the [Learn Nx tutorial](/docs/getting-started/tutorials/crafting-your-workspace). {% /aside %} ## Configure inferred Angular tasks The `@nx/angular/plugin` plugin reads `angular.json` files that are next to a `package.json`. It registers every Angular project in the file as an Nx project and converts each Angular CLI builder target into an Nx task. Nx preserves the Angular target's configurations and default configuration, then derives cache inputs, outputs, dependencies, and continuous task settings from the configured builder. Configure the plugin in the `plugins` array in `nx.json`: ```json // nx.json { "plugins": [ { "plugin": "@nx/angular/plugin", "options": { "targetNamePrefix": "angular:" } } ] } ``` | Option | Type | Default | Description | | ------------------ | ------ | ------- | ----------------------------------------------------------------------------------- | | `targetNamePrefix` | string | none | Prefix added to every target imported from `angular.json`, such as `angular:build`. | Omit `targetNamePrefix` to keep the target names from `angular.json`. Use `include` and `exclude` glob patterns on the plugin entry to scope which `angular.json` files it processes. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/angular/plugin`. ## Local development ### Generate an Angular application ```shell nx g @nx/angular:app apps/my-app ``` Nx configures the linter your workspace already uses and Playwright for e2e tests. Unit tests use Vitest on Angular 21 and later. Earlier versions use Jest. Read more of the options available for the application generator in the [Angular application generators reference](/docs/technologies/angular/generators#application). ```shell nx serve my-app nx build my-app nx test my-app nx lint my-app nx e2e my-app ``` ### Generate an Angular library ```shell nx g @nx/angular:lib libs/my-lib ``` Nx configures the linter your workspace already uses. Unit tests use Vitest on Angular 21 and later. Earlier versions use Jest. Read more of the options available for the library generator in the [Angular library generators reference](/docs/technologies/angular/generators#library). There are also generators for setting up common items inside your projects, such as components and services: ```shell nx g @nx/angular:component libs/my-lib/src/lib/my-component nx g @nx/angular:service libs/my-lib/src/lib/my-service ``` ```shell nx test my-lib nx lint my-lib ``` ### Buildable and publishable libraries If you need libraries that build or publish, generate them with build output enabled and an import path. This creates build targets you can cache and run in CI. See [Set up incremental builds for Angular libraries](/docs/kb/setup-incremental-builds-angular) for details. ```shell nx g @nx/angular:lib libs/my-lib --publishable --importPath=@my-org/my-lib ``` ### Set up micro frontends As of Nx v23, the `@nx/angular` `host` and `remote` generators are deprecated. For Angular micro frontends, use [`@angular-architects/native-federation`](https://www.npmjs.com/package/@angular-architects/native-federation), with Nx orchestrating, caching, and scoping CI to the affected apps. See [Micro Frontends with Angular](/docs/kb/angular-micro-frontends) for the setup path. ## Structure your Angular monorepo Split each application into small, focused libraries grouped by domain (for example `products`, `checkout`, `shared/ui`). Tag those libraries and [enforce module boundaries](/docs/features/enforce-module-boundaries) to control which libraries can depend on each other. This keeps a growing monorepo maintainable and makes `nx affected` precise. To decide how to split projects, see [folder structure](/docs/kb/folder-structure) and [project size](/docs/kb/project-size). For a worked example of organizing Angular apps into feature, UI, data-access, and utility libraries, see [Architecting Angular Applications](/blog/architecting-angular-applications). ## Set up CI for your Angular monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches. Connecting your workspace to Nx Cloud adds: - [Remote caching](/docs/features/ci-features/remote-cache) to share build and test results across your team and CI machines - [Nx Agents](/docs/features/ci-features/distribute-task-execution) to distribute your Angular builds, tests, and lint tasks across machines - [Self-healing CI](/docs/features/ci-features/self-healing-ci) to propose fixes for failed tasks directly in your editor or PR Connect your workspace: ```shell npx nx connect ``` For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). ## What's next {% cardgrid %} {% linkcard title="Set up CI with Nx Cloud" description="Remote caching, Nx Agents, and self-healing CI." href="/docs/getting-started/setup-ci" /%} {% linkcard title="Learn Nx Tutorial" description="Build a monorepo step-by-step with Nx." href="/docs/getting-started/tutorials/crafting-your-workspace" /%} {% linkcard title="Migrate from Angular CLI" description="Move an existing Angular CLI project into Nx." href="/docs/kb/migrate-angular-cli-to-nx" /%} {% linkcard title="Micro Frontends with Angular" description="Build Angular micro frontends with Native Federation." href="/docs/kb/angular-micro-frontends" /%} {% linkcard title="Tailwind CSS with Angular" description="Add Tailwind CSS to Angular apps and libraries." href="/docs/kb/using-tailwind-css-with-angular-projects" /%} {% linkcard title="Angular Generators Reference" description="All @nx/angular generators." href="/docs/technologies/angular/generators" /%} {% linkcard title="Angular Executors Reference" description="All @nx/angular executors." href="/docs/technologies/angular/executors" /%} {% /cardgrid %} --- ## Angular Migration {% index_page_cards path="technologies/angular/migration" /%} --- ## @nx/angular Migrations For an overview of the plugin and setup instructions, see the [@nx/angular introduction](/docs/technologies/angular/introduction). The @nx/angular plugin provides various migrations to help you migrate to newer versions of angular projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.2.x ### 23.2.0-package-updates **Version**: 23.2.0-beta.5 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular/cli` | `~22.1.0` | Updated only | `@angular-devkit/build-angular` | `~22.1.0` | Updated only | `@angular-devkit/core` | `~22.1.0` | Updated only | `@angular-devkit/schematics` | `~22.1.0` | Updated only | `@angular/build` | `~22.1.0` | Updated only | `@angular/pwa` | `~22.1.0` | Updated only | `@angular/ssr` | `~22.1.0` | Updated only | `@schematics/angular` | `~22.1.0` | Updated only | `@angular-devkit/architect` | `~0.2201.0` | Updated only | `@angular-devkit/build-webpack` | `~0.2201.0` | Updated only | `@angular/core` | `~22.1.0` | Added if not installed | `@angular/material` | `~22.1.0` | Updated only | `@angular/cdk` | `~22.1.0` | Updated only | `@angular/google-maps` | `~22.1.0` | Updated only | `ng-packagr` | `~22.1.0` | Updated only | `zone.js` | `~0.16.2` | Updated only ### 23.2.0-angular-eslint-package-updates **Version**: 23.2.0-beta.5 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `angular-eslint` | `^22.1.0` | Updated only | `@angular-eslint/eslint-plugin` | `^22.1.0` | Updated only | `@angular-eslint/eslint-plugin-template` | `^22.1.0` | Updated only | `@angular-eslint/template-parser` | `^22.1.0` | Updated only | `@angular-eslint/utils` | `^22.1.0` | Updated only | `@angular-eslint/schematics` | `^22.1.0` | Updated only | `@angular-eslint/test-utils` | `^22.1.0` | Updated only | `@angular-eslint/builder` | `^22.1.0` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^22.1.0` | Updated only ### 23.2.0-@angular-eslint-package-updates **Version**: 23.2.0-beta.5 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-eslint/eslint-plugin` | `^22.1.0` | Updated only | `@angular-eslint/eslint-plugin-template` | `^22.1.0` | Updated only | `@angular-eslint/template-parser` | `^22.1.0` | Updated only | `@angular-eslint/utils` | `^22.1.0` | Updated only | `@angular-eslint/schematics` | `^22.1.0` | Updated only | `@angular-eslint/test-utils` | `^22.1.0` | Updated only | `@angular-eslint/builder` | `^22.1.0` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^22.1.0` | Updated only ## 23.1.x ### `update-23-1-0-rename-ssr-experimental-platform` **Version**: 23.1.0-beta.0 Rename the `ssr.experimentalPlatform` option to `ssr.platform` in `@angular/build:application`, `@angular-devkit/build-angular:application`, and `@nx/angular:application` targets, matching the Angular v22 rename. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=22.0.0` | #### Rename `ssr.experimentalPlatform` to `ssr.platform` Angular v22 renamed the `@angular/build:application` SSR `experimentalPlatform` option to `platform` and removed the old key. This migration renames `ssr.experimentalPlatform` to `ssr.platform` in the options and configurations of application targets, both in `project.json` and in the `nx.json` `targetDefaults`, covering the `@angular/build:application` and `@angular-devkit/build-angular:application` builders as well as the `@nx/angular:application` executor that wraps them. #### Examples ##### `project.json` Before: ```jsonc {9} // project.json { "targets": { "build": { "executor": "@nx/angular:application", "options": { "ssr": { "entry": "src/server.ts", "experimentalPlatform": "neutral", }, }, }, }, } ``` After: ```jsonc {9} // project.json { "targets": { "build": { "executor": "@nx/angular:application", "options": { "ssr": { "entry": "src/server.ts", "platform": "neutral", }, }, }, }, } ``` ##### `nx.json` (`targetDefaults`) Before: ```jsonc {8} // nx.json { "targetDefaults": { "@nx/angular:application": { "options": { "ssr": { "entry": "src/server.ts", "experimentalPlatform": "neutral", }, }, }, }, } ``` After: ```jsonc {8} // nx.json { "targetDefaults": { "@nx/angular:application": { "options": { "ssr": { "entry": "src/server.ts", "platform": "neutral", }, }, }, }, } ``` ### `update-23-1-0-add-istanbul-instrumenter` **Version**: 23.1.0-beta.0 Add `istanbul-lib-instrument` to `devDependencies` when a project runs unit tests with Karma (the `@angular-devkit/build-angular:karma` / `@angular/build:karma` builders, or `@angular/build:unit-test` / `@nx/angular:unit-test` with `runner: karma`), matching the Angular v22 `add-istanbul-instrumenter` migration. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=22.0.0` | #### Add `istanbul-lib-instrument` for Karma Coverage Angular v22 makes `istanbul-lib-instrument` an optional peer dependency of `@angular/build`, used to instrument code for Karma coverage. Since optional peers are not installed automatically, this migration adds `istanbul-lib-instrument` to `devDependencies` when the workspace has a project that runs unit tests with Karma, so Karma coverage keeps working. An existing version is preserved. Karma usage is detected on any target using the `@angular-devkit/build-angular:karma` or `@angular/build:karma` builders, or the `@angular/build:unit-test` / `@nx/angular:unit-test` executors with the `runner` option set to `karma`. Targets that inherit their executor or `runner` from an `nx.json` `targetDefaults` entry are detected too. #### Examples ##### `project.json` Before: ```jsonc {5} // project.json { "targets": { "test": { "executor": "@nx/angular:unit-test", "options": { "runner": "karma", }, }, }, } ``` After (`package.json`): ```jsonc {4} // package.json { "devDependencies": { "istanbul-lib-instrument": "^6.0.3", }, } ``` ##### `nx.json` (`targetDefaults`) A project whose `test` target inherits its executor and `runner` from an `nx.json` `targetDefaults` entry is also detected: ```jsonc {7} // nx.json { "targetDefaults": { "test": { "executor": "@nx/angular:unit-test", "options": { "runner": "karma", }, }, }, } ``` ```jsonc // apps/app1/project.json { "targets": { "test": {}, }, } ``` `istanbul-lib-instrument` is added to `package.json` as shown above. ### `update-23-1-0-add-trust-proxy-headers` **Version**: 23.1.0-beta.0 Add the `trustProxyHeaders` option to `AngularNodeAppEngine` and `AngularAppEngine` instantiations in SSR server files, matching the Angular v22 `trust-proxy-headers` migration. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=22.0.0` | #### Add `trustProxyHeaders` to SSR Engines Angular v22 adds a `trustProxyHeaders` option to `AngularNodeAppEngine` and `AngularAppEngine` that controls whether the `X-Forwarded-*` headers are trusted when resolving the incoming request URL. This migration adds the option to existing engine instantiations in SSR server files so the behavior is explicit, along with a TODO comment flagging it as security-sensitive. Instantiations that already set `trustProxyHeaders` are left unchanged. The migration scans the source files of projects that depend on `@angular/ssr` for `new AngularNodeAppEngine(...)` and `new AngularAppEngine(...)` expressions. #### Examples ##### `server.ts` Before: ```ts import { AngularNodeAppEngine } from '@angular/ssr/node'; const angularApp = new AngularNodeAppEngine(); ``` After: ```ts import { AngularNodeAppEngine } from '@angular/ssr/node'; const angularApp = new AngularNodeAppEngine({ // TODO: This is a security-sensitive option. Remove if not needed. For more information, see https://angular.dev/best-practices/security#configuring-trusted-proxy-headers trustProxyHeaders: ['x-forwarded-host', 'x-forwarded-proto'], }); ``` An existing options object is preserved, with `trustProxyHeaders` added alongside the current options: Before: ```ts const angularApp = new AngularAppEngine({ allowedHosts: ['example.com'], }); ``` After: ```ts const angularApp = new AngularAppEngine({ // TODO: This is a security-sensitive option. Remove if not needed. For more information, see https://angular.dev/best-practices/security#configuring-trusted-proxy-headers trustProxyHeaders: ['x-forwarded-host', 'x-forwarded-proto'], allowedHosts: ['example.com'], }); ``` ### `update-23-1-0-add-angular-build` **Version**: 23.1.0-beta.0 Add `@angular/build` to `devDependencies` when a project uses the `@nx/angular:application` or `@nx/angular:unit-test` executor, or an `@angular/build:*` executor, and it is not already installed. These executors import `@angular/build` directly at runtime, but it has only ever been available transitively via `@angular-devkit/build-angular`, which is not reliable across package managers. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=22.0.0` | #### Add `@angular/build` When Used by an Executor The `@nx/angular:application` and `@nx/angular:unit-test` executors load the Angular builders by importing `@angular/build` directly, and any `@angular/build:*` executor is provided by that package, so `@angular/build` is a required runtime dependency for workspaces using those targets. Nothing declares it as a direct dependency, though: it has only ever been available transitively as a dependency of `@angular-devkit/build-angular`. That is not reliable (for example, under Yarn Berry it can be missing from `node_modules` even when `@angular-devkit/build-angular` is installed), and when `@angular/build` cannot be resolved the build fails with an error like `The "@angular/build" package is required by "@nx/angular:application" but is not installed`. This migration adds `@angular/build` to `devDependencies` when a project uses one of those executors and it is not already installed, at the version the application generator installs. An existing version is preserved. Executor usage is detected on any target using `@nx/angular:application`, `@nx/angular:unit-test`, or an `@angular/build:*` executor. Targets that inherit their executor from an `nx.json` `targetDefaults` entry are detected too. #### Examples ##### `project.json` Before: ```jsonc {5} // project.json { "targets": { "build": { "executor": "@nx/angular:application", "options": {}, }, }, } ``` After (`package.json`): ```jsonc {4} // package.json { "devDependencies": { "@angular/build": "~22.0.4", }, } ``` ##### `nx.json` (`targetDefaults`) A project whose `build` target inherits its executor from an `nx.json` `targetDefaults` entry is also detected: ```jsonc {5} // nx.json { "targetDefaults": { "build": { "executor": "@nx/angular:application", "options": {}, }, }, } ``` ```jsonc // apps/app1/project.json { "targets": { "build": {}, }, } ``` `@angular/build` is added to `package.json` as shown above. ### `update-23-1-0-remove-conflicting-extended-diagnostics` **Version**: 23.1.0-beta.0 Remove the `extendedDiagnostics` Angular compiler option from tsconfig files where `strictTemplates` resolves to `false`. Angular v22's `strict-templates-default` and `strict-safe-navigation-narrow` migrations can together produce this combination, which the Angular compiler rejects (`extendedDiagnostics` requires `strictTemplates` to be enabled). #### Requires | Name | Version | |------|---------| `@angular/core` | `>=22.0.0` | #### Remove `extendedDiagnostics` When `strictTemplates` Is Disabled Angular v22 ships two `ng update` migrations that can leave a project's `tsconfig` in a state the Angular compiler rejects. `strict-templates-default` adds `"strictTemplates": false` to projects that had not enabled strict templates, and `strict-safe-navigation-narrow` adds an `extendedDiagnostics` block to every project `tsconfig`. The compiler requires `strictTemplates` to be enabled whenever `extendedDiagnostics` is configured, so the two together fail the build with "Using extendedDiagnostics requires that strictTemplates is also enabled". Because `@nx/angular` migrations run after Angular's, this migration reconciles the result: for any `tsconfig` where `strictTemplates` resolves to `false` (following the `extends` chain), it removes the `extendedDiagnostics` block. This keeps the intended `strictTemplates: false` behavior rather than enabling strict templates, which would change behavior and surface new template errors. Projects that keep `strictTemplates` enabled are left untouched. #### Examples ##### `tsconfig.app.json` Before: ```jsonc {3,10} // tsconfig.app.json { "angularCompilerOptions": { "extendedDiagnostics": { "checks": { "nullishCoalescingNotNullable": "suppress", "optionalChainNotNullable": "suppress", }, }, "strictTemplates": false, }, } ``` After: ```jsonc {3} // tsconfig.app.json { "angularCompilerOptions": { "strictTemplates": false, }, } ``` ### `update-23-1-0-add-optional-webpack-packages` **Version**: 23.1.0-rc.3 Add optional webpack, Module Federation, and Rspack packages when existing targets require them. #### Add Optional Webpack Packages Adds `@nx/webpack`, `@nx/module-federation`, `@nx/rspack`, and `webpack-merge` to the workspace when existing targets require them. These packages are no longer direct dependencies of `@nx/angular`; they are now optional peer dependencies, so installing `@nx/angular` no longer pulls webpack tooling into workspaces that only use the esbuild or Vite build stack. This migration backfills them for workspaces that already use webpack, Module Federation, or Rspack so those builds keep working after upgrading. Packages that are already present are left untouched. A package is added only when a matching target exists: - `@nx/webpack` and `webpack-merge`: an `@nx/angular:webpack-browser` or `@nx/angular:webpack-server` target, or an `@nx/angular:dev-server` whose build target uses `@nx/angular:webpack-browser` or `@angular-devkit/build-angular:browser`. - `@nx/module-federation`: an `@nx/angular:module-federation-dev-server` or `@nx/angular:module-federation-dev-ssr` target, or a project with a `module-federation.config.{js,ts}` file (covers remotes whose host lives in another workspace). - `@nx/rspack`: an `@nx/rspack:*` target or the `@nx/rspack/plugin` plugin in `nx.json`. Targets that inherit their executor from an `nx.json` `targetDefaults` entry are detected too. #### Examples For a workspace with an `@nx/angular:webpack-browser` build target, the migration adds the webpack packages to `devDependencies`. ##### Before ```jsonc title="package.json" { "devDependencies": { "@nx/angular": "23.1.0", }, } ``` ##### After ```jsonc title="package.json" { "devDependencies": { "@nx/angular": "23.1.0", "@nx/webpack": "23.1.0", "webpack-merge": "^5.8.0", }, } ``` ### 23.1.0-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular/cli` | `~22.0.0` | Updated only | `@angular-devkit/build-angular` | `~22.0.0` | Updated only | `@angular-devkit/core` | `~22.0.0` | Updated only | `@angular-devkit/schematics` | `~22.0.0` | Updated only | `@angular/build` | `~22.0.0` | Updated only | `@angular/pwa` | `~22.0.0` | Updated only | `@angular/ssr` | `~22.0.0` | Updated only | `@schematics/angular` | `~22.0.0` | Updated only | `@angular-devkit/architect` | `~0.2200.0` | Updated only | `@angular-devkit/build-webpack` | `~0.2200.0` | Updated only | `@angular/core` | `~22.0.0` | Added if not installed | `@angular/material` | `~22.0.0` | Updated only | `@angular/cdk` | `~22.0.0` | Updated only | `@angular/google-maps` | `~22.0.0` | Updated only | `ng-packagr` | `~22.0.0` | Updated only | `zone.js` | `~0.16.2` | Updated only ### 23.1.0-angular-eslint-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `angular-eslint` | `^22.0.0` | Updated only | `@angular-eslint/eslint-plugin` | `^22.0.0` | Updated only | `@angular-eslint/eslint-plugin-template` | `^22.0.0` | Updated only | `@angular-eslint/template-parser` | `^22.0.0` | Updated only | `@angular-eslint/utils` | `^22.0.0` | Updated only | `@angular-eslint/schematics` | `^22.0.0` | Updated only | `@angular-eslint/test-utils` | `^22.0.0` | Updated only | `@angular-eslint/builder` | `^22.0.0` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^22.0.0` | Updated only ### 23.1.0-@angular-eslint-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-eslint/eslint-plugin` | `^22.0.0` | Updated only | `@angular-eslint/eslint-plugin-template` | `^22.0.0` | Updated only | `@angular-eslint/template-parser` | `^22.0.0` | Updated only | `@angular-eslint/utils` | `^22.0.0` | Updated only | `@angular-eslint/schematics` | `^22.0.0` | Updated only | `@angular-eslint/test-utils` | `^22.0.0` | Updated only | `@angular-eslint/builder` | `^22.0.0` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^22.0.0` | Updated only ## 23.0.x ### `update-23-0-0-update-with-module-federation-import` **Version**: 23.0.0-beta.0 Update the @nx/angular/module-federation import to use @nx/module-federation/angular. #### Update withModuleFederation Import to New Package Updates the `withModuleFederation` and `withModuleFederationForSSR` imports to use `@nx/module-federation/angular`. The `@nx/angular/module-federation` re-export path was deprecated in Nx v20.2 and is now removed. #### Examples ##### Before ```ts title="apps/shell/webpack.config.ts" import { withModuleFederation, withModuleFederationForSSR, } from '@nx/angular/module-federation'; ``` ##### After ```ts title="apps/shell/webpack.config.ts" import { withModuleFederation, withModuleFederationForSSR, } from '@nx/module-federation/angular'; ``` #### Notes This migration runs for all projects in the workspace that depend on `@nx/angular`, ensuring imports are updated when migrating to Nx v23. ### `update-23-0-0-migrate-ngrx-generator-defaults` **Version**: 23.0.0-beta.7 Split @nx/angular:ngrx generator defaults in nx.json across the @nx/angular:ngrx-root-store and @nx/angular:ngrx-feature-store generators. #### Split `@nx/angular:ngrx` generator defaults across the replacement generators Splits any `@nx/angular:ngrx` generator defaults set in `nx.json` or in project-level `project.json` files across the two replacement generators: `@nx/angular:ngrx-root-store` (for root state) and `@nx/angular:ngrx-feature-store` (for feature state). The `@nx/angular:ngrx` generator was removed in Nx v23. Shared options apply to both keys; `barrels` and `parent` are written only to `@nx/angular:ngrx-feature-store` (the `@nx/angular:ngrx-root-store` schema doesn't accept them). The `minimal` option is written only to `@nx/angular:ngrx-root-store` because its semantics differ between the two new generators (see Notes). The deprecated `module` option is renamed to `parent`. The obsolete `root` toggle is dropped — intent is now expressed by which generator is invoked. Existing defaults already set under the new keys are preserved. #### Examples ##### Before ```json title="nx.json" { "generators": { "@nx/angular:ngrx": { "facade": true, "minimal": true, "barrels": true, "module": "libs/my-lib/src/lib/my-lib-module.ts" } } } ``` ##### After ```json title="nx.json" { "generators": { "@nx/angular:ngrx-root-store": { "facade": true, "minimal": true }, "@nx/angular:ngrx-feature-store": { "facade": true, "barrels": true, "parent": "libs/my-lib/src/lib/my-lib-module.ts" } } } ``` #### Notes The `minimal` option is intentionally not propagated to `@nx/angular:ngrx-feature-store`. In the deprecated `@nx/angular:ngrx` generator, `minimal` only gated root-state file generation and was a no-op for feature-state usage. In `@nx/angular:ngrx-feature-store`, `minimal: true` skips template generation while still wiring imports to the (now missing) files, which would produce broken modules. The split is otherwise intentionally inclusive: shared options are written to both new keys so users can trim the ones they don't want. CLI invocations of `@nx/angular:ngrx` (in shell scripts, CI, or `package.json`) are not migrated — update those manually to call `@nx/angular:ngrx-root-store` or `@nx/angular:ngrx-feature-store`. ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/angular/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/angular` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/angular/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/angular/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/angular/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/angular/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ### `rewrite-angular-internal-subpath-imports` **Version**: 23.0.0-beta.25 Rewrites the now-removed deep `@nx/angular/src/utils`, `@nx/angular/src/generators/utils` and `@nx/angular/src/generators/move/move-impl` subpath imports to the new `@nx/angular/internal` entry. The executor/builder/generator schema and impl subpaths remain available and are left untouched. ## 22.6.x ### 22.6.0-angular-eslint-package-updates **Version**: 22.6.0-beta.6 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `angular-eslint` | `^21.2.0` | Updated only | `@angular-eslint/eslint-plugin` | `^21.2.0` | Updated only | `@angular-eslint/eslint-plugin-template` | `^21.2.0` | Updated only | `@angular-eslint/template-parser` | `^21.2.0` | Updated only | `@angular-eslint/utils` | `^21.2.0` | Updated only | `@angular-eslint/schematics` | `^21.2.0` | Updated only | `@angular-eslint/test-utils` | `^21.2.0` | Updated only | `@angular-eslint/builder` | `^21.2.0` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^21.2.0` | Updated only ### 22.6.0-@angular-eslint-package-updates **Version**: 22.6.0-beta.6 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-eslint/eslint-plugin` | `^21.2.0` | Updated only | `@angular-eslint/eslint-plugin-template` | `^21.2.0` | Updated only | `@angular-eslint/template-parser` | `^21.2.0` | Updated only | `@angular-eslint/utils` | `^21.2.0` | Updated only | `@angular-eslint/schematics` | `^21.2.0` | Updated only | `@angular-eslint/test-utils` | `^21.2.0` | Updated only | `@angular-eslint/builder` | `^21.2.0` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^21.2.0` | Updated only ### 22.6.0-package-updates **Version**: 22.6.0-beta.6 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular/cli` | `~21.2.0` | Updated only | `@angular-devkit/build-angular` | `~21.2.0` | Updated only | `@angular-devkit/core` | `~21.2.0` | Updated only | `@angular-devkit/schematics` | `~21.2.0` | Updated only | `@angular/build` | `~21.2.0` | Updated only | `@angular/pwa` | `~21.2.0` | Updated only | `@angular/ssr` | `~21.2.0` | Updated only | `@schematics/angular` | `~21.2.0` | Updated only | `@angular-devkit/architect` | `~0.2102.0` | Updated only | `@angular-devkit/build-webpack` | `~0.2102.0` | Updated only | `@angular/core` | `~21.2.0` | Added if not installed | `@angular/material` | `~21.2.0` | Updated only | `@angular/cdk` | `~21.2.0` | Updated only | `@angular/google-maps` | `~21.2.0` | Updated only | `ng-packagr` | `~21.2.0` | Updated only ### 22.6.0-module-federation-package-updates **Version**: 22.6.0-beta.10 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/enhanced` | `^2.1.0` | Updated only | `@module-federation/node` | `^2.7.21` | Updated only ## 22.4.x ### 22.4.0-package-updates **Version**: 22.4.0-beta.4 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular/cli` | `~21.1.0` | Updated only | `@angular-devkit/build-angular` | `~21.1.0` | Updated only | `@angular-devkit/core` | `~21.1.0` | Updated only | `@angular-devkit/schematics` | `~21.1.0` | Updated only | `@angular/build` | `~21.1.0` | Updated only | `@angular/pwa` | `~21.1.0` | Updated only | `@angular/ssr` | `~21.1.0` | Updated only | `@schematics/angular` | `~21.1.0` | Updated only | `@angular-devkit/architect` | `~0.2101.0` | Updated only | `@angular-devkit/build-webpack` | `~0.2101.0` | Updated only | `@angular/core` | `~21.1.0` | Added if not installed | `@angular/material` | `~21.1.0` | Updated only | `@angular/cdk` | `~21.1.0` | Updated only | `@angular/google-maps` | `~21.1.0` | Updated only | `ng-packagr` | `~21.1.0` | Updated only ## 22.3.x ### `update-ssr-webpack-config-22-2-0` **Version**: 22.3.0-beta.0 Updates webpack-based SSR configuration to use preserve module format and bundler module resolution. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=21.0.0` | #### Updates Webpack-Based SSR Configuration Updates the TypeScript configuration and import syntax for webpack-based server-side rendering (SSR) projects. This migration sets `module: "preserve"` and `moduleResolution: "bundler"` in `tsconfig.server.json` to align with Angular's build requirements, and updates server file imports from namespace imports (`import * as express`) to default imports (`import express`) to work correctly with the new module format. #### Examples For webpack-based SSR projects (using `@nx/angular:webpack-server` or `@angular-devkit/build-angular:server`), the migration updates the `tsconfig.server.json` file: ##### Before ```jsonc {7-8} // apps/my-app/tsconfig.server.json { "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../dist/out-tsc", "target": "es2022", "module": "commonjs", "moduleResolution": "node", "types": ["node"], }, "files": ["src/main.server.ts", "src/server.ts"], } ``` ##### After ```jsonc {7-8} // apps/my-app/tsconfig.server.json { "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../dist/out-tsc", "target": "es2022", "module": "preserve", "moduleResolution": "bundler", "types": ["node"], }, "files": ["src/main.server.ts", "src/server.ts"], } ``` The migration also updates import statements in the `server.ts` file to use default imports instead of namespace imports: ##### Before ```ts {2-4} // apps/my-app/src/server.ts import * as express from 'express'; import * as compression from 'compression'; import * as cors from 'cors'; const app = express(); app.use(compression()); app.use(cors()); ``` ##### After ```ts {2-4} // apps/my-app/src/server.ts import express from 'express'; import compression from 'compression'; import cors from 'cors'; const app = express(); app.use(compression()); app.use(cors()); ``` Projects that already have the correct TypeScript configuration or projects without a `tsconfig.server.json` file are not modified by this migration. ### `update-module-resolution-22-2-0` **Version**: 22.3.0-beta.0 Update 'module' to 'preserve' and 'moduleResolution' to 'bundler' in TypeScript configurations for Angular projects. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=21.0.0-rc.3` | #### Update `module` to `preserve` and `moduleResolution` to `bundler` in TypeScript configurations Updates the TypeScript `module` and `moduleResolution` compiler options to `'preserve'` and `'bundler'` respectively for Angular projects. These settings are required for Angular's build system to work correctly with modern module resolution algorithms used by bundlers like Webpack, Vite, and esbuild. #### Examples The migration updates TypeScript configuration files in Angular projects to set both compiler options: ##### Before ```jsonc {4-5} // apps/my-app/tsconfig.app.json { "compilerOptions": { "module": "es2020", "moduleResolution": "node", }, } ``` ##### After ```jsonc {4-5} // apps/my-app/tsconfig.app.json { "compilerOptions": { "module": "preserve", "moduleResolution": "bundler", }, } ``` If both values are already set correctly and inherited from an extended tsconfig file, the migration will not modify the configuration: ##### Before ```jsonc {5-6} // apps/my-app/tsconfig.json { "extends": "../../tsconfig.base.json", "compilerOptions": { "module": "preserve", "moduleResolution": "bundler", }, } ``` ```jsonc {4-6} // apps/my-app/tsconfig.app.json { "extends": "./tsconfig.json", "compilerOptions": { "types": [], }, } ``` ##### After ```jsonc {5-6} // apps/my-app/tsconfig.json { "extends": "../../tsconfig.base.json", "compilerOptions": { "module": "preserve", "moduleResolution": "bundler", }, } ``` ```jsonc {4-6} // apps/my-app/tsconfig.app.json { "extends": "./tsconfig.json", "compilerOptions": { "types": [], }, } ``` The migration only processes TypeScript configuration files that are referenced by Angular project build targets, ensuring that only Angular-specific configurations are updated. ### `update-typescript-lib-22-2-0` **Version**: 22.3.0-beta.0 Updates the 'lib' property in tsconfig files to use 'es2022' or a more modern version. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=21.0.0` | #### Update TypeScript `lib` compiler option to ES2022 Updates the TypeScript `lib` compiler option in Angular projects to ensure compatibility with Angular v21+, which requires ES2022 as the minimum ECMAScript version. The migration upgrades any ES versions older than ES2022 (such as ES2015, ES2020, etc.) to ES2022 while preserving other library entries like `'dom'`, `'webworker'`, etc. #### Examples The migration processes TypeScript configuration files referenced by Angular project build targets and updates the `lib` compiler option when outdated ES versions are detected: ##### Before ```jsonc {4} // apps/my-app/tsconfig.app.json { "compilerOptions": { "lib": ["es2020", "dom"], }, } ``` ##### After ```jsonc {4} // apps/my-app/tsconfig.app.json { "compilerOptions": { "lib": ["dom", "es2022"], }, } ``` When the `lib` array contains only an ES version older than ES2022 without additional entries, it is upgraded: ##### Before ```jsonc {4} // apps/my-app/tsconfig.app.json { "compilerOptions": { "lib": ["es2020"], }, } ``` ##### After ```jsonc {4} // apps/my-app/tsconfig.app.json { "compilerOptions": { "lib": ["es2022"], }, } ``` If the configuration already uses ES2022 or higher (e.g., `'es2023'`, `'esnext'`), no changes are made: ##### Before ```jsonc {4} // apps/my-app/tsconfig.app.json { "compilerOptions": { "lib": ["es2022", "dom"], }, } ``` ##### After ```jsonc {4} // apps/my-app/tsconfig.app.json { "compilerOptions": { "lib": ["es2022", "dom"], }, } ``` When the `lib` array contains multiple library entries, only the ES version is upgraded while all other entries are preserved: ##### Before ```jsonc {4} // apps/my-app/tsconfig.app.json { "compilerOptions": { "lib": ["es2020", "dom", "webworker"], }, } ``` ##### After ```jsonc {4} // apps/my-app/tsconfig.app.json { "compilerOptions": { "lib": ["dom", "webworker", "es2022"], }, } ``` The migration only processes TypeScript configuration files that are referenced by Angular project build targets, ensuring that only Angular-specific configurations are updated. ### `update-unit-test-runner-option` **Version**: 22.3.0-beta.0 Update 'vitest' unit test runner option to 'vitest-analog' in generator defaults. #### Update `vitest` unit test runner option to `vitest-analog` in generator defaults Updates the `unitTestRunner` generator default from `vitest` to `vitest-analog` in `nx.json`. The `vitest` option has been split into two explicit options: `vitest-angular` (uses `@angular/build:unit-test`) and `vitest-analog` (uses AnalogJS-based setup). #### Examples The migration updates generator defaults in `nx.json`: ##### Before ```jsonc {5} // nx.json { "generators": { "@nx/angular:application": { "unitTestRunner": "vitest", }, }, } ``` ##### After ```jsonc {5} // nx.json { "generators": { "@nx/angular:application": { "unitTestRunner": "vitest-analog", }, }, } ``` ### `set-isolated-modules-22-3-0` **Version**: 22.3.0-beta.3 Set 'isolatedModules' to 'true' in TypeScript test configurations for Angular projects. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=21.0.0` | #### Set `isolatedModules` to `true` in TypeScript test configurations Sets the TypeScript `isolatedModules` compiler option to `true` in `tsconfig.spec.json` files for Angular projects using the Jest test runner. #### Examples The migration updates TypeScript test configuration files in Angular projects using the Jest test runner to set the `isolatedModules` compiler option: ##### Before ```jsonc {3-5} // apps/my-app/tsconfig.spec.json { "compilerOptions": { "outDir": "./out-tsc/spec", }, } ``` ##### After ```jsonc {4-5} // apps/my-app/tsconfig.spec.json { "compilerOptions": { "outDir": "./out-tsc/spec", "isolatedModules": true, }, } ``` If the value is already set to `true` or inherited from an extended tsconfig file, the migration will not modify the configuration: ##### Before ```jsonc {4} // tsconfig.json { "compilerOptions": { "isolatedModules": true, }, } ``` ```jsonc {4-6} // apps/my-app/tsconfig.spec.json { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/spec", }, } ``` ##### After ```jsonc {4} // tsconfig.json { "compilerOptions": { "isolatedModules": true, }, } ``` ```jsonc {4-6} // apps/my-app/tsconfig.spec.json { "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/spec", }, } ``` The migration only processes TypeScript test configuration files (`tsconfig.spec.json` or custom test tsconfig files referenced by `@nx/jest:jest` tasks) in Angular projects. ### `update-jest-preset-angular-setup` **Version**: 22.3.0-beta.3 Replace 'jest-preset-angular/setup-jest' imports with the new 'setupZoneTestEnv' function. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=21.0.0` | #### Update Jest Preset Angular Setup Replaces the removed `jest-preset-angular/setup-jest` import with the new `setupZoneTestEnv` function from `jest-preset-angular/setup-env/zone`. Starting with `jest-preset-angular` v15, the `setup-jest` files have been removed and replaced with explicit setup functions. The old `setup-jest` import only supported zone-based testing (zoneless support was added in v14.3.0 with the new `setupZonelessTestEnv` function), so all projects using the removed import are migrated to use `setupZoneTestEnv`. #### Examples ##### Before ```ts title="apps/my-app/src/test-setup.ts" import 'jest-preset-angular/setup-jest'; ``` ##### After ```ts title="apps/my-app/src/test-setup.ts" import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; setupZoneTestEnv(); ``` ### 22.3.0-package-updates **Version**: 22.3.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular/cli` | `~21.0.0` | Updated only | `@angular-devkit/build-angular` | `~21.0.0` | Updated only | `@angular-devkit/core` | `~21.0.0` | Updated only | `@angular-devkit/schematics` | `~21.0.0` | Updated only | `@angular/build` | `~21.0.0` | Updated only | `@angular/pwa` | `~21.0.0` | Updated only | `@angular/ssr` | `~21.0.0` | Updated only | `@schematics/angular` | `~21.0.0` | Updated only | `@angular-devkit/architect` | `~0.2100.0` | Updated only | `@angular-devkit/build-webpack` | `~0.2100.0` | Updated only | `@angular/core` | `~21.0.0` | Added if not installed | `@angular/material` | `~21.0.0` | Updated only | `@angular/cdk` | `~21.0.0` | Updated only | `@angular/google-maps` | `~21.0.0` | Updated only | `ng-packagr` | `~21.0.0` | Updated only | `zone.js` | `~0.16.0` | Updated only ### 22.3.0-angular-eslint-package-updates **Version**: 22.3.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `angular-eslint` | `^21.0.1` | Updated only | `@angular-eslint/eslint-plugin` | `^21.0.1` | Updated only | `@angular-eslint/eslint-plugin-template` | `^21.0.1` | Updated only | `@angular-eslint/template-parser` | `^21.0.1` | Updated only | `@angular-eslint/utils` | `^21.0.1` | Updated only | `@angular-eslint/schematics` | `^21.0.1` | Updated only | `@angular-eslint/test-utils` | `^21.0.1` | Updated only | `@angular-eslint/builder` | `^21.0.1` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^21.0.1` | Updated only ### 22.3.0-@angular-eslint-package-updates **Version**: 22.3.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-eslint/eslint-plugin` | `^21.0.1` | Updated only | `@angular-eslint/eslint-plugin-template` | `^21.0.1` | Updated only | `@angular-eslint/template-parser` | `^21.0.1` | Updated only | `@angular-eslint/utils` | `^21.0.1` | Updated only | `@angular-eslint/schematics` | `^21.0.1` | Updated only | `@angular-eslint/test-utils` | `^21.0.1` | Updated only | `@angular-eslint/builder` | `^21.0.1` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^21.0.1` | Updated only ### 22.3.2-ngrx-package-updates **Version**: 22.3.2-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@ngrx/store` | `^21.0.0` | Updated only ## 22.2.x ### 22.2.0-package-updates **Version**: 22.2.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/enhanced` | `^0.21.2` | Updated only | `@module-federation/runtime` | `^0.21.2` | Updated only | `@module-federation/sdk` | `^0.21.2` | Updated only | `@module-federation/node` | `^2.7.21` | Updated only ## 21.6.x ### `update-angular-cli-version-20-3-0` **Version**: 21.6.1-beta.2 Update the @angular/cli package version to ~20.3.0. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=20.3.0` | #### Sample Code Changes Update the `@angular/cli` package version in the `package.json` file at the workspace root to **~20.3.0**. ##### Before ```json title="package.json" { "devDependencies": { "@angular/cli": "~20.2.0" } } ``` ##### After ```json title="package.json" {3} { "devDependencies": { "@angular/cli": "~20.3.0" } } ``` ### 21.6.1-package-updates **Version**: 21.6.1-beta.2 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-devkit/build-angular` | `~20.3.0` | Updated only | `@angular-devkit/core` | `~20.3.0` | Updated only | `@angular-devkit/schematics` | `~20.3.0` | Updated only | `@angular/build` | `~20.3.0` | Updated only | `@angular/pwa` | `~20.3.0` | Updated only | `@angular/ssr` | `~20.3.0` | Updated only | `@schematics/angular` | `~20.3.0` | Updated only | `@angular-devkit/architect` | `~0.2003.0` | Updated only | `@angular-devkit/build-webpack` | `~0.2003.0` | Updated only | `@angular/core` | `~20.3.0` | Added if not installed | `@angular/material` | `~20.2.3` | Updated only | `@angular/cdk` | `~20.2.3` | Updated only | `@angular/google-maps` | `~20.2.3` | Updated only | `ng-packagr` | `~20.3.0` | Updated only ### 21.6.1-angular-eslint-package-updates **Version**: 21.6.1-beta.2 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `angular-eslint` | `^20.3.0` | Updated only | `@angular-eslint/eslint-plugin` | `^20.3.0` | Updated only | `@angular-eslint/eslint-plugin-template` | `^20.3.0` | Updated only | `@angular-eslint/template-parser` | `^20.3.0` | Updated only | `@angular-eslint/utils` | `^20.3.0` | Updated only | `@angular-eslint/schematics` | `^20.3.0` | Updated only | `@angular-eslint/test-utils` | `^20.3.0` | Updated only | `@angular-eslint/builder` | `^20.3.0` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^20.3.0` | Updated only ### 21.6.1-@angular-eslint-package-updates **Version**: 21.6.1-beta.2 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-eslint/eslint-plugin` | `^20.3.0` | Updated only | `@angular-eslint/eslint-plugin-template` | `^20.3.0` | Updated only | `@angular-eslint/template-parser` | `^20.3.0` | Updated only | `@angular-eslint/utils` | `^20.3.0` | Updated only | `@angular-eslint/schematics` | `^20.3.0` | Updated only | `@angular-eslint/test-utils` | `^20.3.0` | Updated only | `@angular-eslint/builder` | `^20.3.0` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^20.3.0` | Updated only ## 21.5.x ### `set-tsconfig-option` **Version**: 21.5.0-beta.0 Set the 'tsConfig' option to build and test targets to help with Angular migration issues. #### Set `tsConfig` option for build and test targets - Set the `tsConfig` option in the target options for library build executors. It moves the value from the target `development` configuration and it doesn't set it if the option is already set. - Set `tsconfig.spec.json` as the `tsConfig` option in the target options for the `@nx/jest:jest` executor. It only does it if the file exists and the options is not already set. #### Examples The migration will move the `tsConfig` option for library build executors (`@nx/angular:ng-packagr-lite` and `@nx/angular:package`) from the `development` configuration to the target options if it's not already set: ##### Before ```json title="libs/lib1/project.json" {7} { "targets": { "build": { "executor": "@nx/angular:ng-packagr-lite", "configurations": { "development": { "tsConfig": "libs/lib1/tsconfig.lib.dev.json" } } } } } ``` ##### After ```json title="libs/lib1/project.json" {6,9} { "targets": { "build": { "executor": "@nx/angular:ng-packagr-lite", "options": { "tsConfig": "libs/lib1/tsconfig.lib.dev.json" }, "configurations": { "development": {} } } } } ``` The migration will set the `tsConfig` option for the `@nx/jest:jest` executor when the `tsconfig.spec.json` file exists and the option is not already set: ##### Before ```json title="apps/app1/project.json" { "targets": { "test": { "executor": "@nx/jest:jest" } } } ``` ##### After ```json title="apps/app1/project.json" {6} { "targets": { "test": { "executor": "@nx/jest:jest", "options": { "tsConfig": "apps/app1/tsconfig.spec.json" } } } } ``` If the `tsConfig` option is already set in the target options, the migration will not modify the configuration: ##### Before ```json title="libs/lib1/project.json" {6,10} { "targets": { "build": { "executor": "@nx/angular:ng-packagr-lite", "options": { "tsConfig": "libs/lib1/tsconfig.lib.json" }, "configurations": { "development": { "tsConfig": "libs/lib1/tsconfig.lib.dev.json" } } } } } ``` ##### After ```json title="libs/lib1/project.json" {6,10} { "targets": { "build": { "executor": "@nx/angular:ng-packagr-lite", "options": { "tsConfig": "libs/lib1/tsconfig.lib.json" }, "configurations": { "development": { "tsConfig": "libs/lib1/tsconfig.lib.dev.json" } } } } } ``` ### `update-angular-cli-version-20-2-0` **Version**: 21.5.0-beta.2 Update the @angular/cli package version to ~20.2.0. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=20.2.0` | #### Sample Code Changes Update the `@angular/cli` package version in the `package.json` file at the workspace root to **~20.2.0**. ##### Before ```json title="package.json" { "devDependencies": { "@angular/cli": "~20.1.0" } } ``` ##### After ```json title="package.json" {3} { "devDependencies": { "@angular/cli": "~20.2.0" } } ``` ### `remove-default-karma-configuration-files` **Version**: 21.5.0-beta.2 Remove any Karma configuration files that only contain the default content. The default configuration is automatically available without a specific project configurationfile. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=20.2.0` | #### Remove the Default Karma Configuration Files Removes Karma configuration files that match the default configuration generated by Angular CLI to reduce boilerplate code in the workspace. The migration also removes the `karmaConfig` option from project targets when the configuration file is removed. #### Examples The migration will remove `karma.conf.js` files that contain only default settings and update the project configuration: ##### Before ```javascript title="apps/my-app/karma.conf.js" // Karma configuration file, see link for more information // https://karma-runner.github.io/1.0/config/configuration-file.html module.exports = function (config) { config.set({ basePath: '', frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), require('karma-coverage'), require('@angular-devkit/build-angular/plugins/karma'), ], client: { jasmine: { // you can add configuration options for Jasmine here // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html // for example, you can disable the random execution with `random: false` // or set a specific seed with `seed: 4321` }, }, jasmineHtmlReporter: { suppressAll: true, // removes the duplicated traces }, coverageReporter: { dir: require('path').join(__dirname, '../../coverage/my-app'), subdir: '.', reporters: [{ type: 'html' }, { type: 'text-summary' }], }, reporters: ['progress', 'kjhtml'], browsers: ['Chrome'], restartOnFileChange: true, }); }; ``` ```json title="apps/my-app/project.json" {7} { "name": "my-app", "targets": { "test": { "executor": "@angular-devkit/build-angular:karma", "options": { "karmaConfig": "apps/my-app/karma.conf.js", "polyfills": ["zone.js", "zone.js/testing"] } } } } ``` ##### After File `apps/my-app/karma.conf.js` is removed. ```json title="apps/my-app/project.json" { "name": "my-app", "targets": { "test": { "executor": "@angular-devkit/build-angular:karma", "options": { "polyfills": ["zone.js", "zone.js/testing"] } } } } ``` If the Karma configuration contains customizations, the migration will preserve the file and configuration: ##### Before ```javascript title="apps/custom-app/karma.conf.js" module.exports = function (config) { config.set({ basePath: '', frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), require('karma-coverage'), require('@angular-devkit/build-angular/plugins/karma'), ], browsers: ['ChromeHeadless'], // Custom browser configuration restartOnFileChange: true, }); }; ``` ##### After ```javascript title="apps/custom-app/karma.conf.js" module.exports = function (config) { config.set({ basePath: '', frameworks: ['jasmine', '@angular-devkit/build-angular'], plugins: [ require('karma-jasmine'), require('karma-chrome-launcher'), require('karma-jasmine-html-reporter'), require('karma-coverage'), require('@angular-devkit/build-angular/plugins/karma'), ], browsers: ['ChromeHeadless'], // Custom browser configuration restartOnFileChange: true, }); }; ``` ### 21.5.0-package-updates **Version**: 21.5.0-beta.2 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-devkit/build-angular` | `~20.2.0` | Updated only | `@angular-devkit/core` | `~20.2.0` | Updated only | `@angular-devkit/schematics` | `~20.2.0` | Updated only | `@angular/build` | `~20.2.0` | Updated only | `@angular/pwa` | `~20.2.0` | Updated only | `@angular/ssr` | `~20.2.0` | Updated only | `@schematics/angular` | `~20.2.0` | Updated only | `@angular-devkit/architect` | `~0.2002.0` | Updated only | `@angular-devkit/build-webpack` | `~0.2002.0` | Updated only | `@angular/core` | `~20.2.0` | Added if not installed | `@angular/material` | `~20.2.0` | Updated only | `@angular/cdk` | `~20.2.0` | Updated only | `@angular/google-maps` | `~20.2.0` | Updated only | `ng-packagr` | `~20.2.0` | Updated only ### 21.5.0-angular-eslint-package-updates **Version**: 21.5.0-beta.2 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `angular-eslint` | `^20.2.0` | Updated only | `@angular-eslint/eslint-plugin` | `^20.2.0` | Updated only | `@angular-eslint/eslint-plugin-template` | `^20.2.0` | Updated only | `@angular-eslint/template-parser` | `^20.2.0` | Updated only | `@angular-eslint/utils` | `^20.2.0` | Updated only | `@angular-eslint/schematics` | `^20.2.0` | Updated only | `@angular-eslint/test-utils` | `^20.2.0` | Updated only | `@angular-eslint/builder` | `^20.2.0` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^20.2.0` | Updated only ### 21.5.0-@angular-eslint-package-updates **Version**: 21.5.0-beta.2 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-eslint/eslint-plugin` | `^20.2.0` | Updated only | `@angular-eslint/eslint-plugin-template` | `^20.2.0` | Updated only | `@angular-eslint/template-parser` | `^20.2.0` | Updated only | `@angular-eslint/utils` | `^20.2.0` | Updated only | `@angular-eslint/schematics` | `^20.2.0` | Updated only | `@angular-eslint/test-utils` | `^20.2.0` | Updated only | `@angular-eslint/builder` | `^20.2.0` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^20.2.0` | Updated only ## 21.4.x ### 21.4.0-ngrx-package-updates **Version**: 21.4.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@ngrx/store` | `^20.0.0` | Updated only ## 21.3.x ### `update-angular-cli-version-20-1-0` **Version**: 21.3.0-beta.4 Update the @angular/cli package version to ~20.1.0. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=20.1.0` | #### Sample Code Changes Update the `@angular/cli` package version in the `package.json` file at the workspace root to **~20.1.0**. ##### Before ```json title="package.json" { "devDependencies": { "@angular/cli": "~20.0.0" } } ``` ##### After ```json title="package.json" {3} { "devDependencies": { "@angular/cli": "~20.1.0" } } ``` ### 21.3.0-package-updates **Version**: 21.3.0-beta.4 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-devkit/build-angular` | `~20.1.0` | Updated only | `@angular-devkit/core` | `~20.1.0` | Updated only | `@angular-devkit/schematics` | `~20.1.0` | Updated only | `@angular/build` | `~20.1.0` | Updated only | `@angular/pwa` | `~20.1.0` | Updated only | `@angular/ssr` | `~20.1.0` | Updated only | `@schematics/angular` | `~20.1.0` | Updated only | `@angular-devkit/architect` | `~0.2001.0` | Updated only | `@angular-devkit/build-webpack` | `~0.2001.0` | Updated only | `@angular/core` | `~20.1.0` | Added if not installed | `@angular/material` | `~20.1.0` | Updated only | `@angular/cdk` | `~20.1.0` | Updated only | `@angular/google-maps` | `~20.1.0` | Updated only | `ng-packagr` | `~20.1.0` | Updated only ## 21.2.x ### `update-angular-cli-version-20-0-0` **Version**: 21.2.0-beta.3 Update the @angular/cli package version to ~20.0.0. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=20.0.0` | #### Sample Code Changes Update the `@angular/cli` package version in the `package.json` file at the workspace root to **~20.0.0**. ##### Before ```json title="package.json" { "devDependencies": { "@angular/cli": "~19.2.0" } } ``` ##### After ```json title="package.json" {3} { "devDependencies": { "@angular/cli": "~20.0.0" } } ``` ### `migrate-provide-server-rendering-import` **Version**: 21.2.0-beta.3 Migrate imports of `provideServerRendering` from `@angular/platform-server` to `@angular/ssr`. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=20.0.0` | #### Migrate Imports of `provideServerRendering` from `@angular/platform-server` to `@angular/ssr` Migrate the imports of `provideServerRendering` from `@angular/platform-server` to `@angular/ssr`. This migration will also add the `@angular/ssr` package to your dependencies if needed. #### Examples Change the import of `provideServerRendering` from `@angular/platform-server` to `@angular/ssr`: ##### Before ```ts title="app/app.config.server.ts" {2} import { ApplicationConfig } from '@angular/core'; import { provideServerRendering } from '@angular/platform-server'; const serverConfig: ApplicationConfig = { providers: [provideServerRendering()], }; ``` ##### After ```ts title="app/app.config.server.ts" {2} import { ApplicationConfig } from '@angular/core'; import { provideServerRendering } from '@angular/ssr'; const serverConfig: ApplicationConfig = { providers: [provideServerRendering()], }; ``` If you already have imports from `@angular/ssr`, the migration will add `provideServerRendering` to the existing import: ##### Before ```ts title="app/app.config.server.ts" {2-3} import { ApplicationConfig } from '@angular/core'; import { provideServerRendering } from '@angular/platform-server'; import { provideServerRouting } from '@angular/ssr'; import { serverRoutes } from './app.routes.server'; const serverConfig: ApplicationConfig = { providers: [provideServerRendering(), provideServerRouting(serverRoutes)], }; ``` ##### After ```ts title="app/app.config.server.ts" {2} import { ApplicationConfig } from '@angular/core'; import { provideServerRouting, provideServerRendering } from '@angular/ssr'; import { serverRoutes } from './app.routes.server'; const serverConfig: ApplicationConfig = { providers: [provideServerRendering(), provideServerRouting(serverRoutes)], }; ``` ### `replace-provide-server-routing` **Version**: 21.2.0-beta.3 Replace `provideServerRouting` and `provideServerRoutesConfig` with `provideServerRendering` using `withRoutes`. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=20.0.0` | #### Replace `provideServerRouting` and `provideServerRoutesConfig` with `provideServerRendering` Replace `provideServerRouting` and `provideServerRoutesConfig` calls with `provideServerRendering` using `withRoutes`. #### Examples Remove `provideServerRouting` from your providers array and update the `provideServerRendering` call to use `withRoutes`: ##### Before ```ts title="app/app.config.server.ts" {2,6} import { ApplicationConfig } from '@angular/core'; import { provideServerRendering, provideServerRouting } from '@angular/ssr'; import { serverRoutes } from './app.routes.server'; const serverConfig: ApplicationConfig = { providers: [provideServerRendering(), provideServerRouting(serverRoutes)], }; ``` ##### After ```ts title="app/app.config.server.ts" {2,6} import { ApplicationConfig } from '@angular/core'; import { provideServerRendering, withRoutes } from '@angular/ssr'; import { serverRoutes } from './app.routes.server'; const serverConfig: ApplicationConfig = { providers: [provideServerRendering(withRoutes(serverRoutes))], }; ``` If you have `provideServerRouting` with additional arguments, the migration will preserve them: ##### Before ```ts title="app/app.config.server.ts" {4,11,12} import { ApplicationConfig } from '@angular/core'; import { provideServerRendering, provideServerRouting, withAppShell, } from '@angular/ssr'; import { serverRoutes } from './app.routes.server'; const serverConfig: ApplicationConfig = { providers: [ provideServerRendering(), provideServerRouting(serverRoutes, withAppShell(AppShellComponent)), ], }; ``` ##### After ```ts title="app/app.config.server.ts" {2,7-10} import { ApplicationConfig } from '@angular/core'; import { provideServerRendering, withAppShell, withRoutes } from '@angular/ssr'; import { serverRoutes } from './app.routes.server'; const serverConfig: ApplicationConfig = { providers: [ provideServerRendering( withRoutes(serverRoutes), withAppShell(AppShellComponent) ), ], }; ``` Remove `provideServerRoutesConfig` from your providers array and update the `provideServerRendering` call to use `withRoutes`: ##### Before ```ts title="app/app.config.server.ts" {4,11,12} import { ApplicationConfig } from '@angular/core'; import { provideServerRendering, provideServerRoutesConfig, withAppShell, } from '@angular/ssr'; import { serverRoutes } from './app.routes.server'; const serverConfig: ApplicationConfig = { providers: [ provideServerRendering(), provideServerRoutesConfig(serverRoutes, withAppShell(AppShellComponent)), ], }; ``` ##### After ```ts title="app/app.config.server.ts" {2,7-10} import { ApplicationConfig } from '@angular/core'; import { provideServerRendering, withAppShell, withRoutes } from '@angular/ssr'; import { serverRoutes } from './app.routes.server'; const serverConfig: ApplicationConfig = { providers: [ provideServerRendering( withRoutes(serverRoutes), withAppShell(AppShellComponent) ), ], }; ``` ### `set-generator-defaults-for-previous-style-guide` **Version**: 21.2.0-beta.3 Update the generator defaults to maintain the previous style guide behavior. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=20.0.0` | #### Set Generator Defaults for Previous Style Guide Updates the generator defaults in the `nx.json` file to maintain the previous Angular Style Guide behavior. This ensures that newly generated code in existing workspaces follows the same conventions as the existing codebase. #### Examples The migration will add default configurations for the relevant Angular generators in the workspace's `nx.json` file: ##### Before ```json title="nx.json" { "generators": {} } ``` ##### After ```json title="nx.json" { "generators": { "@nx/angular:component": { "type": "component" }, "@nx/angular:directive": { "type": "directive" }, "@nx/angular:service": { "type": "service" }, "@nx/angular:scam": { "type": "component" }, "@nx/angular:scam-directive": { "type": "directive" }, "@nx/angular:guard": { "typeSeparator": "." }, "@nx/angular:interceptor": { "typeSeparator": "." }, "@nx/angular:module": { "typeSeparator": "." }, "@nx/angular:pipe": { "typeSeparator": "." }, "@nx/angular:resolver": { "typeSeparator": "." }, "@schematics/angular:component": { "type": "component" }, "@schematics/angular:directive": { "type": "directive" }, "@schematics/angular:service": { "type": "service" }, "@schematics/angular:guard": { "typeSeparator": "." }, "@schematics/angular:interceptor": { "typeSeparator": "." }, "@schematics/angular:module": { "typeSeparator": "." }, "@schematics/angular:pipe": { "typeSeparator": "." }, "@schematics/angular:resolver": { "typeSeparator": "." } } } ``` If some of the generator defaults are already set, the migration will not override them: ##### Before ```json title="nx.json" {3-14} { "generators": { "@nx/angular:component": { "type": "cmp" }, "@schematics/angular:component": { "type": "cmp" }, "@nx/angular:interceptor": { "typeSeparator": "-" }, "@schematics/angular:interceptor": { "typeSeparator": "-" } } } ``` ##### After ```json title="nx.json" {3-14} { "generators": { "@nx/angular:component": { "type": "cmp" }, "@schematics/angular:component": { "type": "cmp" }, "@nx/angular:interceptor": { "typeSeparator": "-" }, "@schematics/angular:interceptor": { "typeSeparator": "-" }, "@nx/angular:directive": { "type": "directive" }, "@nx/angular:service": { "type": "service" }, "@nx/angular:scam": { "type": "component" }, "@nx/angular:scam-directive": { "type": "directive" }, "@nx/angular:guard": { "typeSeparator": "." }, "@nx/angular:module": { "typeSeparator": "." }, "@nx/angular:pipe": { "typeSeparator": "." }, "@nx/angular:resolver": { "typeSeparator": "." }, "@schematics/angular:directive": { "type": "directive" }, "@schematics/angular:service": { "type": "service" }, "@schematics/angular:guard": { "typeSeparator": "." }, "@schematics/angular:module": { "typeSeparator": "." }, "@schematics/angular:pipe": { "typeSeparator": "." }, "@schematics/angular:resolver": { "typeSeparator": "." } } } ``` ### `update-module-resolution` **Version**: 21.2.0-beta.3 Update 'moduleResolution' to 'bundler' in TypeScript configurations. You can read more about this here: https://www.typescriptlang.org/tsconfig/#moduleResolution. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=20.0.0` | #### Update `moduleResolution` to `bundler` in TypeScript configurations Updates the TypeScript `moduleResolution` option to `'bundler'` for improved compatibility with modern package resolution algorithms used by bundlers like Webpack, Vite, and esbuild. #### Examples The migration will update TypeScript configuration files in your workspace to use the `'bundler'` module resolution strategy: ##### Before ```json title="apps/app1/tsconfig.app.json" {4} { "compilerOptions": { "module": "es2020", "moduleResolution": "node" } } ``` ##### After ```json title="apps/app1/tsconfig.app.json" {4} { "compilerOptions": { "module": "es2020", "moduleResolution": "bundler" } } ``` If the `moduleResolution` is already set to `'bundler'` or the `module` is set to `'preserve'`, the migration will not modify the configuration: ##### Before ```json title="apps/app1/tsconfig.app.json" {3-4} { "compilerOptions": { "module": "preserve", "moduleResolution": "node" } } ``` ##### After ```json title="apps/app1/tsconfig.app.json" {3-4} { "compilerOptions": { "module": "preserve", "moduleResolution": "node" } } ``` ### 21.2.0-package-updates **Version**: 21.2.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-devkit/build-angular` | `~20.0.0` | Updated only | `@angular-devkit/core` | `~20.0.0` | Updated only | `@angular-devkit/schematics` | `~20.0.0` | Updated only | `@angular/build` | `~20.0.0` | Updated only | `@angular/pwa` | `~20.0.0` | Updated only | `@angular/ssr` | `~20.0.0` | Updated only | `@schematics/angular` | `~20.0.0` | Updated only | `@angular-devkit/architect` | `~0.2000.0` | Updated only | `@angular-devkit/build-webpack` | `~0.2000.0` | Updated only | `@angular/core` | `~20.0.0` | Added if not installed | `@angular/material` | `~20.0.0` | Updated only | `@angular/cdk` | `~20.0.0` | Updated only | `@angular/google-maps` | `~20.0.0` | Updated only | `ng-packagr` | `~20.0.0` | Updated only ### 21.2.0-angular-eslint-package-updates **Version**: 21.2.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `angular-eslint` | `^20.0.0` | Updated only ### 21.2.0-@angular-eslint-package-updates **Version**: 21.2.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-eslint/eslint-plugin` | `^20.0.0` | Updated only | `@angular-eslint/eslint-plugin-template` | `^20.0.0` | Updated only | `@angular-eslint/template-parser` | `^20.0.0` | Updated only | `@angular-eslint/utils` | `^20.0.0` | Updated only | `@angular-eslint/schematics` | `^20.0.0` | Updated only | `@angular-eslint/test-utils` | `^20.0.0` | Updated only | `@angular-eslint/builder` | `^20.0.0` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^20.0.0` | Updated only ### 21.2.0-angular-rspack-package-updates **Version**: 21.2.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@nx/angular-rspack` | `^21.1.0` | Updated only ### 21.2.0-jest-package-updates **Version**: 21.2.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `jest-preset-angular` | `~14.6.0` | Updated only ## 21.1.x ### 21.1.0-package-updates **Version**: 21.1.0-beta.1 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@nx/angular-rspack` | `^21.0.1` | Updated only ## 21.0.x ### `set-continuous-option` **Version**: 21.0.0-beta.3 Set the `continuous` option to `true` for continuous tasks. #### Set `continuous` Option for Continuous Tasks This migration sets the `continuous` option to `true` for tasks that are known to run continuously, and only if the option is not already explicitly set. Specifically, it updates Angular targets using the following executors: - `@angular-devkit/build-angular:dev-server` - `@angular-devkit/build-angular:ssr-dev-server` - `@nx/angular:dev-server` - `@nx/angular:module-federation-dev-server` - `@nx/angular:module-federation-dev-ssr` #### Examples ##### Before ```json title="apps/app1/project.json" { // ... "targets": { // ... "serve": { "executor": "@angular-devkit/build-angular:dev-server", "options": { "buildTarget": "my-app:build", "port": 4200 } } } } ``` ##### After ```json title="apps/app1/project.json" {6} { // ... "targets": { // ... "serve": { "continuous": true, "executor": "@angular-devkit/build-angular:dev-server", "options": { "buildTarget": "my-app:build", "port": 4200 } } } } ``` When a target is already explicitly configured with a `continuous` option, the migration will not modify it: ##### Before ```json title="apps/app1/project.json" {6} { // ... "targets": { // ... "serve": { "continuous": false, "executor": "@nx/angular:dev-server", "options": { "buildTarget": "my-app:build", "port": 4200 } } } } ``` ##### After ```json title="apps/app1/project.json" {6} { // ... "targets": { // ... "serve": { "continuous": false, "executor": "@nx/angular:dev-server", "options": { "buildTarget": "my-app:build", "port": 4200 } } } } ``` ### `change-data-persistence-operators-imports-to-ngrx-router-store-data-persistence` **Version**: 21.0.0-beta.5 Change the data persistence operator imports to '@ngrx/router-store/data-persistence'. #### Requires | Name | Version | |------|---------| `@ngrx/store` | `>=16.0.0` | #### Change the Data Persistence Operator Imports from `@nx/angular` to `@ngrx/router-store/data-persistence` The data persistence operators (`fetch`, `navigation`, `optimisticUpdate`, and `pessimisticUpdate`) have been deprecated for a while and are now removed from the `@nx/angular` package. This migration automatically updates your import statements to use the `@ngrx/router-store/data-persistence` module and adds `@ngrx/router-store` to your dependencies if needed. #### Examples If you import only data persistence operators from `@nx/angular`, the migration will update the import path to `@ngrx/router-store/data-persistence`. ##### Before ```ts title="apps/app1/src/app/users/users.effects.ts" {2} import { Actions, createEffect, ofType } from '@ngrx/effects'; import { fetch } from '@nx/angular'; @Injectable() export class UsersEffects { // ... } ``` ##### After ```ts title="apps/app1/src/app/users/users.effects.ts" {2} import { Injectable } from '@angular/core'; import { fetch } from '@ngrx/router-store/data-persistence'; @Injectable() export class UsersEffects { // ... } ``` If you import multiple data persistence operators from `@nx/angular`, the migration will update the import path for all of them. ##### Before ```ts title="apps/app1/src/app/users/users.effects.ts" {2} import { Injectable } from '@angular/core'; import { fetch, navigation } from '@nx/angular'; @Injectable() export class UsersEffects { // ... } ``` ##### After ```ts title="apps/app1/src/app/users/users.effects.ts" {2} import { Injectable } from '@angular/core'; import { fetch, navigation } from '@ngrx/router-store/data-persistence'; @Injectable() export class UsersEffects { // ... } ``` If your imports mix data persistence operators with other utilities from `@nx/angular`, the migration will split them into separate import statements. ##### Before ```ts title="apps/app1/src/app/users/users.effects.ts" {2} import { Injectable } from '@angular/core'; import { fetch, someExtraUtility, navigation } from '@nx/angular'; @Injectable() export class UsersEffects { // ... } ``` ##### After ```ts title="apps/app1/src/app/users/users.effects.ts" {2-3} import { Injectable } from '@angular/core'; import { fetch, navigation } from '@ngrx/router-store/data-persistence'; import { someExtraUtility } from '@nx/angular'; @Injectable() export class UsersEffects { // ... } ``` If you don't already have `@ngrx/router-store` in your dependencies, the migration will add it to your package.json. ##### Before ```jsonc title="package.json" { "dependencies": { "@nx/angular": "^21.0.0", "@ngrx/store": "^19.1.0", "@ngrx/effects": "^19.1.0", // ... }, } ``` ##### After ```jsonc title="package.json" {6} { "dependencies": { "@nx/angular": "^21.0.0", "@ngrx/store": "^19.1.0", "@ngrx/effects": "^19.1.0", "@ngrx/router-store": "^19.1.0", // ... }, } ``` ## 20.8.x ### 20.8.1-package-updates **Version**: 20.8.1-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@nx/angular-rspack` | `^20.7.0` | Updated only ## 20.5.x ### `update-angular-cli-version-19-2-0` **Version**: 20.5.0-beta.5 Update the @angular/cli package version to ~19.2.0. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=19.2.0` | #### Sample Code Changes Update the `@angular/cli` package version in the `package.json` file at the workspace root to **~19.2.0**. ##### Before ```json title="package.json" { "devDependencies": { "@angular/cli": "~19.1.0" } } ``` ##### After ```json title="package.json" {3} { "devDependencies": { "@angular/cli": "~19.2.0" } } ``` ### 20.5.0-package-updates **Version**: 20.5.0-beta.5 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-devkit/build-angular` | `~19.2.0` | Updated only | `@angular-devkit/core` | `~19.2.0` | Updated only | `@angular-devkit/schematics` | `~19.2.0` | Updated only | `@angular/build` | `~19.2.0` | Updated only | `@angular/pwa` | `~19.2.0` | Updated only | `@angular/ssr` | `~19.2.0` | Updated only | `@schematics/angular` | `~19.2.0` | Updated only | `@angular-devkit/architect` | `~0.1902.0` | Updated only | `@angular-devkit/build-webpack` | `~0.1902.0` | Updated only | `@angular/core` | `~19.2.0` | Added if not installed | `@angular/material` | `~19.2.1` | Updated only | `@angular/cdk` | `~19.2.1` | Updated only | `@angular/google-maps` | `~19.2.1` | Updated only | `ng-packagr` | `~19.2.0` | Updated only ### 20.5.0-angular-eslint-package-updates **Version**: 20.5.0-rc.1 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `angular-eslint` | `^19.2.0` | Updated only | `@angular-eslint/eslint-plugin` | `^19.2.0` | Updated only | `@angular-eslint/eslint-plugin-template` | `^19.2.0` | Updated only | `@angular-eslint/template-parser` | `^19.2.0` | Updated only | `@angular-eslint/utils` | `^19.2.0` | Updated only | `@angular-eslint/schematics` | `^19.2.0` | Updated only | `@angular-eslint/test-utils` | `^19.2.0` | Updated only | `@angular-eslint/builder` | `^19.2.0` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^19.2.0` | Updated only ## 20.4.x ### `update-angular-cli-version-19-1-0` **Version**: 20.4.0-beta.1 Update the @angular/cli package version to ~19.1.0. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=19.1.0` | #### Update `@angular/cli` to `~19.1.0` Update the version of the Angular CLI if it is specified in `package.json` #### Sample Code Changes Update in `devDependencies`: ##### Before ```json title="package.json" { "devDependencies": { "@angular/cli": "~13.3.0" } } ``` ##### After ```json title="package.json" { "devDependencies": { "@angular/cli": "~19.1.0" } } ``` Update in `dependencies`: ##### Before ```json title="package.json" { "dependencies": { "@angular/cli": "~13.3.0" } } ``` ##### After ```json title="package.json" { "dependencies": { "@angular/cli": "~19.1.0" } } ``` ### 20.4.0-package-updates **Version**: 20.4.0-beta.1 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-devkit/build-angular` | `~19.1.0` | Updated only | `@angular-devkit/core` | `~19.1.0` | Updated only | `@angular-devkit/schematics` | `~19.1.0` | Updated only | `@angular/build` | `~19.1.0` | Updated only | `@angular/pwa` | `~19.1.0` | Updated only | `@angular/ssr` | `~19.1.0` | Updated only | `@schematics/angular` | `~19.1.0` | Updated only | `@angular-devkit/architect` | `~0.1901.0` | Updated only | `@angular-devkit/build-webpack` | `~0.1901.0` | Updated only | `@angular/core` | `~19.1.0` | Added if not installed | `@angular/material` | `~19.1.0` | Updated only | `@angular/cdk` | `~19.1.0` | Updated only | `@angular/google-maps` | `~19.1.0` | Updated only | `ng-packagr` | `~19.1.0` | Updated only ## 20.3.x ### `ensure-nx-module-federation-package` **Version**: 20.3.0-beta.2 If workspace includes Module Federation projects, ensure the new @nx/module-federation package is installed. #### Ensure the @nx/module-federation Package is Installed If workspace includes Module Federation projects, ensure the new `@nx/module-federation` package is installed. #### Sample Code Changes ##### Before ```json title="package.json" { "dependencies": {} } ``` ##### After ```json title="package.json" { "dependencies": { "@nx/module-federation": "20.3.0" } } ``` ### 20.2.3-ngrx-package-updates **Version**: 20.3.0-beta.2 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@ngrx/store` | `^19.0.0` | Updated only ## 20.2.x ### `update-20-2-0-update-module-federation-config-import` **Version**: 20.2.0-beta.2 Update the ModuleFederationConfig import use @nx/module-federation. #### Migrate Module Federation Imports to New Package Update the ModuleFederationConfig imports to use @nx/module-federation. #### Sample Code Changes Update import paths for ModuleFederationConfig. ##### Before ```js title="apps/shell/webpack.config.js" import { ModuleFederationConfig } from '@nx/webpack'; ``` ##### After ```js title="apps/shell/webpack.config.js" import { ModuleFederationConfig } from '@nx/module-federation'; ``` ### `update-20-2-0-update-with-module-federation-import` **Version**: 20.2.0-beta.2 Update the withModuleFederation import use @nx/module-federation/angular. #### Migrate withModuleFederation Import to New Package Update the withModuleFederation import to use @nx/module-federation/webpack. #### Sample Code Changes Update import paths for `withModuleFederation` and `withModuleFederationForSSR`. ##### Before ```ts title="apps/shell/webpack.config.ts" import { withModuleFederation, withModuleFederationForSSR, } from '@nx/angular/module-federation'; ``` ##### After ```ts title="apps/shell/webpack.config.ts" import { withModuleFederation, withModuleFederationForSSR, } from '@nx/module-federation/angular'; ``` ### `update-angular-cli-version-19-0-0` **Version**: 20.2.0-beta.5 Update the @angular/cli package version to ~19.0.0. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=19.0.0` | #### Update `@angular/cli` to `~19.0.0` Update the version of the Angular CLI if it is specified in `package.json` #### Sample Code Changes Update in `devDependencies`: ##### Before ```json title="package.json" { "devDependencies": { "@angular/cli": "~13.3.0" } } ``` ##### After ```json title="package.json" { "devDependencies": { "@angular/cli": "~19.0.0" } } ``` Update in `dependencies`: ##### Before ```json title="package.json" { "dependencies": { "@angular/cli": "~13.3.0" } } ``` ##### After ```json title="package.json" { "dependencies": { "@angular/cli": "~19.0.0" } } ``` ### `add-localize-polyfill-to-targets` **Version**: 20.2.0-beta.5 Add the '@angular/localize/init' polyfill to the 'polyfills' option of targets using esbuild-based executors. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=19.0.0` | #### Add Localize Polyfill to Targets Add the '@angular/localize/init' polyfill to the 'polyfills' option of targets using esbuild-based executors. #### Sample Code Changes Add the `@angular/localize/init` polyfill to any of these executors: - `@angular/build:application` - `@angular-devkit/build-angular:application` - `@nx/angular:application` - `@angular-devkit/build-angular:browser-esbuild` - `@nx/angular:browser-esbuild` ##### Before ```json title="apps/app1/project.json" { "targets": { "build": { "executor": "@angular/build:application", "options": { "localize": true } } } } ``` ##### After ```json title="apps/app1/project.json" { "targets": { "build": { "executor": "@angular/build:application", "options": { "localize": true, "polyfills": ["@angular/localize/init"] } } } } ``` ### `update-angular-ssr-imports-to-use-node-entry-point` **Version**: 20.2.0-beta.5 Update '@angular/ssr' import paths to use the new '/node' entry point when 'CommonEngine' is detected. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=19.0.0` | #### Update Angular SSR Imports to Use Node Entry Point Update '@angular/ssr' import paths to use the new '/node' entry point when 'CommonEngine' is detected. #### Sample Code Changes Update import paths for SSR CommonEngine properties to use `@angular/ssr/node`. ##### Before ```ts title="apps/app1/server.ts" import { CommonEngine } from '@angular/ssr'; import type { CommonEngineOptions, CommonEngineRenderOptions, } from '@angular/ssr'; ``` ##### After ```ts title="apps/app1/server.ts" import { CommonEngine } from '@angular/ssr/node'; import type { CommonEngineOptions, CommonEngineRenderOptions, } from '@angular/ssr/node'; ``` ### `disable-angular-eslint-prefer-standalone` **Version**: 20.2.0-beta.6 Disable the Angular ESLint prefer-standalone rule if not set. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=19.0.0` | #### Disable Angular ESLint Prefer Standalone Disable the Angular ESLint prefer-standalone rule if not set. #### Sample Code Changes Update import paths for `withModuleFederation` and `withModuleFederationForSSR`. ##### Before ```json title="apps/app1/.eslintrc.json" { "overrides": [ { "files": ["*.html"], "rules": { "some-rule-for-html": "error" } } ] } ``` ##### After ```json title="apps/app1/.eslintrc.json" { "overrides": [ { "files": ["*.html"], "rules": { "some-rule-for-html": "error" } }, { "files": ["*.ts"], "rules": { "@angular-eslint/prefer-standalone": "off" } } ] } ``` import { addProjectConfiguration, writeJson, type ProjectConfiguration, type ProjectGraph, type Tree, } from '@nx/devkit'; import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; import migration from './disable-angular-eslint-prefer-standalone'; let projectGraph: ProjectGraph; jest.mock('@nx/devkit', () => ({ ...jest.requireActual('@nx/devkit'), createProjectGraphAsync: () => Promise.resolve(projectGraph), })); describe('disable-angular-eslint-prefer-standalone', () => { let tree: Tree; beforeEach(() => { tree = createTreeWithEmptyWorkspace(); const projectConfig: ProjectConfiguration = { name: 'app1', root: 'apps/app1', }; projectGraph = { dependencies: { app1: [ { source: 'app1', target: 'npm:@angular/core', type: 'static', }, ], }, nodes: { app1: { data: projectConfig, name: 'app1', type: 'app', }, }, }; addProjectConfiguration(tree, projectConfig.name, projectConfig); }); describe('.eslintrc.json', () => { it('should not disable @angular-eslint/prefer-standalone when it is set', async () => { writeJson(tree, 'apps/app1/.eslintrc.json', { overrides: [ { files: ['*.ts'], rules: { '@angular-eslint/prefer-standalone': ['error'] }, }, ], }); await migration(tree); expect(tree.read('apps/app1/.eslintrc.json', 'utf8')) .toMatchInlineSnapshot(` "{ "overrides": [ { "files": ["*.ts"], "rules": { "@angular-eslint/prefer-standalone": ["error"] } } ] } " `); }); it('should not disable @angular-eslint/prefer-standalone when there are multiple overrides for angular eslint and the rule is set in one of them', async () => { writeJson(tree, 'apps/app1/.eslintrc.json', { overrides: [ { files: ['*.ts'], rules: { '@angular-eslint/directive-selector': [ 'error', { type: 'attribute', prefix: 'app', style: 'camelCase' }, ], }, }, { files: ['*.ts'], rules: { '@angular-eslint/prefer-standalone': ['error'] }, }, ], }); await migration(tree); expect(tree.read('apps/app1/.eslintrc.json', 'utf8')) .toMatchInlineSnapshot(` "{ "overrides": [ { "files": ["*.ts"], "rules": { "@angular-eslint/directive-selector": [ "error", { "type": "attribute", "prefix": "app", "style": "camelCase" } ] } }, { "files": ["*.ts"], "rules": { "@angular-eslint/prefer-standalone": ["error"] } } ] } " `); }); it('should disable @angular-eslint/prefer-standalone in an existing override for angular eslint', async () => { writeJson(tree, 'apps/app1/.eslintrc.json', { overrides: [ { files: ['*.ts'], rules: { 'no-unused-vars': 'error' }, }, { files: ['*.ts'], rules: { '@angular-eslint/directive-selector': [ 'error', { type: 'attribute', prefix: 'app', style: 'camelCase' }, ], }, }, ], }); await migration(tree); expect(tree.read('apps/app1/.eslintrc.json', 'utf8')) .toMatchInlineSnapshot(` "{ "overrides": [ { "files": ["*.ts"], "rules": { "no-unused-vars": "error" } }, { "files": ["*.ts"], "rules": { "@angular-eslint/directive-selector": [ "error", { "type": "attribute", "prefix": "app", "style": "camelCase" } ], "@angular-eslint/prefer-standalone": "off" } } ] } " `); }); it('should disable @angular-eslint/prefer-standalone in an existing override for ts files', async () => { writeJson(tree, 'apps/app1/.eslintrc.json', { overrides: [ { files: ['*.ts'], rules: { 'no-unused-vars': 'error' }, }, ], }); await migration(tree); expect(tree.read('apps/app1/.eslintrc.json', 'utf8')) .toMatchInlineSnapshot(` "{ "overrides": [ { "files": ["*.ts"], "rules": { "no-unused-vars": "error", "@angular-eslint/prefer-standalone": "off" } } ] } " `); }); it('should disable @angular-eslint/prefer-standalone in a new override', async () => { writeJson(tree, 'apps/app1/.eslintrc.json', { overrides: [ { files: ['*.html'], rules: { 'some-rule-for-html': 'error' }, }, ], }); await migration(tree); expect(tree.read('apps/app1/.eslintrc.json', 'utf8')) .toMatchInlineSnapshot(` "{ "overrides": [ { "files": ["*.html"], "rules": { "some-rule-for-html": "error" } }, { "files": ["*.ts"], "rules": { "@angular-eslint/prefer-standalone": "off" } } ] } " `); }); }); describe('flat config', () => { it('should not disable @angular-eslint/prefer-standalone when it is set', async () => { tree.write('eslint.config.js', 'module.exports = [];'); tree.write( 'apps/app1/eslint.config.js', `module.exports = [ { files: ['*.ts'], rules: { '@angular-eslint/prefer-standalone': ['error'] }, }, ]; ` ); await migration(tree); expect(tree.read('apps/app1/eslint.config.js', 'utf8')) .toMatchInlineSnapshot(` "module.exports = [ { files: ['*.ts'], rules: { '@angular-eslint/prefer-standalone': ['error'] }, }, ]; " `); }); it('should not disable @angular-eslint/prefer-standalone when there are multiple overrides for angular eslint and the rule is set in one of them', async () => { tree.write('eslint.config.js', 'module.exports = [];'); tree.write( 'apps/app1/eslint.config.js', `module.exports = [ { files: ['*.ts'], rules: { '@angular-eslint/directive-selector': [ 'error', { type: 'attribute', prefix: 'app', style: 'camelCase' }, ], }, }, { files: ['*.ts'], rules: { '@angular-eslint/prefer-standalone': ['error'] }, }, ]; ` ); await migration(tree); expect(tree.read('apps/app1/eslint.config.js', 'utf8')) .toMatchInlineSnapshot(` "module.exports = [ { files: ['*.ts'], rules: { '@angular-eslint/directive-selector': [ 'error', { type: 'attribute', prefix: 'app', style: 'camelCase' }, ], }, }, { files: ['*.ts'], rules: { '@angular-eslint/prefer-standalone': ['error'] }, }, ]; " `); }); it('should disable @angular-eslint/prefer-standalone in an existing override for angular eslint', async () => { tree.write('eslint.config.js', 'module.exports = [];'); tree.write( 'apps/app1/eslint.config.js', `module.exports = [ { files: ['*.ts'], rules: { 'no-unused-vars': 'error' }, }, { files: ['*.ts'], rules: { '@angular-eslint/directive-selector': [ 'error', { type: 'attribute', prefix: 'app', style: 'camelCase' }, ], }, }, ]; ` ); await migration(tree); expect(tree.read('apps/app1/eslint.config.js', 'utf8')) .toMatchInlineSnapshot(` "module.exports = [ { files: ['*.ts'], rules: { 'no-unused-vars': 'error' }, }, { files: ['**/*.ts'], rules: { '@angular-eslint/directive-selector': [ 'error', { type: 'attribute', prefix: 'app', style: 'camelCase', }, ], '@angular-eslint/prefer-standalone': 'off', }, }, ]; " `); }); it('should disable @angular-eslint/prefer-standalone in an existing override for ts files', async () => { tree.write('eslint.config.js', 'module.exports = [];'); tree.write( 'apps/app1/eslint.config.js', `module.exports = [ { files: ['*.ts'], rules: { 'no-unused-vars': 'error' }, }, ]; ` ); await migration(tree); expect(tree.read('apps/app1/eslint.config.js', 'utf8')) .toMatchInlineSnapshot(` "module.exports = [ { files: ['**/*.ts'], rules: { 'no-unused-vars': 'error', '@angular-eslint/prefer-standalone': 'off', }, }, ]; " `); }); it('should disable @angular-eslint/prefer-standalone in a new override', async () => { tree.write('eslint.config.js', 'module.exports = [];'); tree.write( 'apps/app1/eslint.config.js', `module.exports = [ { files: ['*.html'], rules: { 'some-rule-for-html': 'error' }, }, ]; ` ); await migration(tree); expect(tree.read('apps/app1/eslint.config.js', 'utf8')) .toMatchInlineSnapshot(` "module.exports = [ { files: ['*.html'], rules: { 'some-rule-for-html': 'error' }, }, { files: ['**/*.ts'], rules: { '@angular-eslint/prefer-standalone': 'off', }, }, ]; " `); }); }); }); ### `remove-angular-eslint-rules` **Version**: 20.2.0-beta.8 Remove Angular ESLint rules that were removed in v19.0.0. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=19.0.0` | #### Remove Angular ESLint Rules Remove Angular ESLint rules that were removed in v19.0.0. #### Sample Code Changes Removes `@angular-eslint/no-host-metadata-property`, `@angular-eslint/sort-ngmodule-metadata-arrays` and `@angular-eslint/prefer-standalone-component` from any ESLint config file. Files to be searched include `.eslintrc.json`, `.eslintrc.base.json`, `.eslint.config.js` and `.eslint.config.base.js`. ##### Before ```json title="apps/app1/.eslintrc.json" { "overrides": [ { "files": ["*.ts"], "rules": { "@angular-eslint/no-host-metadata-property": ["error"], "@angular-eslint/sort-ngmodule-metadata-arrays": ["error"], "@angular-eslint/prefer-standalone-component": ["error"] } } ] } ``` ##### After ```json title="apps/app1/.eslintrc.json" { "overrides": [ { "files": ["*.ts"], "rules": {} } ] } ``` ### `remove-tailwind-config-from-ng-packagr-executors` **Version**: 20.2.0-beta.8 Remove the deprecated 'tailwindConfig' option from ng-packagr executors. Tailwind CSS configurations located at the project or workspace root will be picked up automatically. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=19.0.0` | #### Remove tailwindConfig from ng-packagr Executors Remove the deprecated 'tailwindConfig' option from ng-packagr executors. Tailwind CSS configurations located at the project or workspace root will be picked up automatically. #### Sample Code Changes Remove `tailwindConfig` from the `@nx/angular:ng-packagr-lite` or `@nx/angular:package` executor options in project configuration. ##### Before ```json title="libs/my-lib/project.json" { "targets": { "build": { "executor": "@nx/angular:ng-packagr-lite", "options": { "project": "libs/lib1/ng-package.json", "tailwindConfig": "libs/lib1/tailwind.config.js" } } } } ``` ##### After ```json title="libs/my-lib/project.json" { "targets": { "build": { "executor": "@nx/angular:ng-packagr-lite", "options": { "project": "libs/lib1/ng-package.json" } } } } ``` Remove `tailwindConfig` from the `@nx/angular:ng-packagr-lite` or `@nx/angular:package` executor target defaults in `nx.json`. ##### Before ```json title="nx.json" { "targetDefaults": { "@nx/angular:ng-packagr-lite": { "options": { "project": "{projectRoot}/ng-package.json", "tailwindConfig": "{projectRoot}/tailwind.config.js" } } } } ``` ##### After ```json title="nx.json" { "targetDefaults": { "@nx/angular:ng-packagr-lite": { "options": { "project": "{projectRoot}/ng-package.json" } } } } ``` ### 20.2.0-module-federation-package-updates **Version**: 20.2.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/enhanced` | `0.7.6` | Updated only | `@module-federation/runtime` | `0.7.6` | Updated only | `@module-federation/sdk` | `0.7.6` | Updated only | `@module-federation/node` | `2.6.11` | Updated only ### 20.2.0-package-updates **Version**: 20.2.0-beta.5 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-devkit/build-angular` | `~19.0.0` | Updated only | `@angular-devkit/core` | `~19.0.0` | Updated only | `@angular-devkit/schematics` | `~19.0.0` | Updated only | `@angular/build` | `~19.0.0` | Updated only | `@angular/pwa` | `~19.0.0` | Updated only | `@angular/ssr` | `~19.0.0` | Updated only | `@schematics/angular` | `~19.0.0` | Updated only | `@angular-devkit/architect` | `~0.1900.0` | Updated only | `@angular-devkit/build-webpack` | `~0.1900.0` | Updated only | `@angular/core` | `~19.0.0` | Added if not installed | `@angular/material` | `~19.0.0` | Updated only | `@angular/cdk` | `~19.0.0` | Updated only | `@angular/google-maps` | `~19.0.0` | Updated only | `ng-packagr` | `~19.0.0` | Updated only | `zone.js` | `~0.15.0` | Updated only ### 20.2.0-jest-package-updates **Version**: 20.2.0-beta.5 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `jest-preset-angular` | `~14.4.0` | Updated only ### 20.2.0-angular-eslint-package-updates **Version**: 20.2.0-beta.5 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `angular-eslint` | `^19.0.0` | Updated only | `@angular-eslint/eslint-plugin` | `^19.0.0` | Updated only | `@angular-eslint/eslint-plugin-template` | `^19.0.0` | Updated only | `@angular-eslint/template-parser` | `^19.0.0` | Updated only | `@angular-eslint/utils` | `^19.0.0` | Updated only | `@angular-eslint/schematics` | `^19.0.0` | Updated only | `@angular-eslint/test-utils` | `^19.0.0` | Updated only | `@angular-eslint/builder` | `^19.0.0` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^19.0.0` | Updated only ### 20.2.0-analog-package-updates **Version**: 20.2.0-beta.7 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@analogjs/vitest-angular` | `~1.10.0` | Updated only | `@analogjs/vite-plugin-angular` | `~1.10.0` | Updated only ### 20.2.2-angular-eslint-package-updates **Version**: 20.2.2-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `angular-eslint` | `^19.0.2` | Updated only | `@angular-eslint/eslint-plugin` | `^19.0.2` | Updated only | `@angular-eslint/eslint-plugin-template` | `^19.0.2` | Updated only | `@angular-eslint/template-parser` | `^19.0.2` | Updated only | `@angular-eslint/utils` | `^19.0.2` | Updated only | `@angular-eslint/schematics` | `^19.0.2` | Updated only | `@angular-eslint/test-utils` | `^19.0.2` | Updated only | `@angular-eslint/builder` | `^19.0.2` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^19.0.2` | Updated only ## 19.7.x ### 19.7.0-package-updates **Version**: 19.7.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/enhanced` | `~0.6.0` | Updated only | `@module-federation/node` | `~2.5.0` | Updated only ## 19.6.x ### `update-19-6-0` **Version**: 19.6.0-beta.4 Ensure Module Federation DTS is turned off by default. ### `update-angular-cli-version-18-2-0` **Version**: 19.6.0-beta.7 Update the @angular/cli package version to ~18.2.0. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=18.2.0` | ### `update-19-6-1-ensure-module-federation-target-defaults` **Version**: 19.6.1-beta.0 Ensure Target Defaults are set correctly for Module Federation. ### 19.6.0-package-updates **Version**: 19.6.0-beta.7 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-devkit/build-angular` | `~18.2.0` | Updated only | `@angular-devkit/core` | `~18.2.0` | Updated only | `@angular-devkit/schematics` | `~18.2.0` | Updated only | `@angular/build` | `~18.2.0` | Updated only | `@angular/pwa` | `~18.2.0` | Updated only | `@angular/ssr` | `~18.2.0` | Updated only | `@schematics/angular` | `~18.2.0` | Updated only | `@angular-devkit/architect` | `~0.1802.0` | Updated only | `@angular-devkit/build-webpack` | `~0.1802.0` | Updated only | `@angular/core` | `~18.2.0` | Added if not installed | `@angular/material` | `~18.2.0` | Updated only | `@angular/cdk` | `~18.2.0` | Updated only | `ng-packagr` | `~18.2.0` | Updated only | `zone.js` | `~0.14.10` | Updated only ### 19.6.1-ngrx-package-updates **Version**: 19.6.1-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@ngrx/store` | `^18.0.2` | Updated only ## 19.5.x ### `update-angular-cli-version-18-1-0` **Version**: 19.5.0-beta.1 Update the @angular/cli package version to ~18.1.0. #### Requires | Name | Version | |------|---------| `@angular/core` | `>=18.1.0` | ### 19.5.0-module-federation-package-updates **Version**: 19.5.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/node` | `^2.3.0` | Updated only ### 19.5.0-package-updates **Version**: 19.5.0-beta.1 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-devkit/build-angular` | `~18.1.0` | Updated only | `@angular-devkit/core` | `~18.1.0` | Updated only | `@angular-devkit/schematics` | `~18.1.0` | Updated only | `@angular/build` | `~18.1.0` | Updated only | `@angular/pwa` | `~18.1.0` | Updated only | `@angular/ssr` | `~18.1.0` | Updated only | `@schematics/angular` | `~18.1.0` | Updated only | `@angular-devkit/architect` | `~0.1801.0` | Updated only | `@angular-devkit/build-webpack` | `~0.1801.0` | Updated only | `@angular/core` | `~18.1.0` | Added if not installed | `@angular/material` | `~18.1.0` | Updated only | `@angular/cdk` | `~18.1.0` | Updated only | `ng-packagr` | `~18.1.0` | Updated only ### 19.5.4-ngrx-package-updates **Version**: 19.5.4-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@ngrx/store` | `^18.0.1` | Updated only | `@ngrx/operators` | `^18.0.1` | Updated only ## 19.4.x ### 19.4.0-ngrx-package-updates **Version**: 19.4.0-beta.1 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@ngrx/store` | `^18.0.0` | Updated only ## 19.2.x ### `add-typescript-eslint-utils` **Version**: 19.2.1-beta.0 Installs the '@typescript-eslint/utils' package when having installed '@angular-eslint/eslint-plugin' or '@angular-eslint/eslint-plugin-template' with version >=18.0.0. #### Requires | Name | Version | |------|---------| `@angular-eslint/eslint-plugin` | `>=18.0.0` | ## 19.1.x ### 19.1.2-package-updates **Version**: 19.1.2-beta.1 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@angular-eslint/eslint-plugin` | `^18.0.1` | Updated only | `@angular-eslint/eslint-plugin-template` | `^18.0.1` | Updated only | `@angular-eslint/template-parser` | `^18.0.1` | Updated only | `@angular-eslint/utils` | `^18.0.1` | Updated only | `@angular-eslint/schematics` | `^18.0.1` | Updated only | `@angular-eslint/test-utils` | `^18.0.1` | Updated only | `@angular-eslint/builder` | `^18.0.1` | Updated only | `@angular-eslint/bundled-angular-compiler` | `^18.0.1` | Updated only --- ## Build Tools {% index_page_cards path="technologies/build-tools" /%} --- ## Docker Get started with [Nx with Docker](/docs/technologies/build-tools/docker/introduction), or browse the topics below. {% index_page_cards path="technologies/build-tools/docker" /%} --- ## Nx with Docker [Docker](https://www.docker.com/) packages applications into portable container images. In a Docker monorepo, Nx builds, caches, and publishes those images alongside the rest of your projects. The Nx Plugin for Docker contains executors and utilities for building and publishing docker images within an Nx workspace. Using the `@nx/docker` [Inference Plugin](/docs/concepts/mental-model#inferred-tasks), Nx will automatically detect `Dockerfile`'s in your workspace and provide a `docker:build` and `docker:run` target for each. It will also provide a `nx-release-publish` target for publishing docker images to a registry. ## Installation In any Nx workspace, you can install `@nx/docker` by running the following command: ```shell nx add @nx/docker ``` This will install the correct version of `@nx/docker` to match the version of Nx you are using. ### How @nx/docker infers tasks The `@nx/docker` plugin will create a task for any project that has a Dockerfile present. It will infer the following tasks: - `docker:build` - `docker:run` - `nx-release-publish` ### View inferred tasks To view inferred tasks for a project, open the [project details view](/docs/concepts/mental-model#inferred-tasks) in Nx Console or run `nx show project my-project` in the command line. ### @nx/docker configuration The `@nx/docker` plugin is configured in the `plugins` array in `nx.json`. ```json // nx.json { "plugins": [ { "plugin": "@nx/docker", "options": { "buildTarget": "docker:build", "runTarget": "docker:run" } } ] } ``` The `buildTarget` and `runTarget` options control the names of the inferred Docker tasks. The default names are `docker:build` and `docker:run`. ### Advanced plugin configuration Configure `buildTarget` and `runTarget` as objects to pass custom arguments, set environment variables, and use pattern interpolation. ```json // nx.json { "plugins": [ { "plugin": "@nx/docker", "options": { "buildTarget": { "name": "docker:build", "args": [ "--platform", "linux/amd64,linux/arm64", "--label", "project={projectName}" ], "env": { "DOCKER_BUILDKIT": "1" }, "envFile": ".env.docker", "cwd": "{projectRoot}/docker" }, "runTarget": { "name": "docker:run", "args": ["--rm"], "env": { "NODE_ENV": "production" } } } } ] } ``` #### Configuration properties When using the object format, the following properties are available: - **`name`** (string, required): The name of the task - **`args`** (string[], optional): Additional arguments to pass to the Docker command. Supports pattern interpolation. - **`env`** (object, optional): Environment variables to set when running the Docker command. Values support pattern interpolation. - **`envFile`** (string, optional): Path to an environment file to load - **`cwd`** (string, optional): Working directory for the command. Supports pattern interpolation. - **`configurations`** (object, optional): Named Nx configurations that override `args`, `env`, `envFile`, `cwd`, or `skipDefaultTag`. - **`skipDefaultTag`** (boolean, default `false`): Do not add the default Nx `--tag` argument to build commands. Supply a tag in `args`; opting out also disables Nx Release versioning and publishing for that Docker project. The build task depends on the project's `build` task and dependency builds. The run task depends on the Docker build task. Nx also adds an `nx-release-publish` task for publishing images with [Nx Release](/docs/features/manage-releases). #### Pattern interpolation All string values in the configuration support pattern interpolation using the following tokens: - **`{projectName}`** - The name of the project from `project.json` or `package.json` - **`{projectRoot}`** - The root directory of the project (e.g., `apps/api`) - **`{imageRef}`** - The default image reference derived from the project root (e.g., `apps-api`) - **`{currentDate}`** - Current date in ISO format (e.g., `2025-01-30T14:30:00.000Z`) - **`{currentDate|FORMAT}`** - Current date with custom formatting (e.g., `{currentDate|YYYY.MM.DD}` → `2025.01.30`) - **`{commitSha}`** - Full Git commit SHA - **`{shortCommitSha}`** - First 7 characters of the commit SHA - **`{env.VAR_NAME}`** - Environment variable values (e.g., `{env.BUILD_NUMBER}`) Date formatting supports these tokens: `YYYY` (year), `YY` (2-digit year), `MM` (month), `DD` (day), `HH` (hours), `mm` (minutes), `ss` (seconds). #### Example: multi-Platform build with metadata Here's a practical example that builds Docker images for multiple platforms and includes build metadata: ```json // nx.json { "plugins": [ { "plugin": "@nx/docker", "options": { "buildTarget": { "name": "build", "args": [ "--platform", "linux/amd64,linux/arm64", "--label", "org.opencontainers.image.title={projectName}", "--label", "org.opencontainers.image.source={projectRoot}", "--label", "org.opencontainers.image.revision={commitSha}", "--label", "org.opencontainers.image.created={currentDate}", "--build-arg", "BUILD_DATE={currentDate|YYYY-MM-DD}", "--build-arg", "VERSION={env.VERSION}", "--tag", "{imageRef}:{currentDate|YYMMDD}.{shortCommitSha}" ], "env": { "DOCKER_BUILDKIT": "1", "IMAGE_NAME": "{imageRef}" } }, "runTarget": { "name": "run", "args": [ "--rm", "--publish", "3000:3000", "--env", "NODE_ENV=production" ] } } } ] } ``` This configuration: - Builds for both AMD64 and ARM64 architectures - Adds OpenContainers metadata labels with project information - Tags images with a calendar version and commit SHA (e.g., `my-app:250130.abc1234`) - Enables Docker BuildKit for improved build performance - Injects build arguments and environment variables from the CI/CD pipeline Use `include` and `exclude` glob patterns on the plugin entry to scope inference. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/docker`. ## Set up CI In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). {% aside type="note" title="Building Docker images on Nx Agents" %} To build Docker images on Nx Agents, your organization needs the [dedicated compute cluster](/docs/features/ci-features/dedicated-compute-cluster) add-on. It reserves an isolated Nx Cloud environment that runs Docker-in-Docker, which agents need to build and push container images. {% /aside %} ## Using Nx release to publish Docker images The `@nx/docker` plugin provides a `nx-release-publish` target for publishing docker images to a registry. [Nx Release](/docs/features/manage-releases) was also updated to support versioning docker images, generating changelogs, and publishing docker images to a registry through a single command. You can learn more about how to manage releases for Docker Images in the [Release Docker Images](/docs/kb/release-docker-images) guide. --- ## @nx/docker Migrations For an overview of the plugin and setup instructions, see the [@nx/docker introduction](/docs/technologies/build-tools/docker/introduction). The @nx/docker plugin provides various migrations to help you migrate to newer versions of docker projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.0.x ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/docker` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/docker` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/docker` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/docker'; ``` ##### After ```ts import { createNodes } from '@nx/docker'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/docker` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. --- ## ESBuild Get started with [Nx with esbuild](/docs/technologies/build-tools/esbuild/introduction), or browse the topics below. {% index_page_cards path="technologies/build-tools/esbuild" /%} --- ## @nx/esbuild Executors The @nx/esbuild plugin provides various executors to help you create and configure esbuild projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/esbuild introduction](/docs/technologies/build-tools/esbuild/introduction). ### `esbuild` Bundle a package for different platforms. `/project.json`: ```jsonc { //... "targets": { //... "build": { "executor": "@nx/esbuild:esbuild", "options": { "main": "", "tsConfig": "/tsconfig.app.json", "outputPath": "dist/", }, }, }, } ``` ```bash nx build ``` ### Examples ###### CommonJS output The CommonJS format is required in some environments, such as Electron applications. By default, `esbuild` will use the ESM format, which is recommended for Web and Node applications. You may also output to multiple formats. ```bash nx build --format=cjs nx build --format=esm,cjs nx build # defaults to es# defaults to esm ``` ```json "build": { "executor": "@nx/esbuild:esbuild", "options": { "main": "", "tsConfig": "/tsconfig.app.json", "outputPath": "dist/", "format": ["esm", "cjs"] } } ``` ###### External packages External packages are not bundled by default. To include them in the bundle you can use either the `thirdParty` option to include all third-party dependencies, or use `excludeFromExternal` option to include specific dependencies in the bundle. To mark additional packages or assets as external, you may use the `external` option, which supports the `*` wildcard to match assets. For example, this configuration includes all third-party dependencies such as `lodash` or `date-fns` in the bundle. It also marks all `*.png` files as external assets. ```json "build": { "executor": "@nx/esbuild:esbuild", "options": { "main": "", "tsConfig": "/tsconfig.app.json", "outputPath": "dist/", "thirdParty": true, "external": ["*.png"] } } ``` And this configuration includes only `lodash` in the bundle, while keeping `*.png` files as external assets. ```json "build": { "executor": "@nx/esbuild:esbuild", "options": { "main": "", "tsConfig": "/tsconfig.app.json", "outputPath": "dist/", "excludeFromExternal": ["lodash"], "external": ["*.png"] } } ``` ###### Skip type checking Type checking is the slowest part of the build. You may want to skip type checking during build and run it as another job in CI. ```json "build": { "executor": "@nx/esbuild:esbuild", "options": { "main": "", "tsConfig": "/tsconfig.app.json", "outputPath": "dist/", "skipTypeCheck": true } } ``` ###### Additional esbuild options Additional [esbuild options](https://esbuild.github.io/api/) can be passed using `esbuildOptions` in your project configuration. ```json "build": { "executor": "@nx/esbuild:esbuild", "options": { "main": "", "tsConfig": "/tsconfig.app.json", "outputPath": "dist/", "esbuildOptions": { "legalComments": "inline", "banner": { ".js": "// banner" }, "footer": { ".js": "// footer" } } } } ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `main` | string [**required**] | The path to the entry file, relative to project. | | | `outputPath` | string [**required**] | The output path of the generated files. | | | `tsConfig` | string [**required**] | The path to tsconfig file. | | | `additionalEntryPoints` | array | List of additional entry points. | `[]` | | `assets` | array | List of static assets. | `[]` | | `bundle` | boolean | Whether to bundle the main entry point and additional entry points. Set to false to keep individual output files. | `true` | | `declaration` | boolean | Generate declaration (*.d.ts) files for every TypeScript or JavaScript file inside your project. Should be used for libraries that are published to an npm repository. | | | `declarationRootDir` | string | Sets the rootDir for the declaration (*.d.ts) files. | | | `deleteOutputPath` | boolean | Remove previous output before build. | `true` | | `esbuildConfig` | string | Path to a esbuild configuration file. See https://esbuild.github.io/api/. Cannot be used with 'esbuildOptions' option. | | | `esbuildOptions` | object | Additional options to pass to esbuild. See https://esbuild.github.io/api/. Cannot be used with 'esbuildConfig' option. | | | `excludeFromExternal` | array | List of dependencies that should be excluded from the external list. This is useful when Nx automatically marks certain packages as external, but you want to bundle them instead. | | | `external` | array | Mark one or more module as external. Can use * wildcards, such as '*.png'. | | | `format` | array | List of module formats to output. Defaults to matching format from tsconfig (e.g. CJS for CommonJS, and ESM otherwise). | `["esm"]` | | `generatePackageJson` | boolean | Generates a `package.json` and pruned lock file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated. | `false` | | `metafile` | boolean | Generate a meta.json file in the output folder that includes metadata about the build. This file can be analyzed by other tools. | `false` | | `minify` | boolean | Minifies outputs. | `false` | | `outputFileName` | string | Name of the main output file. Defaults same basename as 'main' file. | | | `outputHashing` | string | Define the output filename cache-busting hashing mode. | `"none"` | | `platform` | string | Platform target for outputs. | `"node"` | | `skipTypeCheck` | boolean | Skip type-checking via TypeScript. Skipping type-checking speeds up the build but type errors are not caught. | `false` | | `sourcemap` | string | Generate sourcemap. | | | `target` | string | The environment target for outputs. | `"esnext"` | | `thirdParty` | boolean | Includes third-party packages in the bundle (i.e. npm packages). | | | `watch` | boolean | Enable re-building when files change. | `false` | --- ## @nx/esbuild Generators The @nx/esbuild plugin provides various generators to help you create and configure esbuild projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/esbuild introduction](/docs/technologies/build-tools/esbuild/introduction). ## `configuration` Add esbuild configuration to a project. ### Examples ```bash nx g @nx/esbuild:configuration my-package ``` :::note[Overwriting existing build option] The `configuration` generator validates that an existing `build` target isn't already defined for the project. If you are adding esbuild to a project with an existing `build` target, pass the `--skipValidation` option. ::: You may also provide a custom main entry file, or a custom tsconfig file if the defaults don't work. By default, the generator will look for a main file matching `src/index.ts` or `src/main.ts`, and a tsconfig file matching `tsconfig.app.json` or `tsconfig.lib.json`. ```bash nx g @nx/esbuild:configuration my-package \ --main=packages/my-package/src/entry.ts \ --tsConfig=packages/my-package/tsconfig.custom.json ``` **Usage:** ```bash nx generate @nx/esbuild:configuration [options] ``` **Aliases:** `esbuild-project` **Arguments:** ```bash nx generate @nx/esbuild:configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--buildTarget` | string | The build target to add. | `"build"` | | `--format` | array | The format to build the library (esm or cjs). | `["esm"]` | | `--importPath` | string | The library name used to import it, like `@myorg/my-awesome-lib`. | | | `--main` | string | Path relative to the workspace root for the main entry file. Defaults to `/src/main.ts` or `src/index.ts`, whichever is found. | | | `--platform` | string | Platform target for outputs. | `"node"` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--skipValidation` | boolean | Do not perform any validation on existing project. | `false` | | `--tsConfig` | string | Path relative to the workspace root for the tsconfig file to build with. Defaults to `/tsconfig.app.json` or `/tsconfig.lib.json`, whichever is found. | | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/esbuild: --help ``` --- ## Nx with esbuild [esbuild](https://esbuild.github.io/api/) is an extremely fast JavaScript bundler. In an esbuild monorepo, Nx builds and caches projects across your workspace so only what changed gets rebuilt. The Nx Plugin for esbuild runs these builds through the `@nx/esbuild:esbuild` executor, with type-checking and asset handling layered on top. ## Requirements The `@nx/esbuild` plugin supports the following package versions. | Package | Supported Versions | | --------- | ------------------ | | `esbuild` | >=0.19.2 <1.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. Why should you use this plugin? - _Fast_ builds using esbuild. - Type-checking using TypeScript, which esbuild does not handle. - Intelligent `package.json` output. - Additional [assets](/docs/technologies/build-tools/esbuild/executors#esbuild) for the output. ## Setting up @nx/esbuild ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/esbuild` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/esbuild` by running the following command: ```shell nx add @nx/esbuild ``` This will install the correct version of `@nx/esbuild`. ## Using the @nx/esbuild plugin ### Creating a new JS library You can add a new library that builds using esbuild with: ```shell nx g @nx/js:lib libs/mylib --bundler=esbuild ``` This command will install the esbuild plugin if needed, and set `@nx/esbuild:esbuild` executor for the `build` target. ### Adding esbuild target to existing libraries If you already have a JS project that you want to use esbuild for, run this command: ```shell nx g @nx/esbuild:configuration mylib ``` This generator validates there isn't an existing `build` target. If you want to overwrite the existing target you can pass the `--skipValidation` option. ```shell nx g @nx/esbuild:configuration mylib --skipValidation ``` ## Using esbuild You can run builds with: ```shell nx build mylib ``` Replace `mylib` with the name or your project. This command works for both applications and libraries. ### Copying assets Assets are non-JS and non-TS files, such as images, CSS, etc. You can add them to the project configuration as follows. ```jsonc "build": { "executor": "@nx/esbuild:esbuild", "options": { //... "assets": [ { "input": "libs/mylib", "glob": "README.md", "output": "/" }, { "input": "libs/mylib", "glob": "logo.png", "output": "/" }, { "input": "libs/mylib", "glob": "docs/**/*.md", "output": "/docs" }, //... ] } } ``` Running `nx build mylib` outputs something like this. ```text dist/libs/mylib/ ├── README.md ├── docs │ ├── CONTRIBUTING.md │ └── TESTING.md ├── index.js ├── logo.png └── package.json ``` ### Generating a metafile A metafile can be generated by passing the `--metafile` option. This file contains information about the build that can be analyzed by other tools, such as [bundle buddy](https://www.bundle-buddy.com/esbuild). ```shell nx build mylib --metafile ``` This command will generate a `meta.json` file in the output directory. ```text dist/libs/mylib/ ├── README.md ├── index.js ├── meta.json └── package.json ``` ### Custom esbuild options Extra API options for esbuild can be passed in the `esbuildOptions` object for your project configuration. ```jsonc "build": { "executor": "@nx/esbuild:esbuild", "options": { //... "esbuildOptions": { "banner": { ".js": "// banner" }, "footer": { ".js": "// footer" } } } } ``` ## More documentation - [Using JS](/docs/technologies/typescript/introduction) ## Set up CI for your esbuild monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/esbuild Migrations For an overview of the plugin and setup instructions, see the [@nx/esbuild introduction](/docs/technologies/build-tools/esbuild/introduction). No migrations found for `@nx/esbuild`. Nx only retains migrations for the last 2 major versions of Nx. It's possible migrations existed in previous versions of `@nx/esbuild`. You can check the previous docs for [Nx v22](https://22.nx.dev/docs) or [Nx v21](https://21.nx.dev/docs). --- ## Rollup Get started with [Nx with Rollup](/docs/technologies/build-tools/rollup/introduction), or browse the topics below. {% index_page_cards path="technologies/build-tools/rollup" /%} --- ## @nx/rollup Executors The @nx/rollup plugin provides various executors to help you create and configure rollup projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/rollup introduction](/docs/technologies/build-tools/rollup/introduction). ### `rollup` Packages a library for different web usages (ESM, CommonJS). ###### Including Dependencies To include dependencies in the output `package.json`, the dependencies must be installed as a **dependencies** in the root `package.json` ```json title="package.json" { "dependencies": { "some-dependency": "^1.0.0" } } ``` ###### Using `babelUpwardRootMode` Copying from the [Babel documentation](https://babeljs.io/docs/config-files#root-babelconfigjson-file): > [...] if you are running your Babel compilation process from within a subpackage, you need to tell Babel where to look for the config. There are a few ways to do that, but the recommended way is the "rootMode" option with "upward", which will make Babel search from the working directory upward looking for your babel.config.json file, and will use its location as the "root" value. Setting `babelUpwardRootMode` to `true` in your `project.json` will set `rootMode` option to `upward` in the Babel config. You may want the `upward` mode in a monorepo when projects must apply their individual `.babelrc` file. We recommend that you don't set it at all, so it will use the default to `false` as the `upward` mode brings additional complexity to the build process. ```json //... "my-app": { "targets": { "build": { "executor": "@nx/rollup:rollup", "options": { "babelUpwardRootMode": true, //... }, //... }, //... }, //... } ``` When `babelUpwardRootMode` is `true`, Babel will look for a root `babel.config.json` at the root of the workspace, which should look something like this to include all packages: ```json { "babelrcRoots": ["*"] } ``` Then for each package, you must have a `.babelrc` file that will be applied to that package. For example: ```json { "presets": ["@babel/preset-env", "@babel/preset-typescript"] } ``` All packages will use its own `.babelrc` file, thus you must ensure the right presets and plugins are set in each config file. This behavior can lead to build discrepancies between packages, so we recommend that you don't set `babelUpwardRootMode` at all. ```text ├── packages │ ├── a │ │ └── .babelrc │ └── b │ └── .babelrc └── babel.config.json ``` In workspace above, if `a` imports `b`, it will apply the config `packages/b/.babelrc` and not apply its own `packages/a/.babelrc` to `b`. Anything in `babel.config.json` will apply to all packages. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `main` | string [**required**] | The path to the entry file, relative to project. | | | `outputPath` | string [**required**] | The output path of the generated files. | | | `tsConfig` | string [**required**] | The path to tsconfig file. | | | `additionalEntryPoints` | array | Additional entry-points to add to exports field in the package.json file. | | | `allowJs` | boolean | Allow JavaScript files to be compiled. | `false` | | `assets` | array | List of static assets. | `[]` | | `babelUpwardRootMode` | boolean | Whether to set rootmode to upward. See https://babeljs.io/docs/en/options#rootmode | `false` | | `buildLibsFromSource` | boolean | Read buildable libraries from source instead of building them separately. | `true` | | `compiler` | string | Which compiler to use. | `"babel"` | | `deleteOutputPath` | boolean | Delete the output path before building. | `true` | | `external` | array | A list of external modules that will not be bundled (`react`, `react-dom`, etc.). Can also be set to `all` (bundle nothing) or `none` (bundle everything). | | | `extractCss` | boolean | string | CSS files will be extracted to the output folder. Alternatively custom filename can be provided (e.g. styles.css) | `true` | | `format` | array | List of module formats to output. Defaults to matching format from tsconfig (e.g. CJS for CommonJS, and ESM otherwise). | | | `generateExportsField` | boolean | Update the output package.json file's 'exports' field. This field is used by Node and bundles. | `false` | | `javascriptEnabled` | boolean | Sets `javascriptEnabled` option for less loader | `false` | | `outputFileName` | string | Name of the main output file. Defaults same basename as 'main' file. | | | `project` | string | The path to package.json file. | | | `rollupConfig` | string | Path to a function which takes a rollup config and returns an updated rollup config. | | | `skipTypeCheck` | boolean | Whether to skip TypeScript type checking. | `false` | | `skipTypeField` | boolean | Prevents 'type' field from being added to compiled package.json file. Use this if you are having an issue with this field. | `false` | | `sourceMap` | boolean | Output sourcemaps. | | | `watch` | boolean | Enable re-building when files change. | `false` | --- ## @nx/rollup Generators The @nx/rollup plugin provides various generators to help you create and configure rollup projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/rollup introduction](/docs/technologies/build-tools/rollup/introduction). ## `configuration` Add Rollup Configuration to a project. **Usage:** ```bash nx generate @nx/rollup:configuration [options] ``` **Aliases:** `rollup-project` **Arguments:** ```bash nx generate @nx/rollup:configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--buildTarget` | string | The build target to add. | `"build"` | | `--compiler` | string | The compiler to use to build source. | `"babel"` | | `--external` | array | A list of external modules that will not be bundled (`react`, `react-dom`, etc.). | | | `--format` | array | The format to build the library (esm or cjs). | `["esm"]` | | `--importPath` | string | The library name used to import it, like `@myorg/my-awesome-lib`. | | | `--main` | string | Path relative to the workspace root for the main entry file. Defaults to '/src/index.ts'. | | | `--rollupConfig` | string | Path relative to workspace root to a custom rollup file that takes a config object and returns an updated config. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--skipValidation` | boolean | Do not perform any validation on existing project. | `false` | | `--tsConfig` | string | Path relative to the workspace root for the tsconfig file to build with. Defaults to '/tsconfig.lib.json'. | | ## `convert-to-inferred` Convert existing Rollup project(s) using `@nx/rollup:rollup` executor to use `@nx/rollup/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target. **Usage:** ```bash nx generate @nx/rollup:convert-to-inferred [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/rollup:rollup` executor to use `@nx/rollup/plugin`. | | | `--skipFormat` | boolean | Whether to format files at the end of the migration. | `false` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/rollup: --help ``` --- ## Nx with Rollup [Rollup](https://rollupjs.org/) is a module bundler for JavaScript libraries and applications. In a Rollup monorepo, Nx builds and caches projects across your workspace and rebuilds only what a change affects. The Nx Plugin for Rollup contains executors and generators that support building applications with Rollup. ## Requirements The `@nx/rollup` plugin supports the following package versions. | Package | Supported Versions | | -------- | ------------------ | | `rollup` | ^3.0.0 \|\| ^4.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setting up @nx/rollup ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/rollup` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/rollup` by running the following command: ```shell nx add @nx/rollup ``` This will install the correct version of `@nx/rollup`. ## Configure task inference The `@nx/rollup/plugin` plugin looks for these Rollup configuration files: - `rollup.config.js` - `rollup.config.cjs` - `rollup.config.mjs` - `rollup.config.ts` - `rollup.config.cts` - `rollup.config.mts` A configuration file is associated with a project when its directory also contains a `package.json` or `project.json`. Nx creates a cached build task that runs `rollup -c`, with outputs based on your Rollup configuration, and depends on the same task in dependency projects. Configure the plugin in `nx.json`: ```json // nx.json { "plugins": [ { "plugin": "@nx/rollup/plugin", "options": { "buildTargetName": "build", "buildDepsTargetName": "build-deps", "watchDepsTargetName": "watch-deps" } } ] } ``` | Option | Type | Default | Description | | --------------------- | ------ | ------------ | --------------------------------------------------- | | `buildTargetName` | string | `build` | Name of the cached Rollup build task. | | `buildDepsTargetName` | string | `build-deps` | Name of the task that builds project dependencies. | | `watchDepsTargetName` | string | `watch-deps` | Name of the task that watches project dependencies. | Use `include` and `exclude` glob patterns on the plugin entry to scope inference. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/rollup/plugin`. ## Using @nx/rollup ### Generate a new project using Rollup You can generate a new library that uses Rollup with the following command: ```bash frame="none" nx g @nx/js:lib mylib --bundler=rollup ``` This will create a new library configured to use Rollup for building. ## Set up CI for your Rollup monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/rollup Migrations For an overview of the plugin and setup instructions, see the [@nx/rollup introduction](/docs/technologies/build-tools/rollup/introduction). The @nx/rollup plugin provides various migrations to help you migrate to newer versions of rollup projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.0.x ### `update-23-0-0-remove-use-legacy-typescript-plugin` **Version**: 23.0.0-beta.4 Remove deprecated useLegacyTypescriptPlugin option from @nx/rollup:rollup #### Remove the `useLegacyTypescriptPlugin` Option from the `@nx/rollup:rollup` Executor Removes the deprecated `useLegacyTypescriptPlugin` option from the `@nx/rollup:rollup` executor. The legacy TypeScript plugin support has been dropped; the option no longer has any effect. The migration removes it from project configuration, target defaults in `nx.json`, and `rollup.config.*` files that pass it to `withNx`. #### Sample Code Changes Remove `useLegacyTypescriptPlugin` from the `@nx/rollup:rollup` executor options in project configuration. ##### Before ```json title="libs/my-lib/project.json" {8} { "targets": { "build": { "executor": "@nx/rollup:rollup", "options": { "main": "libs/my-lib/src/index.ts", "outputPath": "dist/libs/my-lib", "useLegacyTypescriptPlugin": true } } } } ``` ##### After ```json title="libs/my-lib/project.json" { "targets": { "build": { "executor": "@nx/rollup:rollup", "options": { "main": "libs/my-lib/src/index.ts", "outputPath": "dist/libs/my-lib" } } } } ``` Remove `useLegacyTypescriptPlugin` from the `@nx/rollup:rollup` executor target defaults in `nx.json`. ##### Before ```json title="nx.json" {7} { "targetDefaults": { "@nx/rollup:rollup": { "options": { "outputPath": "dist/{projectRoot}", "tsConfig": "{projectRoot}/tsconfig.lib.json", "useLegacyTypescriptPlugin": true } } } } ``` ##### After ```json title="nx.json" { "targetDefaults": { "@nx/rollup:rollup": { "options": { "outputPath": "dist/{projectRoot}", "tsConfig": "{projectRoot}/tsconfig.lib.json" } } } } ``` Remove `useLegacyTypescriptPlugin` from `withNx` calls in `rollup.config.*` files. ##### Before ```js title="libs/my-lib/rollup.config.cjs" {7} const { withNx } = require('@nx/rollup/with-nx'); module.exports = withNx({ outputPath: '../../dist/libs/my-lib', main: './src/index.ts', tsConfig: './tsconfig.lib.json', format: ['cjs', 'esm'], useLegacyTypescriptPlugin: true, }); ``` ##### After ```js title="libs/my-lib/rollup.config.cjs" const { withNx } = require('@nx/rollup/with-nx'); module.exports = withNx({ outputPath: '../../dist/libs/my-lib', main: './src/index.ts', tsConfig: './tsconfig.lib.json', format: ['cjs', 'esm'], }); ``` ### `rewrite-rollup-internal-subpath-imports` **Version**: 23.0.0-beta.24 Rewrites `@nx/rollup/src/*` subpath imports now that the `./src/*` subpath is no longer exposed by `@nx/rollup`'s exports map. Named imports/exports of public symbols are routed to `@nx/rollup` and the rest to the new `@nx/rollup/internal` entry; `require`, dynamic `import` and `jest.mock` calls reference the whole module and are routed to `@nx/rollup/internal`. ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/rollup/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/rollup` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/rollup/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/rollup/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/rollup/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/rollup/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ### 23.0.0-package-updates **Version**: 23.0.0-beta.24 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `rollup` | `^4.14.0` | Added if not installed --- ## Rsbuild Get started with [Nx with Rsbuild](/docs/technologies/build-tools/rsbuild/introduction), or browse the topics below. {% index_page_cards path="technologies/build-tools/rsbuild" /%} --- ## @nx/rsbuild Generators The @nx/rsbuild plugin provides various generators to help you create and configure rsbuild projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/rsbuild introduction](/docs/technologies/build-tools/rsbuild/introduction). ## `configuration` Rsbuild configuration generator. **Usage:** ```bash nx generate @nx/rsbuild:configuration [options] ``` **Arguments:** ```bash nx generate @nx/rsbuild:configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--devServerPort` | number | The port for the dev server to listen on. | `4200` | | `--entry` | string | Path relative to the workspace root for the entry file. Defaults to '/src/index.ts'. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--target` | string | Target platform for the build, same as the Rsbuild output.target config option. | `"web"` | | `--tsConfig` | string | Path relative to the workspace root for the tsconfig file to build with. Defaults to '/tsconfig.app.json'. | | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/rsbuild: --help ``` --- ## Nx with Rsbuild [Rsbuild](https://rsbuild.dev/) is an Rspack-based build tool for web applications. In an Rsbuild monorepo, Nx lets you build, serve, and cache every project from a single workspace. The Nx Plugin for Rsbuild contains executors and generators that support building applications using Rsbuild, wiring each project into Nx. ## Requirements The `@nx/rsbuild` plugin supports the following package versions. | Package | Supported Versions | Default Installed | | --------------- | ---------------------- | ----------------- | | `@rsbuild/core` | `^1.0.0` \|\| `^2.0.0` | `2.0.6` | [Nx generators](/docs/features/generate-code) install the latest supported version automatically when scaffolding new projects. When `@rsbuild/core` is already installed in your workspace, generators detect the installed version and keep it in place rather than overwriting it. ## Setting up @nx/rsbuild ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/rsbuild` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/rsbuild` by running the following command: ```shell nx add @nx/rsbuild ``` This will install the correct version of `@nx/rsbuild`. ## Configure task inference The `@nx/rsbuild` plugin looks for these configuration files in directories that also contain a `package.json` or `project.json`: - `rsbuild.config.js` - `rsbuild.config.ts` - `rsbuild.config.mjs` - `rsbuild.config.mts` - `rsbuild.config.cjs` - `rsbuild.config.cts` Nx creates a cached `build` task using the output directory from the Rsbuild configuration, continuous `dev` and `preview` tasks, and an `inspect` task. It also creates a cached `typecheck` task when the project has a `tsconfig*.json` file. Configure the plugin in `nx.json`: ```json // nx.json { "plugins": [ { "plugin": "@nx/rsbuild", "options": { "buildTargetName": "build", "devTargetName": "dev", "previewTargetName": "preview", "inspectTargetName": "inspect", "typecheckTargetName": "typecheck", "buildDepsTargetName": "build-deps", "watchDepsTargetName": "watch-deps" } } ] } ``` | Option | Type | Default | Description | | --------------------- | ------ | ----------- | -------------------------------------------------- | | `buildTargetName` | string | `build` | Name of the cached production build task. | | `devTargetName` | string | `dev` | Name of the continuous development server task. | | `previewTargetName` | string | `preview` | Name of the continuous production preview task. | | `inspectTargetName` | string | `inspect` | Name of the Rsbuild configuration inspection task. | | `typecheckTargetName` | string | `typecheck` | Name of the cached TypeScript typecheck task. | | `buildDepsTargetName` | string | none | Name of an optional dependency build task. | | `watchDepsTargetName` | string | none | Name of an optional dependency watch task. | Use `include` and `exclude` glob patterns on the plugin entry to scope inference. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/rsbuild`. ## Using @nx/rsbuild ### Generate a new project using Rsbuild You can generate a new application that uses Rsbuild with the following command: ```bash frame="none" nx g @nx/react:app myapp --bundler=rsbuild ``` This will create a new application configured to use Rsbuild for building. ## Set up CI for your Rsbuild monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/rsbuild Migrations For an overview of the plugin and setup instructions, see the [@nx/rsbuild introduction](/docs/technologies/build-tools/rsbuild/introduction). The @nx/rsbuild plugin provides various migrations to help you migrate to newer versions of rsbuild projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.1.x ### `update-23-0-0-migrate-rsbuild-config-to-v2` **Version**: 23.1.0-beta.0 Rewrites rsbuild.config.{js,ts,mjs,cjs} files for @rsbuild/core@2: renames preview.setupMiddlewares to server.setupMiddlewares, drops the removed performance.removeMomentLocale option, moves source.alias/source.aliasStrategy to the resolve block, and updates server.proxy entries for http-proxy-middleware v4 (context to pathFilter, on* handlers to a unified on object). #### Requires | Name | Version | |------|---------| `@rsbuild/core` | `>=2.0.0` | ### `update-23-0-0-migrate-node-output-to-esm` **Version**: 23.1.0-beta.0 AI-assisted migration: make Node-target rsbuild builds runnable under @rsbuild/core@2, which emits ESM output by default. #### Requires | Name | Version | |------|---------| `@rsbuild/core` | `>=2.0.0` | ### `update-23-0-0-migrate-performance-options` **Version**: 23.1.0-beta.0 AI-assisted migration: adopt replacements for the performance.bundleAnalyze and performance.profile options removed in @rsbuild/core@2, and migrate the deprecated performance.chunkSplit option to splitChunks. #### Requires | Name | Version | |------|---------| `@rsbuild/core` | `>=2.0.0` | ### 23.0.0-rsbuild-v2-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@rsbuild/core` | `^2.0.7` | Updated only | `@rsbuild/plugin-react` | `^2.0.0` | Updated only | `@rsbuild/plugin-sass` | `^1.5.2` | Updated only ## 23.0.x ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/rsbuild` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/rsbuild` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/rsbuild` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/rsbuild'; ``` ##### After ```ts import { createNodes } from '@nx/rsbuild'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/rsbuild` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. --- ## Rspack Get started with [Nx with Rspack](/docs/technologies/build-tools/rspack/introduction), or browse the topics below. {% index_page_cards path="technologies/build-tools/rspack" /%} --- ## @nx/rspack Executors The @nx/rspack plugin provides various executors to help you create and configure rspack projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/rspack introduction](/docs/technologies/build-tools/rspack/introduction). ### `dev-server` Run @rspack/dev-server to serve a project. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `buildTarget` | string [**required**] | The build target for rspack. | | | `host` | string | Host to listen on. | `"localhost"` | | `mode` | string | Mode to run the server in. | | | `port` | number | The port to for the dev-server to listen on. | | | `proxyConfig` | string | Path to proxy configuration file. For more information, see https://rspack.rs/config/dev-server#devserverproxy. | | | `publicHost` | string | Public URL where the application will be served. | | | `ssl` | boolean | Serve using `HTTPS`. | `false` | | `sslCert` | string | SSL certificate to use for serving `HTTPS`. | | | `sslKey` | string | SSL key to use for serving `HTTPS`. | | ### `module-federation-dev-server` Serve a module federation application. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `buildTarget` | string | Target which builds the application. | | | `devRemotes` | array | List of Producer (remote) applications to run in development mode (i.e. using serve target). | | | `host` | string | Host to listen on. | `"localhost"` | | `isInitialHost` | boolean | Whether the Consumer (host) that is running this executor is the first in the project tree to do so. | `true` | | `parallel` | number | Max number of parallel processes for building static Producers (remotes). | | | `pathToManifestFile` | string | Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic Producer (remote) applications relative to the workspace root. | | | `port` | number | Port to listen on. | `4200` | | `publicHost` | string | Public URL where the application will be served. | | | `skipRemotes` | array | List of Producer (remote) applications to not automatically serve, either statically or in development mode. This will not remove the Producers (remotes) from the `module-federation.config` file, and therefore the application may still try to fetch these Producers (remotes). This option is useful if you have other means for serving the Producer (remote) application(s). **NOTE:** Producers (remotes) that are not in the workspace will be skipped automatically. | | | `ssl` | boolean | Serve using `HTTPS`. | `false` | | `sslCert` | string | SSL certificate to use for serving `HTTPS`. | | | `sslKey` | string | SSL key to use for serving `HTTPS`. | | | `static` | boolean | Whether to use a static file server instead of the rspack-dev-server. This should be used for Producer (remote) applications that are also Consumer (host) applications. | | | `staticRemotesPort` | number | The port at which to serve the file-server for the static Producers (remotes). | | ### `module-federation-ssr-dev-server` Serve a SSR Consumer (host) application along with its known Producers (remotes). #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `browserTarget` | string [**required**] | Target which builds the browser application. | | | `serverTarget` | string [**required**] | Target which builds the server application. | | | `devRemotes` | array | List of Producers (remote) applications to run in development mode (i.e. using serve target). | | | `host` | string | Host to listen on. | `"localhost"` | | `isInitialHost` | boolean | Whether the Consumer (host) that is running this executor is the first in the project tree to do so. | `true` | | `pathToManifestFile` | string | Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic Producers (remote) applications relative to the workspace root. | | | `port` | number | The port to be set on `process.env.PORT` for use in the server. | `4200` | | `publicHost` | string | Public URL where the application will be served. | | | `skipRemotes` | array | List of Producers (remote) applications to not automatically serve, either statically or in development mode. | | | `ssl` | boolean | Serve using HTTPS. | `false` | | `sslCert` | string | SSL certificate to use for serving HTTPS. | | | `sslKey` | string | SSL key to use for serving HTTPS. | | | `staticRemotesPort` | number | The port at which to serve the file-server for the static Producers (remotes). | | ### `module-federation-static-server` Serve a Consumer (host) application statically along with it's Producers (remotes). #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `serveTarget` | string [**required**] | | | ### `rspack` Run Rspack via an executor for a project. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `rspackConfig` | string [**required**] | The path to the rspack config file. | | | `additionalEntryPoints` | array | | | | `assets` | array | List of static application assets. | `[]` | | `baseHref` | string | Base url for the application being built. | | | `buildLibsFromSource` | boolean | Read buildable libraries from source instead of building them separately. If set to `false`, the `tsConfig` option must also be set to remap paths. | `true` | | `cache` | boolean | Configure rspack caching behavior. When not specified, defaults to `true` for Node targets in watch mode, and `undefined` otherwise. | | | `cssModuleHashFunction` | string | Hash algorithm used to generate CSS module class names. Defaults to `md5`, which FIPS-restricted OpenSSL rejects. Changing it renames generated class names. | | | `deployUrl` | string | URL where the application will be deployed. | | | `externalDependencies` | string | Dependencies to keep external to the bundle. (`all` (default), `none`, or an array of module names) | | | `extractCss` | boolean | Extract CSS into a `.css` file. | | | `extractLicenses` | boolean | Extract all licenses in a separate file. | `false` | | `fileReplacements` | array | Replace files with other files in the build. | `[]` | | `generateIndexHtml` | boolean | Generates `index.html` file to the output path. This can be turned off if using a webpack plugin to generate HTML such as `html-webpack-plugin`. | | | `generatePackageJson` | boolean | Generates a `package.json` and pruned lock file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated. | | | `index` | string | HTML File which will be contain the application. | | | `main` | string | The main entry file. | | | `memoryLimit` | number | Memory limit for type checking service process in `MB`. | | | `mode` | string | Mode to run the build in. | | | `namedChunks` | boolean | Names the produced bundles according to their entry file. | | | `optimization` | string | Enables optimization of the build output. | | | `outputFileName` | string | The main output entry file | | | `outputHashing` | string | Define the output filename cache-busting hashing mode. | | | `outputPath` | string | The output path for the bundle. | | | `poll` | number | Enable and define the file watching poll time period. | | | `polyfills` | string | Polyfills to load before application | | | `postcssConfig` | string | Set a path to PostCSS config that applies to the app and all libs. Defaults to `undefined`, which auto-detects postcss.config.js files in each `app`/`lib` directory. | | | `progress` | boolean | Log progress to the console while building. | | | `publicPath` | string | Set a public path for assets resources with absolute paths. | | | `rebaseRootRelative` | boolean | Whether to rebase absolute path for assets in postcss cli resources. | | | `runtimeChunk` | boolean | Use a separate bundle containing the runtime. | | | `runtimeDependencies` | array | Add runtime dependencies to the generated `package.json` file. Useful for Docker installs. Only works in conjunction with `generatePackageJson` option. | | | `scripts` | array | External Scripts which will be included before the main application entry. | | | `skipTypeChecking` | boolean | Skip the type checking. Default is `false`. | | | `sourceMap` | string | Output sourcemaps. Use 'hidden' for use with error reporting tools without generating sourcemap comment. | `true` | | `standardRspackConfigFunction` | boolean | Set to true if the rspack config exports a standard rspack function, not an Nx-specific one. See: https://rspack.dev/config/ | `false` | | `statsJson` | boolean | Generates a 'stats.json' file which can be analyzed using tools such as: 'webpack-bundle-analyzer' See: https://rspack.dev/guide/optimization/analysis | | | `stylePreprocessorOptions` | object | Options to pass to style preprocessors. | | | `styles` | array | External Styles which will be included with the application | | | `target` | string | The platform to target (e.g. web, node). | | | `transformers` | array | List of TypeScript Compiler Transfomers Plugins. | | | `tsConfig` | string | The tsconfig file to build the project. | | | `typeCheckOptions` | string | Configure type checking during the build. Set to `true` to enable with defaults (async: true). Set to `false` to disable type checking entirely. Use `{ async: true }` to run type checking in a separate process without blocking the build. Default is `{ async: true }`. | | | `vendorChunk` | boolean | Use a separate bundle containing only vendor libraries. | | ### `ssr-dev-server` Serve a SSR application using rspack. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `browserTarget` | string [**required**] | Target which builds the browser application. | | | `serverTarget` | string [**required**] | Target which builds the server application. | | | `browserTargetOptions` | object | Additional options to pass into the browser build target. | `{}` | | `port` | number | The port to be set on `process.env.PORT` for use in the server. | `4200` | | `serverTargetOptions` | object | Additional options to pass into the server build target. | `{}` | --- ## @nx/rspack Generators The @nx/rspack plugin provides various generators to help you create and configure rspack projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/rspack introduction](/docs/technologies/build-tools/rspack/introduction). ## `configuration` Rspack configuration generator. **Usage:** ```bash nx generate @nx/rspack:configuration [options] ``` **Arguments:** ```bash nx generate @nx/rspack:configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--buildTarget` | string | The build target of the project to be transformed to use the @nx/vite:build executor. | | | `--devServer` | boolean | Add a serve target to run a local rspack dev-server | `false` | | `--framework` | string | The framework used by the project. | | | `--main` | string | Path relative to the workspace root for the main entry file. Defaults to '/src/main.ts'. | | | `--newProject` | boolean | Is this a new project? | `false` | | `--rootProject` | boolean | | | | `--serveTarget` | string | The serve target of the project to be transformed to use the @nx/vite:dev-server and @nx/vite:preview-server executors. | | | `--style` | string | The style solution to use. | | | `--target` | string | Target platform for the build, same as the rspack config option. | `"web"` | | `--tsConfig` | string | Path relative to the workspace root for the tsconfig file to build with. Defaults to '/tsconfig.app.json'. | | ## `convert-config-to-rspack-plugin` Convert existing Rspack project(s) using `@nx/rspack:rspack` executor that uses `withNx` to use `NxAppRspackPlugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target. **Usage:** ```bash nx generate @nx/rspack:convert-config-to-rspack-plugin [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/rspack:rspack` executor and `withNx` plugin to use `NxAppRspackPlugin`. | | | `--skipFormat` | boolean | Whether to format files at the end of the migration. | `false` | ## `convert-to-inferred` Convert existing Rspack project(s) using `@nx/rspack:rspack` executor to use `@nx/rspack/plugin`. **Usage:** ```bash nx generate @nx/rspack:convert-to-inferred [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/rspack:rspack` executor to use `@nx/rspack/plugin`. If not provided, all projects using the `@nx/rspack:rspack` executor will be converted. | | | `--skipFormat` | boolean | Whether to format files. | `false` | ## `convert-webpack` Convert a Webpack project to Rspack. **Usage:** ```bash nx generate @nx/rspack:convert-webpack [options] ``` **Aliases:** `convert-to-rspack` **Arguments:** ```bash nx generate @nx/rspack:convert-webpack [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--skipFormat` | boolean | Skip formatting files. | `false` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/rspack: --help ``` --- ## Nx with Rspack Rspack is a Rust-based JavaScript bundler with a webpack-compatible API, built as a drop-in replacement for webpack with much faster builds. Teams with existing webpack configurations get that speedup without rewriting their setup. The `@nx/rspack` plugin adds caching, affected builds, and task inference so Rspack scales across a monorepo. ## What is Rspack? [Rspack](https://rspack.rs) is a bundler written in Rust that implements the webpack API. Per the Rspack team's [benchmarks](https://github.com/rstackjs/build-tools-performance) (as of Rspack 2), it builds and serves projects several times faster than webpack. In a monorepo those savings compound, and Nx layers caching and affected-only builds on top so unchanged projects don't build at all. ### Is Rspack a drop-in replacement for webpack? Mostly - existing loaders and the majority of webpack plugins run unchanged, so many configurations migrate with few or no edits. Plugins that depend on webpack internals may not work, so test complex setups when you migrate. ## Requirements The `@nx/rspack` plugin supports Rspack 1 and 2 (as of Nx v23). | Package | Supported Versions | Default Installed | | -------------- | ---------------------- | ----------------- | | `@rspack/core` | `^1.0.0` \|\| `^2.0.0` | `2.0.4` | [Nx generators](/docs/features/generate-code) install the latest supported version automatically when scaffolding new projects. When `@rspack/core` is already installed in your workspace, generators detect the installed version and keep it in place rather than overwriting it. ## Setting up @nx/rspack ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/rspack` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/rspack` by running the following command: ```shell nx add @nx/rspack ``` This will install the correct version of `@nx/rspack`. ### How @nx/rspack infers tasks The `@nx/rspack` plugin will create a task for any project that has a Rspack configuration file present. Any of the following files will be recognized as a Rspack configuration file: - `rspack.config.js` - `rspack.config.ts` - `rspack.config.mjs` - `rspack.config.mts` - `rspack.config.cjs` - `rspack.config.cts` ### View inferred tasks To view inferred tasks for a project, open the [project details view](/docs/concepts/mental-model#inferred-tasks) in Nx Console or run `nx show project my-project --web` in the command line. ### @nx/rspack configuration The `@nx/rspack/plugin` is configured in the `plugins` array in `nx.json`. ```json // nx.json { "plugins": [ { "plugin": "@nx/rspack/plugin", "options": { "buildTargetName": "build", "previewTargetName": "preview", "serveTargetName": "serve", "serveStaticTargetName": "serve-static", "buildDepsTargetName": "build-deps", "watchDepsTargetName": "watch-deps" } } ] } ``` | Option | Default | Inferred behavior | | ----------------------- | -------------- | ----------------------------------------------------------- | | `buildTargetName` | `build` | Runs and caches `rspack build` with its configured outputs. | | `serveTargetName` | `serve` | Runs `rspack serve` continuously in development mode. | | `previewTargetName` | `preview` | Runs `rspack serve` continuously in production mode. | | `serveStaticTargetName` | `serve-static` | Serves the build output continuously after the build task. | | `buildDepsTargetName` | none | Name of an optional dependency build task. | | `watchDepsTargetName` | none | Name of an optional dependency watch task. | The build task depends on builds of project dependencies. When TypeScript project references are enabled, the tasks also run the TypeScript sync generator. Use `include` and `exclude` glob patterns on the plugin entry to scope inference. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/rspack/plugin`. ## Using @nx/rspack ### Generate a new project using Rspack You can generate a [React](/docs/technologies/react/introduction) application that uses Rspack. The [`@nx/react:app`](/docs/technologies/react/generators#application) generator accepts the `bundler` option, where you can pass `rspack`. This will generate a new application configured to use Rspack, and it will also install all the necessary dependencies, including the `@nx/rspack` plugin. To generate a React application using Rspack, run the following: ```bash frame="none" nx g @nx/react:app my-app --bundler=rspack ``` ### Modify an existing React project to use Rspack You can use the `@nx/rspack:configuration` generator to change your React project to use Rspack. This generator will modify your project's configuration to use Rspack, and it will also install all the necessary dependencies, including the `@nx/rspack` plugin. You can read more about this generator on the [`@nx/rspack:configuration`](/docs/technologies/build-tools/rspack/generators#configuration) generator page. ## Why use Rspack in a monorepo? Rspack speeds up each build, and Nx cuts down how many builds run. In a monorepo with many Rspack projects: - [Caching](/docs/features/cache-task-results) skips builds whose inputs haven't changed, locally and in CI. - [`nx affected`](/docs/features/ci-features/affected) rebuilds and retests only the projects a change touches. - Because the API is webpack-compatible, you can migrate webpack projects to Rspack one at a time instead of converting every configuration in the repo at once. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/rspack Migrations For an overview of the plugin and setup instructions, see the [@nx/rspack introduction](/docs/technologies/build-tools/rspack/introduction). The @nx/rspack plugin provides various migrations to help you migrate to newer versions of rspack projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.1.x ### `update-23-0-0-migrate-rspack-config-to-v2` **Version**: 23.1.0-beta.0 Rewrites rspack.config.{js,ts,mjs,cjs} files for @rspack/core@2: replaces output.libraryTarget with output.library.type and drops experiments.css: false (no-op on v2). #### Requires | Name | Version | |------|---------| `@rspack/core` | `>=2.0.0` | ### `update-23-0-0-rewrite-experiments-css-to-module-rules` **Version**: 23.1.0-beta.0 AI-assisted migration: rewrite experiments.css: true configs for @rspack/core@2 by injecting a CSS rule into module.rules. #### Requires | Name | Version | |------|---------| `@rspack/core` | `>=2.0.0` | ### 23.0.0-rspack-v2-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@rspack/core` | `^2.0.4` | Updated only | `@rspack/dev-server` | `^2.0.1` | Updated only | `@rspack/plugin-react-refresh` | `^2.0.0` | Updated only ### 23.0.0-module-federation-rspack-v2-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/enhanced` | `^2.4.0` | Updated only | `@module-federation/sdk` | `^2.4.0` | Updated only | `@module-federation/runtime` | `^2.4.0` | Updated only | `@module-federation/node` | `^2.7.42` | Updated only ## 23.0.x ### `update-23-0-0-add-svgr-to-rspack-config` **Version**: 23.0.0-beta.9 Updates rspack configs using React to use the new withSvgr composable function instead of the svgr option in withReact or NxReactRspackPlugin. #### Replace the `svgr` Option with the `withSvgr` Composable in Rspack Configs Updates rspack configs that use React to use a new `withSvgr` composable function instead of passing an `svgr` option to `withReact` or `NxReactRspackPlugin`. The option was removed; SVG handling is now consolidated into the images asset rule by default. The migration inlines a `withSvgr` helper into each affected config so existing SVGR-as-React-component behavior is preserved. #### Sample Code Changes Replace the `svgr` option on `withReact` with a `withSvgr(...)` call composed alongside it. ##### Before ```js title="apps/myapp/rspack.config.js" {5} const { composePlugins, withNx, withReact } = require('@nx/rspack'); module.exports = composePlugins( withNx(), withReact({ svgr: { svgo: false, titleProp: true, ref: true } }) ); ``` ##### After ```js title="apps/myapp/rspack.config.js" const { composePlugins, withNx, withReact } = require('@nx/rspack'); // SVGR support function (migrated from svgr option in withReact/NxReactRspackPlugin) function withSvgr(svgrOptions = {}) { const defaultOptions = { svgo: false, titleProp: true, ref: true }; const options = { ...defaultOptions, ...svgrOptions }; return function configure(config) { const svgLoaderIdx = config.module.rules.findIndex( (rule) => typeof rule === 'object' && typeof rule.test !== 'undefined' && rule.test.toString().includes('svg') ); if (svgLoaderIdx !== -1) { config.module.rules.splice(svgLoaderIdx, 1); } config.module.rules.push( { test: /\.svg$/i, type: 'asset', resourceQuery: /url/ }, { test: /\.svg$/i, issuer: /\.[jt]sx?$/, resourceQuery: { not: [/url/] }, use: [{ loader: '@svgr/webpack', options }], } ); return config; }; } module.exports = composePlugins( withNx(), withReact(), withSvgr({ svgo: false, titleProp: true, ref: true }) ); ``` For the `NxReactRspackPlugin` style, the `svgr` option is removed from the plugin call and the entire `module.exports` is wrapped with a `withSvgr(...)` call that registers the loader rules directly on the compiler. ##### Before ```js title="apps/myapp/rspack.config.js" {5} const { NxReactRspackPlugin } = require('@nx/rspack'); module.exports = { plugins: [new NxReactRspackPlugin({ svgr: true, main: './src/main.tsx' })], }; ``` ##### After ```js title="apps/myapp/rspack.config.js" const { NxReactRspackPlugin } = require('@nx/rspack'); // SVGR support function (migrated from svgr option in withReact/NxReactRspackPlugin) function withSvgr(svgrOptions = {}) { /* same helper as above, compiler.options.module.rules variant */ } module.exports = withSvgr()({ plugins: [new NxReactRspackPlugin({ main: './src/main.tsx' })], }); ``` ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/rspack/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/rspack` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/rspack/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/rspack/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/rspack/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/rspack/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ## 22.2.x ### 22.2.0-module-federation-enhanced-package-updates **Version**: 22.2.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/enhanced` | `^0.21.2` | Updated only ### 22.2.0-module-federation-sdk-package-updates **Version**: 22.2.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/sdk` | `^0.21.2` | Updated only ### 22.2.0-module-federation-runtime-package-updates **Version**: 22.2.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/runtime` | `^0.21.2` | Updated only ### 22.2.0-module-federation-node-package-updates **Version**: 22.2.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/node` | `^2.7.21` | Updated only ## 22.0.x ### `remove-deprecated-rspack-options` **Version**: 22.0.0-beta.1 Remove deprecated deleteOutputPath and sassImplementation options from rspack configurations. ## 21.5.x ### 21.5.0-package-updates **Version**: 21.5.0-beta.2 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@rspack/core` | `^1.5.0` | Updated only | `@rspack/dev-server` | `^1.1.4` | Updated only ## 21.4.x ### 21.4.0-package-updates **Version**: 21.4.0-beta.8 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `http-proxy-middleware` | `^3.0.5` | Updated only ## 21.3.x ### 21.3.0-module-federation-enhanced-package-updates **Version**: 21.3.0-beta.8 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/enhanced` | `^0.17.0` | Updated only ### 21.3.0-module-federation-node-package-updates **Version**: 21.3.0-beta.8 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/node` | `^2.7.9` | Updated only ## 21.0.x ### 21.0.1-package-updates **Version**: 21.0.1-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@rspack/core` | `^1.3.8` | Updated only | `@rspack/dev-server` | `^1.1.1` | Updated only --- ## Vite Get started with [Nx with Vite](/docs/technologies/build-tools/vite/introduction), or browse the topics below. {% index_page_cards path="technologies/build-tools/vite" /%} --- ## @nx/vite Executors The @nx/vite plugin provides various executors to help you create and configure vite projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/vite introduction](/docs/technologies/build-tools/vite/introduction). ### `build` Builds a Vite application for production. `project.json`: ```json //... "my-app": { "targets": { //... "build": { "executor": "@nx/vite:build", //... //... "options": { "outputPath": "dist/apps/my-app" }, //... } }, } } ``` ```bash nx serve my-app ``` ### Examples ###### Set a custom path for vite.config.ts Nx will automatically look in the root of your application for a `vite.config.ts` (or a `vite.config.js`) file. If you want to use a different path, you can set it in your `project.json` file, in the `build` target options: ```json //... "my-app": { "targets": { //... "build": { "executor": "@nx/vite:build", //... "options": { "outputPath": "dist/apps/my-app", "configFile": "apps/my-app/vite.config.other-path.ts" }, "configurations": { ... } }, } } ``` or even ```json //... "my-app": { "targets": { //... "build": { "executor": "@nx/vite:build", //... "options": { "outputPath": "dist/apps/my-app", "configFile": "vite.config.base.ts" }, "configurations": { ... } }, } } ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `buildLibsFromSource` | boolean | Read buildable libraries from source instead of building them separately. | `true` | | `configFile` | string | The name of the Vite configuration file. | | | `generatePackageJson` | boolean | Generate a package.json for the build output. | | | `includeDevDependenciesInPackageJson` | boolean | Include devDependencies in the generated package.json. | | | `outputPath` | string | The output path of the generated files. | | | `skipOverrides` | boolean | Do not add a `overrides` and `resolutions` entries to the generated package.json file. Only works in conjunction with `generatePackageJson` option. | | | `skipPackageManager` | boolean | Do not add a `packageManager` entry to the generated package.json file. Only works in conjunction with `generatePackageJson` option. | | | `skipTypeCheck` | boolean | Skip type-checking via TypeScript. Skipping type-checking speeds up the build but type errors are not caught. | `false` | | `tsConfig` | string | The path to custom tsconfig file for type-checking when skipTypeCheck is false. Required when tsconfig file is not at the projectRoot level. | | | `useEnvironmentsApi` | boolean | Use the new Environments API for building multiple environments at once. Only works with Vite 6.0.0 or higher. | `false` | | `watch` | string | Enable re-building when files change. | `false` | ### `dev-server` Starts a dev server using Vite. `project.json`: ```json //... "my-app": { "targets": { //... "serve": { "executor": "@nx/vite:dev-server", "defaultConfiguration": "development", "options": { "buildTarget": "my-app:build", }, "configurations": { ... } }, } } ``` ```bash nx serve my-app ``` ### Examples ###### Set up a custom port You can always set the port in your `vite.config.ts` file. However, you can also set it directly in your `project.json` file, in the `serve` target options: ```json //... "my-app": { "targets": { //... "serve": { "executor": "@nx/vite:dev-server", "defaultConfiguration": "development", "options": { "buildTarget": "my-app:build", "port": 4200, }, "configurations": { ... } }, } } ``` ###### Specify a proxyConfig You can specify a proxy config by pointing to the path of your proxy configuration file: ```json //... "my-app": { "targets": { //... "serve": { "executor": "@nx/vite:dev-server", "defaultConfiguration": "development", "options": { "buildTarget": "my-app:build", "proxyConfig": "apps/my-app/proxy.conf.json" }, "configurations": { ... } }, } } ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `buildTarget` | string [**required**] | Target which builds the application. Only used to retrieve the configuration as the dev-server does not build the code. | | | `buildLibsFromSource` | boolean | Read buildable libraries from source instead of building them separately. | `true` | | `proxyConfig` | string | Path to the proxy configuration file. | | ### `preview-server` Preview Server for Vite. `project.json`: ```json //... "my-app": { "targets": { //... "preview": { "executor": "@nx/vite:preview-server", "defaultConfiguration": "development", "options": { "buildTarget": "my-app:build", }, "configurations": { ... } }, } } ``` ```bash nx preview my-app ``` ### Examples ###### Set up a custom port You can always set the port in your `vite.config.ts` file. However, you can also set it directly in your `project.json` file, in the `preview` target options: ```json //... "my-app": { "targets": { //... "preview": { "executor": "@nx/vite:preview-server", "defaultConfiguration": "development", "options": { "buildTarget": "my-app:build", "port": 4200, }, "configurations": { ... } }, } } ``` ###### Specify a proxyConfig You can specify a proxy config by pointing to the path of your proxy configuration file: ```json //... "my-app": { "targets": { //... "preview": { "executor": "@nx/vite:preview-server", "defaultConfiguration": "development", "options": { "buildTarget": "my-app:build", "proxyConfig": "apps/my-app/proxy.conf.json" }, "configurations": { ... } }, } } ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `buildTarget` | string [**required**] | Target which builds the application. | | | `proxyConfig` | string | Path to the proxy configuration file. | | | `staticFilePath` | string | Path where the build artifacts are located. If not provided then it will be infered from the buildTarget executor options as outputPath | | | `watch` | boolean | Enable re-building when files change. If not specified, watch mode will be enabled by default. | `true` | --- ## @nx/vite Generators The @nx/vite plugin provides various generators to help you create and configure vite projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/vite introduction](/docs/technologies/build-tools/vite/introduction). ## `configuration` Configure a project to use Vite. This generator is used for converting an existing React or Web project to use [Vite](https://vite.dev/). It will create a `vite.config.ts` file at the root of your project with the correct settings, or if there's already a `vite.config.ts` file, it will modify it to include the correct settings. :::caution[Your code will be modified!] This generator will modify your code, so make sure to commit your changes before running it. ::: ```bash nx g @nx/vite:configuration ``` When running this generator, you will be prompted to provide the following: - The `project`, as the name of the project you want to generate the configuration for. - The `uiFramework` you want to use. Supported values are: `react` and `none`. You must provide a `project` and a `uiFramework` for the generator to work. You may also pass the `includeVitest` flag. This will also configure your project for testing with [Vitest](https://vitest.dev/), by adding the `test` configuration in your `vite.config.ts` file. :::note[Vitest-only configuration] If you only need to add Vitest to a project (without Vite build configuration), use the `@nx/vitest:configuration` generator instead. See the [@nx/vitest package](/packages/vitest) for more information. ::: ### How to use If you have an existing project that does not use Vite, you may want to convert it to use Vite. This can be a `webpack` project, a buildable JS library that uses the `@nx/js:babel`, the `@nx/js:swc` or the `@nx/rollup:rollup` executor, or even a non-buildable library. By default, the `@nx/vite:configuration` generator will search your project to find the relevant configuration (either a `webpack.config.ts` file for example, or the `@nx/js` executors). If it determines that your project can be converted, then Nx will generate the configuration for you. If it cannot determine that your project can be converted, it will ask you if you want to convert it anyway or throw an error if it determines that it cannot be converted. You can then test on your own if the result works or not, and modify the configuration as needed. It's suggested that you commit your changes before running the generator, so you can revert the changes if needed. ### Projects that can be converted to use the `@nx/vite` executors Usually, React and Web projects generated with the `@nx/react` and the `@nx/web` generators can be converted to use the `@nx/vite` executors without any issues. The list of executors for building, testing and serving that can be converted to use the `@nx/vite` executors is: #### Supported `build` executors - `@nxext/vite:build` - `@nx/js:babel` - `@nx/js:swc` - `@nx/rollup:rollup` - `@nx/webpack:webpack` - `@nx/web:rollup` #### Unsupported executors - `@nx/angular:ng-packagr-lite` - `@nx/angular:package` - `@nx/angular:webpack-browser` - `@angular-devkit/build-angular:browser` - `@angular-devkit/build-angular:dev-server` - `@nx/esbuild:esbuild` - `@nx/react-native:start` - `@nx/next:build` - `@nx/next:server` - `@nx/js:tsc` - any executor _not_ listed in the lists of "supported executors" - any project that does _not_ have a target for building, serving or testing We **cannot** guarantee that projects using unsupported executors - _or any executor that is NOT listed in the list of "supported executors"_ - for either building, testing or serving will work correctly when converted to use Vite. You can read more in the [Vite package overview page](/nx-api/vite). ### Examples #### Convert a React app to use Vite ```bash nx g @nx/vite:configuration --project=my-react-app --uiFramework=react --includeVitest ``` This will configure the `my-react-app` project to use Vite. #### Convert a Web app to use Vite ```bash nx g @nx/vite:configuration --project=my-web-app --uiFramework=none --includeVitest ``` This will configure the `my-web-app` project to use Vite. **Usage:** ```bash nx generate @nx/vite:configuration [options] ``` **Aliases:** `config` **Arguments:** ```bash nx generate @nx/vite:configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--compiler` | string | Compiler to use for Vite when UI Framework is React. | `"babel"` | | `--includeLib` | boolean | Add a library build option and skip the server option. | | | `--includeVitest` | boolean | Use vitest for the test suite. | | | `--newProject` | boolean | Is this a new project? | `false` | | `--port` | number | The port to use for the development server | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--testEnvironment` | string | The vitest environment to use. See https://vitest.dev/config/#environment. | `"jsdom"` | | `--uiFramework` | string | UI Framework to use for Vite. | `"none"` | ## `convert-to-inferred` Convert existing Vite project(s) using `@nx/vite:*` executors to use `@nx/vite/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target. **Usage:** ```bash nx generate @nx/vite:convert-to-inferred [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/vite:*` executors to use `@nx/vite/plugin`. | | | `--skipFormat` | boolean | Whether to format files at the end of the migration. | `false` | ## `setup-paths-plugin` Updates vite config files to enable support for workspace libraries via the nxViteTsPaths plugin. **Usage:** ```bash nx generate @nx/vite:setup-paths-plugin [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--skipFormat` | boolean | Skip formatting files. | `false` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/vite: --help ``` --- ## Guides {% index_page_cards path="technologies/build-tools/vite/guides" /%} --- ## Nx with Vite [Vite](https://vite.dev/) is a fast build tool and dev server for modern web apps. In a Vite monorepo, Nx runs and caches Vite tasks across every project so you only rebuild what changed. The `@nx/vite` plugin adds [inferred tasks](#configuration) and [project configuration generators](#setup). {% aside type="note" title="Vitest is now in @nx/vitest" %} Starting with Nx v22, Vitest support has moved to the dedicated [`@nx/vitest` plugin](/docs/technologies/test-tools/vitest/introduction). In Nx v21 and prior, `@nx/vite` contained what is now in `@nx/vitest`. If you are upgrading, see the [migration guide](/docs/kb/migrating-from-nx-vite). {% /aside %} You can use Vite with Nx without the plugin and still get [task caching](/docs/features/cache-task-results), [task orchestration](/docs/features/run-tasks), and the [project graph](/docs/features/explore-graph). ## Requirements The `@nx/vite` plugin supports the following package versions. | Package | Supported Versions | | ------- | ------------------------------------------ | | `vite` | ^5.0.0 \|\| ^6.0.0 \|\| ^7.0.0 \|\| ^8.0.0 | For supported `vitest` versions, see the [`@nx/vitest` plugin requirements](/docs/technologies/test-tools/vitest/introduction#requirements). [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setup ### Add to an existing workspace {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/vite` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). Using `nx add` helps ensure the correct version is installed and configured. {% /aside %} Install the plugin: ```shell nx add @nx/vite ``` Configure an existing project to use Vite: ```shell nx g @nx/vite:configuration --project=my-app ``` Verify inferred tasks in the project details view: ```shell nx show project my-app --web ``` Replace `my-app` with your project name. ### Create a new workspace Use a framework preset and keep the setup minimal: {% tabs syncKey="framework" %} {% tabitem label="React" %} ```shell npx create-nx-workspace@latest --preset=react-standalone --bundler=vite ``` {% /tabitem %} {% tabitem label="Web" %} ```shell npx create-nx-workspace@latest --preset=web-components --bundler=vite ``` {% /tabitem %} {% /tabs %} For the full framework workflows, see the [React introduction](/docs/technologies/react/introduction) or [Web app reference](/docs/reference/web). ## Local development - `nx dev my-app` starts the Vite dev server for the project (same as the deprecated `serve` target). - `nx build my-app` creates a production build in `dist/` (controlled by `build.outDir` in `vite.config.*`) and registers the outputs for Nx caching. - `nx preview my-app` serves the production build using Vite preview (runs after `build`). - `nx serve-static my-app` serves the built assets from `dist/` for quick smoke tests. - `nx typecheck my-app` runs TypeScript typechecking when a `tsconfig*.json` exists in the project. Replace `my-app` with your project name. ## Configuration ### Configure task inference The `@nx/vite` plugin infers tasks by reading Vite config files. Any of the following config files will be picked up: - `vite.config.js` - `vite.config.ts` - `vite.config.mjs` - `vite.config.mts` - `vite.config.cjs` - `vite.config.cts` The configuration directory must also contain a `package.json` or `project.json`. Build tasks are only created when the project is buildable. A project is treated as buildable when any of the following are true: - `build.lib` is set in `vite.config.*` - `build.rollupOptions.input` or `build.rolldownOptions.input` is set (Vite 8 uses `rolldownOptions`) - `builder.buildApp` is set - an `index.html` file exists at the project root Dev, preview, and serve-static tasks are created for buildable projects. If you are in library mode (`build.lib`), these tasks are skipped unless you explicitly configure a dev server (e.g. `server.host` or `server.port`). Typecheck tasks are created when a `tsconfig*.json` file exists in the project. ### Plugin options Configure `@nx/vite/plugin` in the `plugins` array in `nx.json`: ```json // nx.json { "plugins": [ { "plugin": "@nx/vite/plugin", "options": { "buildTargetName": "build", "previewTargetName": "preview", "serveTargetName": "serve", "devTargetName": "dev", "serveStaticTargetName": "serve-static", "typecheckTargetName": "typecheck", "compiler": "tsc", "buildDepsTargetName": "build-deps", "watchDepsTargetName": "watch-deps" } } ] } ``` `serveTargetName` is deprecated; use `devTargetName` instead. `buildDepsTargetName` and `watchDepsTargetName` create tasks that build or watch project dependencies. | Option | Description | Default | | ----------------------- | -------------------------------------------------- | -------------- | | `buildTargetName` | Name of the Vite build task | `build` | | `previewTargetName` | Name of the Vite preview task | `preview` | | `serveTargetName` | Name of the deprecated Vite serve task | `serve` | | `devTargetName` | Name of the Vite dev server task | `dev` | | `serveStaticTargetName` | Name of the static file server task | `serve-static` | | `typecheckTargetName` | Name of the typecheck task | `typecheck` | | `compiler` | Typecheck compiler: `tsc`, `tsgo`, or `vue-tsc` | auto-detected | | `buildDepsTargetName` | Name of the build-deps task for dependency builds | none | | `watchDepsTargetName` | Name of the watch-deps task for dependency watches | none | The build and typecheck tasks are cached, with build outputs based on your Vite configuration. The dev, preview, and static-server tasks are continuous. When `compiler` is omitted, Nx uses `vue-tsc` when it detects a Vite Vue plugin and `tsc` otherwise. ### Disable or scope inference To stop inference for a specific project, exclude its config file with `include`/`exclude` filters in `nx.json`: ```json // nx.json { "plugins": [ { "plugin": "@nx/vite/plugin", "exclude": ["apps/legacy/**"] } ] } ``` To avoid specific targets, adjust the config files that trigger them. For example, remove `tsconfig*.json` to stop `typecheck`, or use library mode (`build.lib`) to skip `dev`, `preview`, and `serve-static` unless you set dev server options. ### View inferred tasks To see what tasks Nx inferred for a project, open the [project details view](/docs/concepts/mental-model#inferred-tasks) in Nx Console or run: ```shell nx show project my-app --web ``` A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter for these tasks must use the exact identifier `@nx/vite/plugin`. ## Set up CI for your Vite monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). ## What's next {% cardgrid %} {% linkcard title="Configure Vite" description="Set up Vite config, Nx-specific plugins, and recommended defaults." href="/docs/kb/configure-vite" /%} {% linkcard title="React with Nx" description="Learn the React workflow in an Nx monorepo and how Vite fits." href="/docs/technologies/react/introduction" /%} {% linkcard title="Vue with Nx" description="See the Vue workflow in Nx and where Vite is used." href="/docs/technologies/vue/introduction" /%} {% linkcard title="Vitest with Nx" description="Add and run Vitest tests, including CI-friendly patterns." href="/docs/technologies/test-tools/vitest/introduction" /%} {% linkcard title="Vite Generators" description="Browse all Vite generator options for configuration and testing." href="/docs/technologies/build-tools/vite/generators" /%} {% linkcard title="Vite Executors" description="Browse all Vite executor options for dev and build." href="/docs/technologies/build-tools/vite/executors" /%} {% /cardgrid %} --- ## @nx/vite Migrations For an overview of the plugin and setup instructions, see the [@nx/vite introduction](/docs/technologies/build-tools/vite/introduction). The @nx/vite plugin provides various migrations to help you migrate to newer versions of vite projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.0.x ### `ensure-vitest-package-migration-23` **Version**: 23.0.0-beta.10 Safety net: ensure any remaining @nx/vite:test executor usages are swapped to @nx/vitest:test and @nx/vitest is installed. # ensure-vitest-package-migration (Nx v23) Migration that runs automatically when upgrading to Nx 23. It is a safety net for workspaces that still have `@nx/vite` vitest artifacts in place after the optional v22 migration. ## What it does 1. **Installs `@nx/vitest`**: added to `devDependencies` when vitest is detected in the workspace (skipped when already present or when the workspace does not use vitest at all). 2. **Swaps `@nx/vite:test` to `@nx/vitest:test`**: updates every `project.json` target and every `targetDefaults` entry that still references the removed executor. 3. **Splits `@nx/vite/plugin` registrations**: vitest-related options (`testTargetName`, `ciTargetName`, `ciGroupName`) are extracted from each `@nx/vite/plugin` entry and moved to a new `@nx/vitest` plugin entry. The vite entry is updated to retain only build/serve/preview options. 4. **Registers `@nx/vitest` plugin**: for workspaces that used `@nx/vite/plugin` in its default configuration (no vitest options), a matching `@nx/vitest` plugin entry is added automatically so vitest targets continue to be inferred. ## Why this migration exists The vitest support that previously lived in `@nx/vite` (the `@nx/vite:test` executor, the `@nx/vite:vitest` generator, and the vitest target inference in `@nx/vite/plugin`) was removed in Nx 23 and is now exclusively provided by the `@nx/vitest` package. See the [Migrating from @nx/vite guide](/technologies/test-tools/vitest/guides/migrating-from-nx-vite). ## No action required Run `nx migrate` and this migration will handle the conversion automatically. To verify the result, check `nx.json`, your `project.json` files, and `package.json` after running migrations. ### `rename-rollup-options-to-rolldown-options` **Version**: 23.0.0-beta.10 Rename `rollupOptions` to `rolldownOptions` in vite config files (top-level and inside `environments`). Vite 8 replaced Rollup with Rolldown. #### Requires | Name | Version | |------|---------| `vite` | `>=8.0.0` | #### Rename `rollupOptions` to `rolldownOptions` in Vite Config Files Renames the `rollupOptions` property to `rolldownOptions` inside `vite.config.{js,ts,mjs,mts,cjs,cts}` files. [Vite 8 replaced Rollup with Rolldown](https://vite.dev/blog/announcing-vite8) as its production bundler; `rollupOptions` is accepted as a deprecated alias but logs a warning and may produce precedence surprises when both keys are present. The migration covers top-level usage as well as nested `environments..build.rollupOptions`. The migration only touches files matching `vite.*config*.{js,ts,mjs,mts,cjs,cts}`. Helper modules imported by your config and `rollupOptions` declared elsewhere need to be renamed by hand. #### Sample Code Changes Top-level rename inside `build`. ##### Before ```ts title="apps/myapp/vite.config.ts" {3} export default defineConfig({ build: { rollupOptions: { external: ['react'], }, }, }); ``` ##### After ```ts title="apps/myapp/vite.config.ts" export default defineConfig({ build: { rolldownOptions: { external: ['react'], }, }, }); ``` The same rename applies inside `environments..build`. ##### Before ```ts title="apps/myapp/vite.config.ts" {5} export default defineConfig({ environments: { ssr: { build: { rollupOptions: { external: ['fs'] }, }, }, }, }); ``` ##### After ```ts title="apps/myapp/vite.config.ts" export default defineConfig({ environments: { ssr: { build: { rolldownOptions: { external: ['fs'] }, }, }, }, }); ``` > **Note**: Rolldown is largely Rollup-compatible but a handful of options have different semantics. The most common one to check after this migration: `output.manualChunks` only accepts a function in Rolldown (the object-of-globs form is invalid). See `tools/ai-migrations/MIGRATE_VITE_8.md` (created by a sibling migration) for the full Vite 8 upgrade checklist. ### `create-ai-instructions-for-vite-8` **Version**: 23.0.0-beta.10 Create AI Instructions to help migrate users workspaces past breaking changes for Vite 8. #### Requires | Name | Version | |------|---------| `vite` | `>=8.0.0` | ### `migrate-to-vitest-3` **Version**: 23.0.0-beta.22 Migrate workspaces past breaking changes for Vitest 3. #### Requires | Name | Version | |------|---------| `vitest` | `>=3.0.0` | ### `migrate-to-vitest-4` **Version**: 23.0.0-beta.22 Migrate workspaces past breaking changes for Vitest 4. #### Requires | Name | Version | |------|---------| `vitest` | `>=4.0.0` | ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/vite/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/vite` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/vite/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/vite/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/vite/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/vite/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ### 23.0.0-package-updates **Version**: 23.0.0-beta.10 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `vite` | `^8.0.0` | Updated only | `@vitejs/plugin-react` | `^6.0.0` | Updated only ### 23.0.0-vitest-v3-floor-package-updates **Version**: 23.0.0-beta.22 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `vitest` | `^3.0.0` | Updated only | `@vitest/coverage-v8` | `^3.0.0` | Updated only | `@vitest/coverage-istanbul` | `^3.0.0` | Updated only | `@vitest/ui` | `^3.0.0` | Updated only ### 23.0.0-vitest-v4-package-updates **Version**: 23.0.0-beta.22 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `vitest` | `^4.0.0` | Updated only | `@vitest/coverage-v8` | `^4.0.0` | Updated only | `@vitest/coverage-istanbul` | `^4.0.0` | Updated only | `@vitest/ui` | `^4.0.0` | Updated only ## 22.2.x ### `update-22-2-0` **Version**: 22.2.0-beta.1 Create AI Instructions to help migrate users workspaces past breaking changes for Vitest 4. #### Requires | Name | Version | |------|---------| `vitest` | `>=4.0.0` | ### `migrate-vitest-to-vitest-package` **Version**: 22.2.0-beta.2 Migrate Vitest usage from @nx/vite to @nx/vitest package. ### 22.2.0-package-updates **Version**: 22.2.0-beta.1 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `vitest` | `^4.0.0` | Updated only | `@vitest/coverage-v8` | `^4.0.0` | Updated only | `@vitest/coverage-istanbul` | `^4.0.0` | Updated only | `@vitest/ui` | `^4.0.0` | Updated only ### 22.2.0-analog-package-updates **Version**: 22.2.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@analogjs/vite-plugin-angular` | `~2.1.2` | Updated only ### 22.2.0-analog-vitest-angular-package-updates **Version**: 22.2.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@analogjs/vitest-angular` | `~2.1.2` | Updated only ## 21.5.x ### 21.5.0-package-updates **Version**: 21.5.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `vite` | `^7.1.3` | Updated only ## 21.3.x ### 21.3.0-package-updates **Version**: 21.3.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@analogjs/vite-plugin-angular` | `~1.19.1` | Updated only | `@analogjs/vitest-angular` | `~1.19.1` | Updated only ## 21.2.x ### 21.2.0-package-updates **Version**: 21.2.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@analogjs/vite-plugin-angular` | `~1.17.1` | Updated only | `@analogjs/vitest-angular` | `~1.17.1` | Updated only ## 21.1.x ### 21.1.2-package-updates **Version**: 21.1.2-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@analogjs/vitest-angular` | `~1.16.1` | Updated only --- ## Webpack Get started with [Nx with Webpack](/docs/technologies/build-tools/webpack/introduction), or browse the topics below. {% index_page_cards path="technologies/build-tools/webpack" /%} --- ## @nx/webpack Executors The @nx/webpack plugin provides various executors to help you create and configure webpack projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/webpack introduction](/docs/technologies/build-tools/webpack/introduction). ### `dev-server` Serve an application using webpack. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `allowedHosts` | string | This option allows you to whitelist services that are allowed to access the dev server. | | | `baseHref` | string | Base url for the application being built. | | | `buildTarget` | string | Target which builds the application. | | | `hmr` | boolean | Enable hot module replacement. | `false` | | `host` | string | Host to listen on. | `"localhost"` | | `liveReload` | boolean | Whether to reload the page on change, using live-reload. | `true` | | `memoryLimit` | number | Memory limit for type checking service process in `MB`. | | | `open` | boolean | Open the application in the browser. | `false` | | `port` | number | Port to listen on. | `4200` | | `publicHost` | string | Public URL where the application will be served. | | | `ssl` | boolean | Serve using `HTTPS`. | `false` | | `sslCert` | string | SSL certificate to use for serving `HTTPS`. | | | `sslKey` | string | SSL key to use for serving `HTTPS`. | | | `watch` | boolean | Watches for changes and rebuilds application. | `true` | ### `ssr-dev-server` Serve a SSR application using webpack. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `browserTarget` | string [**required**] | Target which builds the browser application. | | | `serverTarget` | string [**required**] | Target which builds the server application. | | | `browserTargetOptions` | object | Additional options to pass into the browser build target. | `{}` | | `port` | number | The port to be set on `process.env.PORT` for use in the server. | `4200` | | `serverTargetOptions` | object | Additional options to pass into the server build target. | `{}` | ### `webpack` Build a project using webpack. `project.json`: ```json5 //... "my-app": { "targets": { "build": { "executor": "@nx/webpack:webpack", "options": { "webpackConfig": "apps/my-app/webpack.config.js" } }, //... } } ``` ```bash nx build my-app ``` ### Examples ###### Using `babelUpwardRootMode` Copying from the [Babel documentation](https://babeljs.io/docs/config-files#root-babelconfigjson-file): > [...] if you are running your Babel compilation process from within a subpackage, you need to tell Babel where to look for the config. There are a few ways to do that, but the recommended way is the "rootMode" option with "upward", which will make Babel search from the working directory upward looking for your babel.config.json file, and will use its location as the "root" value. Setting `babelUpwardRootMode` to `true` in your `project.json` will set `rootMode` option to `upward` in the Babel config. You may want the `upward` mode in a monorepo when projects must apply their individual `.babelrc` file. We recommend that you don't set it at all, so it will use the default to `false` as the `upward` mode brings additional complexity to the build process. ```json5 //... "my-app": { "targets": { "build": { "executor": "@nx/webpack:webpack", "options": { "webpackConfig": "apps/my-app/webpack.config.js", "babelUpwardRootMode": true } }, //... } } ``` When `babelUpwardRootMode` is `true`, Babel will look for a root `babel.config.json` at the root of the workspace, which should look something like this to include all packages: ```json { "babelrcRoots": ["*"] } ``` Then for each package, you must have a `.babelrc` file that will be applied to that package. For example: ```json { "presets": ["@babel/preset-env", "@babel/preset-typescript"] } ``` All packages will use its own `.babelrc` file, thus you must ensure the right presets and plugins are set in each config file. This behavior can lead to build discrepancies between packages, so we recommend that you don't set `babelUpwardRootMode` at all. ```text ├── apps │ └── demo │ └── .babelrc ├── libs │ ├── a │ │ └── .babelrc │ └── b │ └── .babelrc └── babel.config.json ``` In workspace above, if `demo` imports `a` and `b`, it will apply the config `libs/a/.babelrc` and `libs/b/.babelrc` to the respective packages and not apply its own `apps/demo/.babelrc` to `a` and `b`. Anything in `babel.config.json` will apply to all packages. ###### Specify a custom Babel config file If you have a custom Babel config file (i.e. not `.babelrc`), you can use the `configFile` option as follows: ```json5 //... "my-app": { "targets": { "build": { "executor": "@nx/webpack:webpack", "options": { "webpackConfig": "apps/my-app/webpack.config.js", "babelConfig": "apps/my-app/.babelrc.custom.json", } }, // ... } } ``` If you do not set the path to the `.babelrc` file, Nx will look for a `.babelrc` file in the root of your application. Note that this option does not work if `babelUpwardRootMode` is set to `true`. ###### Run webpack with `isolatedConfig` Setting `isolatedConfig` to `true` in your `project.json` file means that Nx will not apply the Nx webpack plugins automatically. In that case, the Nx plugins need to be applied in the project's `webpack.config.js` file (e.g. `withNx`, `withReact`, etc.). So don't forget to also specify the path to your webpack config file (using the `webpackConfig` option). Read more on how to configure Webpack in our [Nx Webpack config guide](/recipes/webpack/webpack-config-setup) an in our [Webpack Plugins guide](/recipes/webpack/webpack-plugins). Note that this is the new default setup for webpack in the latest version of Nx. Set `isolatedConfig` to `true` in your `project.json` file in the `build` target options like this: ```json //... "my-app": { "targets": { "build": { "executor": "@nx/webpack:webpack", "options": { "webpackConfig": "apps/my-app/webpack.config.js", "isolatedConfig": true } }, } } ``` ###### Configuring type checking You can configure type checking behavior using the `typeCheckOptions` option. By default, type checking runs asynchronously (non-blocking). ```json5 //... "my-app": { "targets": { "build": { "executor": "@nx/webpack:webpack", "options": { "webpackConfig": "apps/my-app/webpack.config.js", // Run type checking asynchronously (non-blocking) "typeCheckOptions": { "async": true } } }, //... } } ``` To disable type checking entirely: ```json5 //... "my-app": { "targets": { "build": { "executor": "@nx/webpack:webpack", "options": { "webpackConfig": "apps/my-app/webpack.config.js", "typeCheckOptions": false } }, //... } } ``` ###### Adding runtime dependencies to generated package.json When using `generatePackageJson`, you can add additional runtime dependencies that should be included in the generated `package.json` file. This is useful for Docker installs where you need dependencies that aren't detected automatically: ```json5 //... "my-app": { "targets": { "build": { "executor": "@nx/webpack:webpack", "options": { "webpackConfig": "apps/my-app/webpack.config.js", "generatePackageJson": true, "runtimeDependencies": ["pg", "redis"] } }, //... } } ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `additionalEntryPoints` | array | | | | `assets` | array | List of static application assets. | | | `babelConfig` | string | Path to the babel configuration file of your project. If not provided, Nx will default to the .babelrc file at the root of your project. See https://babeljs.io/docs/en/config-files | | | `babelUpwardRootMode` | boolean | Whether to set rootmode to upward. See https://babeljs.io/docs/en/options#rootmode | | | `baseHref` | string | Base url for the application being built. | | | `buildLibsFromSource` | boolean | Read buildable libraries from source instead of building them separately. If set to `false`, the `tsConfig` option must also be set to remap paths. | `true` | | `cache` | string | Configure webpack caching behavior. When not specified, defaults to `{ type: 'memory' }` for Node targets in watch mode, and `undefined` otherwise. | | | `commonChunk` | boolean | Use a separate bundle containing code used across multiple bundles. | | | `compiler` | string | The compiler to use. | | | `crossOrigin` | string | The `crossorigin` attribute to use for generated javascript script tags. One of 'none' | 'anonymous' | 'use-credentials'. | | | `cssModuleHashFunction` | string | Hash algorithm used to generate CSS module class names. Defaults to `md5`, which FIPS-restricted OpenSSL rejects. Changing it renames generated class names. | | | `deployUrl` | string | URL where the application will be deployed. | | | `externalDependencies` | string | Dependencies to keep external to the bundle. (`all` (default), `none`, or an array of module names) | | | `extractCss` | boolean | Extract CSS into a `.css` file. | | | `extractLicenses` | boolean | Extract all licenses in a separate file, in the case of production builds only. | | | `fileReplacements` | array | Replace files with other files in the build. | | | `generateIndexHtml` | boolean | Generates `index.html` file to the output path. This can be turned off if using a webpack plugin to generate HTML such as `html-webpack-plugin`. | | | `generatePackageJson` | boolean | Generates a `package.json` and pruned lock file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated. | | | `index` | string | HTML File which will be contain the application. | | | `main` | string | The name of the main entry-point file. | | | `memoryLimit` | number | Memory limit for type checking service process in `MB`. | | | `namedChunks` | boolean | Names the produced bundles according to their entry file. | | | `optimization` | string | Enables optimization of the build output. | | | `outputFileName` | string | Name of the main output file. | `"main.js"` | | `outputHashing` | string | Define the output filename cache-busting hashing mode. | | | `outputPath` | string | The output path of the generated files. | | | `poll` | number | Enable and define the file watching poll time period. | | | `polyfills` | string | Polyfills to load before application | | | `postcssConfig` | string | Set a path to PostCSS config that applies to the app and all libs. Defaults to `undefined`, which auto-detects postcss.config.js files in each `app`/`lib` directory. | | | `progress` | boolean | Log progress to the console while building. | | | `publicPath` | string | Set a public path for assets resources with absolute paths. | | | `rebaseRootRelative` | boolean | Whether to rebase absolute path for assets in postcss cli resources. | | | `runtimeChunk` | boolean | Use a separate bundle containing the runtime. | | | `runtimeDependencies` | array | Add runtime dependencies to the generated `package.json` file. Useful for Docker install. | | | `scripts` | array | External Scripts which will be included before the main application entry. | | | `skipOverrides` | boolean | Do not add a `overrides` and `resolutions` entries to the generated package.json file. Only works in conjunction with `generatePackageJson` option. | | | `skipPackageManager` | boolean | Do not add a `packageManager` entry to the generated package.json file. Only works in conjunction with `generatePackageJson` option. | | | `skipTypeChecking` | boolean | Skip the type checking. Default is `false`. | | | `sourceMap` | string | Output sourcemaps. Use 'hidden' for use with error reporting tools without generating sourcemap comment. | | | `standardWebpackConfigFunction` | boolean | Set to true if the webpack config exports a standard webpack function, not an Nx-specific one. See: https://webpack.js.org/configuration/configuration-types/#exporting-a-function | `false` | | `statsJson` | boolean | Generates a 'stats.json' file which can be analyzed using tools such as: 'webpack-bundle-analyzer' or ``. | | | `stylePreprocessorOptions` | object | Options to pass to style preprocessors. | | | `styles` | array | External Styles which will be included with the application | | | `subresourceIntegrity` | boolean | Enables the use of subresource integrity validation. | | | `target` | string | Target platform for the build, same as the Webpack target option. | | | `transformers` | array | List of TypeScript Compiler Transfomers Plugins. | | | `tsConfig` | string | The name of the Typescript configuration file. | | | `typeCheckOptions` | string | Configure type checking during the build. Set to `true` to enable with defaults (async: true). Set to `false` to disable type checking entirely. Use `{ async: true }` to run type checking in a separate process without blocking the build. Default is `{ async: true }`. | | | `vendorChunk` | boolean | Use a separate bundle containing only vendor libraries. | | | `verbose` | boolean | Emits verbose output | | | `watch` | boolean | Enable re-building when files change. | | | `webpackConfig` | string | Path to a function which takes a webpack config, some context and returns the resulting webpack config. See https://nx.dev/guides/customize-webpack | | --- ## @nx/webpack Generators The @nx/webpack plugin provides various generators to help you create and configure webpack projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/webpack introduction](/docs/technologies/build-tools/webpack/introduction). ## `convert-config-to-webpack-plugin` Convert existing Webpack project(s) using `@nx/webpack:webpack` executor that uses `withNx` to use `NxAppWebpackPlugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target. **Usage:** ```bash nx generate @nx/webpack:convert-config-to-webpack-plugin [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/webpack:webpack` executor and `withNx` plugin to use `NxAppWebpackPlugin`. | | | `--skipFormat` | boolean | Whether to format files at the end of the migration. | `false` | ## `convert-to-inferred` Convert existing Webpack project(s) using `@nx/webpack:wepack` executor to use `@nx/webpack/plugin`. **Usage:** ```bash nx generate @nx/webpack:convert-to-inferred [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/webpack:webpack` executor to use `@nx/webpack/plugin`. If not provided, all projects using the `@nx/webpack:webpack` executor will be converted. | | | `--skipFormat` | boolean | Whether to format files. | `false` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/webpack: --help ``` --- ## Guides {% index_page_cards path="technologies/build-tools/webpack/guides" /%} --- ## Nx with Webpack [Webpack](https://webpack.js.org/) is a static module bundler for modern JavaScript applications. In a Webpack monorepo, Nx brings smart task running and caching to every project. The `@nx/webpack` plugin provides executors that allow you to build and serve your projects using webpack, plus an executor for SSR. You can [customize your webpack configuration](/docs/kb/webpack-config-setup) for your projects. Nx also provides [a number of webpack plugins](/docs/kb/webpack-plugins) for supporting Nx and other frameworks. ## Requirements The `@nx/webpack` plugin supports the following package versions. | Package | Supported Versions | | -------------------- | ------------------------------ | | `webpack` | ^5.0.0 | | `webpack-dev-server` | ^5.0.0 | | `webpack-cli` | ^5.0.0 \|\| ^6.0.0 \|\| ^7.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setting up a new Nx workspace with Webpack You can create a new workspace that uses Webpack with one of the following commands: - Generate a new standalone React app set up with Webpack ```shell npx create-nx-workspace@latest --preset=react-standalone --bundler=webpack ``` - Generate a new React monorepo set up with Webpack ```shell npx create-nx-workspace@latest --preset=react-monorepo --bundler=webpack ``` ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/webpack` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/webpack` by running the following command: ```shell nx add @nx/webpack ``` This will install the correct version of `@nx/webpack`. ### How @nx/webpack infers tasks The `@nx/webpack` plugin will create a task for any project that has a Webpack configuration file present. Any of the following files will be recognized as a Webpack configuration file: - `webpack.config.js` - `webpack.config.ts` - `webpack.config.mjs` - `webpack.config.cjs` The configuration directory must also contain a `package.json` or `project.json`. ### View inferred tasks To view inferred tasks for a project, open the [project details view](/docs/concepts/mental-model#inferred-tasks) in Nx Console or run `nx show project my-project --web` in the command line. ### @nx/webpack configuration The `@nx/webpack/plugin` is configured in the `plugins` array in `nx.json`. ```json {% meta="{2-10}" %} // nx.json { "plugins": [ { "plugin": "@nx/webpack/plugin", "options": { "buildTargetName": "build", "previewTargetName": "preview", "serveTargetName": "serve", "serveStaticTargetName": "serve-static", "buildDepsTargetName": "build-deps", "watchDepsTargetName": "watch-deps" } } ] } ``` | Option | Default | Inferred behavior | | ----------------------- | -------------- | ---------------------------------------------------------------- | | `buildTargetName` | `build` | Runs and caches `webpack-cli build` with its configured outputs. | | `serveTargetName` | `serve` | Runs `webpack-cli serve` continuously in development mode. | | `previewTargetName` | `preview` | Runs `webpack-cli serve` continuously in production mode. | | `serveStaticTargetName` | `serve-static` | Serves the build output continuously after the build task. | | `buildDepsTargetName` | `build-deps` | Name of the dependency build task. | | `watchDepsTargetName` | `watch-deps` | Name of the dependency watch task. | The build task depends on builds of project dependencies. When TypeScript project references are enabled, the tasks also run the TypeScript sync generator. Use `include` and `exclude` glob patterns on the plugin entry to scope inference. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/webpack/plugin`. ## Generate a new project using Webpack You can generate a [React](/docs/technologies/react/introduction) application or a [Web](/docs/reference/web) application that uses Webpack in an existing Nx workspace. The [`@nx/react:app`](/docs/technologies/react/generators#application), [`@nx/node:app`](/docs/technologies/node/generators#application) and [`@nx/web:app`](/docs/reference/web/generators#application) generators accept the `bundler` option, where you can pass `webpack`. This will generate a new application configured to use Webpack, and it will also install all the necessary dependencies, including the `@nx/webpack` plugin. To generate a React application using Webpack, run the following: ```bash {% frame="none" %} nx g @nx/react:app apps/my-app --bundler=webpack ``` To generate a Node application using Webpack, run the following: ```bash {% frame="none" %} nx g @nx/node:app apps/my-app --bundler=webpack ``` To generate a Web application using Webpack, run the following: ```bash {% frame="none" %} nx g @nx/web:app apps/my-app --bundler=webpack ``` ## Set up CI for your Webpack monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/webpack Migrations For an overview of the plugin and setup instructions, see the [@nx/webpack introduction](/docs/technologies/build-tools/webpack/introduction). The @nx/webpack plugin provides various migrations to help you migrate to newer versions of webpack projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.0.x ### `update-23-0-0-remove-nx-tsconfig-paths-webpack-plugin-import` **Version**: 23.0.0-beta.10 Rewrites imports of NxTsconfigPathsWebpackPlugin from '@nx/webpack' to the sub-path '@nx/webpack/tsconfig-paths-plugin'. #### Rewrite `NxTsconfigPathsWebpackPlugin` Imports to the `@nx/webpack/tsconfig-paths-plugin` Sub-path The deprecated re-export of `NxTsconfigPathsWebpackPlugin` from `@nx/webpack` is removed in v23. The migration rewrites both ES module imports and CJS `require()` calls to use the `@nx/webpack/tsconfig-paths-plugin` sub-path. Imports that combine the deprecated symbol with other named imports are split into two declarations so the rest of the original import still resolves from `@nx/webpack`. Aliases (`as Plugin`, `: Plugin`) are preserved. #### Sample Code Changes ES module import. ##### Before ```ts title="apps/my-app/webpack.config.ts" {1} import { NxTsconfigPathsWebpackPlugin } from '@nx/webpack'; export default { plugins: [new NxTsconfigPathsWebpackPlugin()] }; ``` ##### After ```ts title="apps/my-app/webpack.config.ts" import { NxTsconfigPathsWebpackPlugin } from '@nx/webpack/tsconfig-paths-plugin'; export default { plugins: [new NxTsconfigPathsWebpackPlugin()] }; ``` ES module import combined with other named imports. ##### Before ```ts title="apps/my-app/webpack.config.ts" {1} import { NxTsconfigPathsWebpackPlugin, NxAppWebpackPlugin } from '@nx/webpack'; ``` ##### After ```ts title="apps/my-app/webpack.config.ts" import { NxTsconfigPathsWebpackPlugin } from '@nx/webpack/tsconfig-paths-plugin'; import { NxAppWebpackPlugin } from '@nx/webpack'; ``` CJS `require()`. ##### Before ```js title="apps/my-app/webpack.config.js" {1} const { NxTsconfigPathsWebpackPlugin } = require('@nx/webpack'); ``` ##### After ```js title="apps/my-app/webpack.config.js" const { NxTsconfigPathsWebpackPlugin, } = require('@nx/webpack/tsconfig-paths-plugin'); ``` ### `rewrite-webpack-internal-subpath-imports` **Version**: 23.0.0-beta.24 Rewrites `@nx/webpack/src/*` subpath imports now that the `./src/*` subpath is no longer exposed by `@nx/webpack`'s exports map. Named imports/exports of public symbols are routed to `@nx/webpack` and the rest to the new `@nx/webpack/internal` entry; `require`, dynamic `import` and `jest.mock` calls reference the whole module and are routed to `@nx/webpack/internal`. ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/webpack/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/webpack` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/webpack/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/webpack/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/webpack/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/webpack/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ### 23.0.0-package-updates **Version**: 23.0.0-beta.24 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `webpack` | `^5.101.3` | Added if not installed | `webpack-dev-server` | `^5.2.1` | Added if not installed | `webpack-cli` | `^7.0.0` | Added if not installed ## 22.0.x ### `update-22-0-0-remove-deprecated-options` **Version**: 22.0.0-beta.0 Remove deprecated deleteOutputPath and sassImplementation options from @nx/webpack:webpack ## 21.5.x ### 21.5.0-package-updates **Version**: 21.5.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `webpack` | `5.101.3` | Updated only ## 21.0.x ### `update-21-0-0-remove-isolated-config` **Version**: 21.0.0-beta.11 Remove isolatedConfig option for @nx/webpack:webpack #### Remove `isolatedConfig` option The `isolatedConfig` option is no longer supported by the `@nx/webpack:webpack` executor. Previously, setting `isolatedConfig: false` allowed you to use the executor's built-in Webpack configuration. If this option is set in `project.json`, then it will be removed in favor of an explicit `webpackConfig` file. The Webpack configuration file matches the previous built-in configuration of the `@nx/webpack:webpack` executor. #### Sample Code Changes ##### Before ```json title="project.json" { "targets": { "build": { "executor": "@nx/webpack:webpack", "options": { "isolatedConfig": false } } } } ``` ##### After ```json title="project.json" {6} { "targets": { "build": { "executor": "@nx/webpack:webpack", "options": { "webpackConfig": "apps/myapp/webpack.config.js" } } } } ``` --- ## .NET Get started with [Nx with .NET](/docs/technologies/dotnet/introduction), or browse the topics below. {% index_page_cards path="technologies/dotnet" /%} --- ## @nx/dotnet Generators The @nx/dotnet plugin provides various generators to help you create and configure dotnet projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/dotnet introduction](/docs/technologies/dotnet/introduction). ## `ci-workflow` Setup a CI Workflow to run Nx in CI. **Usage:** ```bash nx generate @nx/dotnet:ci-workflow [options] ``` **Arguments:** ```bash nx generate @nx/dotnet:ci-workflow [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--ci` | string [**required**] | CI provider. | | ## `init` Initializes a .NET project in the current workspace. **Usage:** ```bash nx generate @nx/dotnet:init [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--keepExistingVersions` | boolean | Keep existing dependencies versions | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--updatePackageScripts` | boolean | Update `package.json` scripts with inferred targets | `false` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/dotnet: --help ``` --- ## Guides {% index_page_cards path="technologies/dotnet/guides" /%} --- ## Nx with .NET Nx brings smart task execution and caching to your .NET monorepo. [.NET](https://dotnet.microsoft.com/) is a free, cross-platform, open-source developer platform for building many different types of applications. With .NET, you can use multiple languages, editors, and libraries to build for web, mobile, desktop, games, IoT, and more. The Nx plugin for .NET registers .NET projects in your Nx workspace. It allows MSBuild tasks to be run through Nx. Nx effortlessly makes your [CI faster](/docs/kb/setup-ci). Nx adds the following features to your workspace: - [Cache task results](/docs/features/cache-task-results) - [Distribute task execution](/docs/features/ci-features/distribute-task-execution) - [Run only tasks affected by a PR](/docs/features/ci-features/affected) - [Interactively explore your workspace](/docs/features/explore-graph) {% aside type="note" title=".NET SDK Compatibility" %} This plugin requires .NET SDK 8.0 or newer. Older versions of .NET core may work, but are unsupported and may lead to issues. If you need support for an older version, please create an issue on [Github](https://github.com/nrwl/nx)! {% /aside %} ## Setup @nx/dotnet ### Install Nx You can install Nx globally. Depending on your package manager, use one of the following commands: {% tabs syncKey="package-manager" %} {% tabitem label="npm" %} ```shell npm add --global nx@latest ``` {% /tabitem %} {% tabitem label="Homebrew (macOS, Linux)" %} ```shell brew install nx ``` {% /tabitem %} {% tabitem label="Chocolatey (Windows)" %} ```shell choco install nx ``` {% /tabitem %} {% tabitem label="apt (Ubuntu)" %} ```shell sudo add-apt-repository ppa:nrwl/nx sudo apt update sudo apt install nx ``` {% /tabitem %} {% /tabs %} ### Add Nx to a .NET workspace In any .NET workspace, run the following command to add Nx and the `@nx/dotnet` plugin: ```shell nx init ``` Then, you can run .NET tasks using Nx. For example: ```shell nx build ``` ### Add .NET to an existing Nx workspace If you already have an Nx workspace set up, you can add the `@nx/dotnet` plugin by running the following command: ```shell nx add @nx/dotnet ``` {% aside title="Minimum Nx Version" type="caution" %} The `@nx/dotnet` plugin only became available in Nx 22.0.0. Your Nx Workspace will need to be on this version or higher to use it. {% /aside %} New projects can be created with the `dotnet new` CLI command, or any other method you may be familiar with. For example: ```shell dotnet new webapi -o ./apps/my-api ``` Full project creation docs can be found here [on the official Microsoft documentation](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-new). ## How @nx/dotnet infers tasks The `@nx/dotnet` plugin uses MSBuild to analyze your .NET solution and project structure. When using `nx add`, the plugin is automatically configured in your `nx.json` file. The plugin automatically detects .NET projects by scanning for the following project file patterns: - `**/*.csproj` (C# projects) - `**/*.fsproj` (F# projects) - `**/*.vbproj` (Visual Basic projects) The plugin analyzes your MSBuild project files to determine: - Project dependencies (via `` elements) - Available build targets (build, test, clean, etc.) - Project outputs and configuration ## View inferred tasks To view inferred tasks for a project, open the [project details view](/docs/features/explore-graph#explore-projects-in-your-workspace) in Nx Console or run `nx show project my-project` in the command line. ## @nx/dotnet configuration The `@nx/dotnet` is configured in the `plugins` array in `nx.json`. Each target type can be configured with: - `targetName` - Rename the target if needed (e.g., change "build" to "compile") - Additional target configuration properties (options, configurations, dependsOn, cache, inputs, outputs) - Set to `false` to disable a target For example: ```json // nx.json { "plugins": [ { "plugin": "@nx/dotnet", "options": { "build": { "targetName": "compile", "configurations": { "production": { "optimization": true } } }, "test": { "targetName": "unit-test", "dependsOn": ["build"] }, "pack": false } } ] } ``` Once a .NET project file has been identified, the targets are created with the configuration you specify in the `nx.json` `plugins` array. The default names for the inferred targets are: - `build` - Compiles the project - `test` - Runs unit tests (for test projects) - `clean` - Removes build outputs - `restore` - Restores NuGet package dependencies - `publish` - Publishes the application (for executable projects) - `pack` - Creates a NuGet package (for library projects) - `watch` - Watches the project for changes and rebuilds (automatically marked as continuous) - `run` - Runs the application (for executable projects) Use `include` and `exclude` glob patterns on the plugin entry to scope inference. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/dotnet`. ### Target availability Not all targets are available for every project type. The plugin intelligently determines which targets to create based on the project type: - **Console Applications & Web Projects**: build, clean, restore, publish, watch, run - **Class Libraries**: build, clean, restore, pack, watch - **Test Projects**: build, clean, restore, test, watch ### Continuous tasks The `@nx/dotnet` plugin automatically marks the `watch` target as [continuous](/docs/reference/project-configuration#continuous), ensuring Nx handles it correctly during development. Unlike watch, `run` is not automatically marked as continuous as it may exit depending on the type of application. To mark `run` as continuous, you can configure it manually in `project.json` or `nx.json`. {% tabs %} {% tabitem label="nx.json" %} In the `nx.json`, you can specify that run should be continuous like so: ```json {% meta="{5}" %} // nx.json { "plugins": [ { "plugin": "@nx/dotnet", "options": { "run": { "continuous": true } } } ] } ``` {% /tabitem %} {% tabitem label="project.json" %} Alternatively, you can create a new `project.json` file in the root of your project (next to the `.csproj` file) and configure the `run` target as continuous like so: ```json {% meta="{4}" %} // project.json { "run": { "continuous": true } } ``` {% /tabitem %} {% /tabs %} ## Working with configurations The `@nx/dotnet` plugin supports MSBuild configurations (Debug, Release, etc.) through Nx configuration system. You can run tasks with specific configurations: ```shell # Build with Release configuration nx build my-app --configuration release # Build with Debug configuration (usually the default) nx build my-app --configuration debug ``` ## Set up CI for your .NET monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/dotnet Migrations For an overview of the plugin and setup instructions, see the [@nx/dotnet introduction](/docs/technologies/dotnet/introduction). The @nx/dotnet plugin provides various migrations to help you migrate to newer versions of dotnet projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.0.x ### `update-23-0-0-migrate-dotnet-plugin-path` **Version**: 23.0.0-beta.24 Update `nx.json` plugin registrations that use the removed `@nx/dotnet/plugin` subpath to the bare `@nx/dotnet` specifier. --- ## ESLint Get started with [Nx with ESLint](/docs/technologies/eslint/introduction), or browse the topics below. {% index_page_cards path="technologies/eslint" /%} --- ## ESLint Plugin {% index_page_cards path="technologies/eslint/eslint-plugin" /%} --- ## Guides {% index_page_cards path="technologies/eslint/eslint-plugin/guides" /%} --- ## @nx/eslint-plugin Migrations For an overview of the plugin and setup instructions, see the [@nx/eslint-plugin introduction](/docs/technologies/eslint/eslint-plugin/introduction). No migrations found for `@nx/eslint-plugin`. Nx only retains migrations for the last 2 major versions of Nx. It's possible migrations existed in previous versions of `@nx/eslint-plugin`. You can check the previous docs for [Nx v22](https://22.nx.dev/docs) or [Nx v21](https://21.nx.dev/docs). --- ## @nx/eslint Executors The @nx/eslint plugin provides various executors to help you create and configure eslint projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/eslint introduction](/docs/technologies/eslint/introduction). ### `lint` ESLint Lint Target. Linter can be configured in multiple ways. The basic way is to provide only `lintFilePatterns`, which tells us where to look for files to lint. If not specified, it defaults to `['{projectRoot}']`. `project.json`: ```json "lint": { "executor": "@nx/eslint:lint", "options": { "lintFilePatterns": ["apps/frontend/**/*.ts"] } } ``` ### Examples ###### Fixing linter issues Linter provides an automated way of fixing known issues. To ensure that those changes are properly cached, we need to add an `outputs` property to the `lint` target. Omitting the `outputs` property would produce an invalid cache record. Both of these properties are set by default when scaffolding a new project. ```json "lint": { "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"], "options": { "lintFilePatterns": ["apps/frontend/**/*.ts"] } } ``` With these settings, we can run the command with a `--fix` flag: ```bash nx run frontend:lint --fix ``` We can also set this flag via project configuration to always fix files when running lint: ```json "lint": { "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"], "options": { "lintFilePatterns": ["apps/frontend/**/*.ts"], "fix": true } } ``` ###### Custom output format ESLint executor uses the `stylish` output format by default. You can change this by specifying the `format` property: ```json "lint": { "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"], "options": { "lintFilePatterns": ["apps/frontend/**/*.ts"], "format": "json" } } ``` ###### Silence warnings Migrated or legacy projects tend to have an overwhelming amount of lint errors. We might want to change those temporarily to be warnings so they don't block the development. But they would still clutter the report. We can run the command with `--quiet` to hide warning (errors would still break the lint): ```bash nx run frontend:lint --quiet ``` We can also set this via project configuration as a default option. ```json "lint": { "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"], "options": { "lintFilePatterns": ["apps/frontend/**/*.ts"], "quiet": true } } ``` ###### Flat Config file `ESLint` provides several ways of specifying the configuration. The executor resolves the config file automatically, but you can override it by setting the `eslintConfig` flag, for example to point at a flat config file: ```json "lint": { "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"], "options": { "lintFilePatterns": ["apps/frontend/**/*.ts"], "eslintConfig": "eslint.config.cjs" } } ``` **Note:** In contrast to other configuration formats, the `Flat Config` requires that all configuration files are converted to `eslint.config.cjs`. Built-in migrations and generators support only `.eslintrc.json` at the moment. ###### Bulk Suppression ESLint v9.24.0 introduced bulk suppression features that allow you to suppress existing violations while only new violations trigger errors. This is particularly useful when migrating to stricter lint rules in existing codebases. **Suppress all existing violations:** ```json "lint": { "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"], "options": { "lintFilePatterns": ["apps/frontend/**/*.ts"], "suppressAll": true } } ``` **Suppress specific rules:** ```json "lint": { "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"], "options": { "lintFilePatterns": ["apps/frontend/**/*.ts"], "suppressRule": ["no-console", "no-unused-vars"] } } ``` **Specify custom suppressions file location:** ```json "lint": { "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"], "options": { "lintFilePatterns": ["apps/frontend/**/*.ts"], "suppressAll": true, "suppressionsLocation": "./custom-suppressions.json" } } ``` **Note:** Bulk suppression options require ESLint v9.24.0 or higher. When using these options with older ESLint versions, the executor will throw an error. --- #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `cache` | boolean | Only check changed files. | `false` | | `cacheLocation` | string | Path to the cache file or directory. | | | `cacheStrategy` | string | Strategy to use for detecting changed files in the cache. | `"metadata"` | | `errorOnUnmatchedPattern` | boolean | When set to false, equivalent of the `--no-error-on-unmatched-pattern` flag on the ESLint CLI. | `true` | | `eslintConfig` | string | The name of the ESLint configuration file. | | | `fix` | boolean | Fixes linting errors (may overwrite linted files). | `false` | | `force` | boolean | Succeeds even if there was linting errors. | `false` | | `format` | string | ESLint Output formatter (https://eslint.org/docs/user-guide/formatters). | `"stylish"` | | `hasTypeAwareRules` | boolean | Deprecated. No longer has any effect. | | | `ignorePath` | string | The path of the `.eslintignore` file. Not supported for Flat Config. | | | `lintFilePatterns` | array | One or more files/dirs/globs to pass directly to ESLint's `lintFiles()` method. | `["{projectRoot}"]` | | `maxWarnings` | number | Number of warnings to trigger nonzero exit code - default: `-1`. | `-1` | | `noEslintrc` | boolean | The equivalent of the `--no-eslintrc` flag on the ESLint CLI, it is `false` by default. | `false` | | `outputFile` | string | File to write report to. | | | `printConfig` | string | The equivalent of the `--print-config` flag on the ESLint CLI. | | | `quiet` | boolean | Report errors only - default: `false`. | `false` | | `reportUnusedDisableDirectives` | string | The equivalent of the `--report-unused-disable-directives` flag on the ESLint CLI. | | | `resolvePluginsRelativeTo` | string | The equivalent of the `--resolve-plugins-relative-to` flag on the ESLint CLI. Not supported for Flat Config. | | | `rulesdir` | array | The equivalent of the `--rulesdir` flag on the ESLint CLI. | `[]` | | `silent` | boolean | Hide output text. | `false` | | `suppressAll` | boolean | Suppress all existing violations. This is equivalent to the `--suppress-all` flag on the ESLint CLI. Requires ESLint v9.24.0 or higher. | `false` | | `suppressionsLocation` | string | Specify the location of the suppressions file. This is equivalent to the `--suppressions-location` flag on the ESLint CLI. Defaults to 'eslint-suppressions.json' in the project root. Requires ESLint v9.24.0 or higher. | | | `suppressRule` | array | Suppress violations for specific rules. This is equivalent to the `--suppress-rule` flag on the ESLint CLI. Requires ESLint v9.24.0 or higher. | `[]` | --- ## @nx/eslint Generators The @nx/eslint plugin provides various generators to help you create and configure eslint projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/eslint introduction](/docs/technologies/eslint/introduction). ## `convert-to-flat-config` Convert an Nx workspace's ESLint configs to use Flat Config. **Usage:** ```bash nx generate @nx/eslint:convert-to-flat-config [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--eslintConfigFormat` | string | The format of the generated ESLint flat config files. | `"mjs"` | | `--keepExistingVersions` | boolean | Keep the existing versions of any ESLint-related packages already declared in `package.json` instead of overwriting them. Only newly added packages are installed at the latest supported versions. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | ## `convert-to-inferred` Convert existing Eslint project(s) using `@nx/eslint:lint` executor to use `@nx/eslint/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target. **Usage:** ```bash nx generate @nx/eslint:convert-to-inferred [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/eslint:lint` executor to use `@nx/eslint/plugin`. | | | `--skipFormat` | boolean | Whether to format files at the end of the migration. | `false` | ## `workspace-rule` Create a new Workspace Lint Rule. ###### Create rule This command will generate a new workspace lint rule called `my-custom-rule`. The new rule will be generated in `tools/eslint-rules/rules` folder: ```shell nx g @nx/eslint:workspace-rule my-custom-rule ``` ###### Custom sub-folder We can change the default sub-folder from `rules` and specify a custom one: ```shell nx g @nx/eslint:workspace-rule --name=my-custom-rule --directory=my/custom/path ``` The command above will generate the rule in `tools/eslint-rules/my/custom/path` folder. --- **Usage:** ```bash nx generate @nx/eslint:workspace-rule [options] ``` **Arguments:** ```bash nx generate @nx/eslint:workspace-rule [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--directory` | string [**required**] | Create the rule under this directory within `tools/eslint-rules/` (can be nested). | `"rules"` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/eslint: --help ``` --- ## Guides {% index_page_cards path="technologies/eslint/guides" /%} --- ## Nx with ESLint [ESLint](https://eslint.org/) statically analyzes your code to find and fix problems. In an ESLint monorepo setup, Nx runs lint tasks with caching enabled and re-lints only the projects a change affects. The ESLint plugin integrates ESLint with Nx and includes code generators to set up ESLint in your workspace. ## Requirements The `@nx/eslint` plugin supports the following package versions. | Package | Supported Versions | | -------- | ------------------- | | `eslint` | ^9.0.0 \|\| ^10.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setting up @nx/eslint ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/eslint` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/eslint` by running the following command: ```shell nx add @nx/eslint ``` This will install the correct version of `@nx/eslint`. ### How @nx/eslint infers tasks The `@nx/eslint` plugin will create a task for any project that has an ESLint configuration file present and files to lint. Any of the following files will be recognized as an ESLint configuration file: - `.eslintrc` - `.eslintrc.js` - `.eslintrc.cjs` - `.eslintrc.yaml` - `.eslintrc.yml` - `.eslintrc.json` - `eslint.config.js` - `eslint.config.mjs` - `eslint.config.cjs` - `eslint.config.ts` - `eslint.config.mts` - `eslint.config.cts` Because ESLint applies configuration files to all subdirectories, the `@nx/eslint` plugin will also infer tasks for projects in subdirectories. So, if there is an ESLint configuration file in the root of the repository, every project will have an inferred ESLint task. Even if a project has an ESLint configuration file, it will only have an inferred ESLint task if there are files to lint. Otherwise, the task will not be created. Therefore, if you don't want an ESLint task to be inferred for a particular project, make sure the project files are properly excluded from ESLint. ### View inferred tasks To view inferred tasks for a project, open the [project details view](/docs/concepts/mental-model#inferred-tasks) in Nx Console or run `nx show project my-project --web` in the command line. ### @nx/eslint configuration The `@nx/eslint/plugin` is configured in the `plugins` array in `nx.json`. ```json {% meta="{2-9}" %} // nx.json { "plugins": [ { "plugin": "@nx/eslint/plugin", "options": { "targetName": "lint", "extensions": ["ts", "tsx", "js", "jsx", "html", "vue"] } } ] } ``` | Option | Type | Default | Description | | ------------ | -------- | ------------------------------------------------- | ----------------------------------------------- | | `targetName` | string | `lint` | Name of the ESLint task. | | `extensions` | string[] | `ts, cts, mts, tsx, js, cjs, mjs, jsx, html, vue` | File extensions Nx checks before adding a task. | The lint task runs ESLint from the project root and is cached, with inputs and outputs based on your ESLint configuration. Use `include` and `exclude` glob patterns on the plugin entry to scope inference. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/eslint/plugin`. ## Lint You can lint an application or a library with the following command: ```shell nx lint my-project ``` ## Utils - [convert-to-flat-config](/docs/technologies/eslint/generators#convert-to-flat-config) - Converts the workspace's [ESLint](https://eslint.org/) configs to the new [Flat Config](https://eslint.org/blog/2022/08/new-config-system-part-2) ## ESLint plugin Read about our dedicated ESLint plugin - [eslint-plugin-nx](/docs/technologies/eslint/eslint-plugin). ## Set up CI In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/eslint Migrations For an overview of the plugin and setup instructions, see the [@nx/eslint introduction](/docs/technologies/eslint/introduction). The @nx/eslint plugin provides various migrations to help you migrate to newer versions of eslint projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.1.x ### `update-23-1-0-convert-to-flat-config` **Version**: 23.1.0-beta.0 Convert remaining ESLint configs to flat config for ESLint v9 and keep the workspace lint-passing, disabling rules whose preset defaults changed. #### Requires | Name | Version | |------|---------| `eslint` | `>=9.0.0` | ### `update-23-1-0-remove-removed-typescript-eslint-extension-rules` **Version**: 23.1.0-beta.5 Handle typescript-eslint rules removed in v8 in ESLint flat configs, since referencing a removed rule stops the config from loading. Deletes the removed formatting/extension rules (e.g. @typescript-eslint/no-extra-semi) and rewrites the safe 1:1 renames (no-throw-literal -> only-throw-error, no-useless-template-literals -> no-unnecessary-template-expression) to preserve enforcement. ### `update-23-1-0-migrate-ban-types-rule` **Version**: 23.1.0-beta.5 Migrate the removed @typescript-eslint/ban-types rule to its v8 successors (no-empty-object-type, no-unsafe-function-type, no-wrapper-object-types), whose options do not map 1:1 - so it is driven by an AI prompt rather than a deterministic codemod. ### 23.1.0-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `eslint` | `^9.8.0` | Updated only | `typescript-eslint` | `^8.40.0` | Updated only | `@typescript-eslint/eslint-plugin` | `^8.40.0` | Updated only | `@typescript-eslint/parser` | `^8.40.0` | Updated only | `@typescript-eslint/utils` | `^8.40.0` | Updated only | `@typescript-eslint/type-utils` | `^8.40.0` | Updated only | `@typescript-eslint/rule-tester` | `^8.40.0` | Updated only | `@typescript-eslint/scope-manager` | `^8.40.0` | Updated only | `@typescript-eslint/typescript-estree` | `^8.40.0` | Updated only | `eslint-config-prettier` | `^10.0.0` | Updated only ### 23.1.0-typescript-eslint-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `typescript-eslint` | `^8.58.0` | Updated only | `@typescript-eslint/eslint-plugin` | `^8.58.0` | Updated only | `@typescript-eslint/parser` | `^8.58.0` | Updated only | `@typescript-eslint/utils` | `^8.58.0` | Updated only | `@typescript-eslint/rule-tester` | `^8.58.0` | Updated only | `@typescript-eslint/scope-manager` | `^8.58.0` | Updated only | `@typescript-eslint/typescript-estree` | `^8.58.0` | Updated only ### 23.1.0-@typescript-eslint-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `typescript-eslint` | `^8.58.0` | Updated only | `@typescript-eslint/eslint-plugin` | `^8.58.0` | Updated only | `@typescript-eslint/parser` | `^8.58.0` | Updated only | `@typescript-eslint/utils` | `^8.58.0` | Updated only | `@typescript-eslint/rule-tester` | `^8.58.0` | Updated only | `@typescript-eslint/scope-manager` | `^8.58.0` | Updated only | `@typescript-eslint/typescript-estree` | `^8.58.0` | Updated only ## 23.0.x ### `rewrite-eslint-internal-subpath-imports` **Version**: 23.0.0-beta.17 Rewrites `@nx/eslint/src/*` subpath imports now that the `./src/*` subpath is no longer exposed by `@nx/eslint`'s exports map. Named imports/exports of public symbols are routed to `@nx/eslint` and the rest to the new `@nx/eslint/internal` entry; `require`, dynamic `import` and `jest.mock` calls reference the whole module and are routed to `@nx/eslint/internal`. ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/eslint/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/eslint` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/eslint/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/eslint/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/eslint/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/eslint/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ## 22.7.x ### `update-executor-lint-inputs` **Version**: 22.7.0-beta.12 Add missing inputs to @nx/eslint:lint executor target defaults ## 21.5.x ### 21.5.0-typescript-eslint-package-updates **Version**: 21.5.0-beta.2 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `typescript-eslint` | `^8.40.0` | Updated only | `@typescript-eslint/eslint-plugin` | `^8.40.0` | Updated only | `@typescript-eslint/parser` | `^8.40.0` | Updated only | `@typescript-eslint/utils` | `^8.40.0` | Updated only | `@typescript-eslint/rule-tester` | `^8.40.0` | Updated only | `@typescript-eslint/scope-manager` | `^8.40.0` | Updated only | `@typescript-eslint/typescript-estree` | `^8.40.0` | Updated only ### 21.5.0-@typescript-eslint-package-updates **Version**: 21.5.0-beta.2 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `typescript-eslint` | `^8.40.0` | Updated only | `@typescript-eslint/eslint-plugin` | `^8.40.0` | Updated only | `@typescript-eslint/parser` | `^8.40.0` | Updated only | `@typescript-eslint/utils` | `^8.40.0` | Updated only | `@typescript-eslint/rule-tester` | `^8.40.0` | Updated only | `@typescript-eslint/scope-manager` | `^8.40.0` | Updated only | `@typescript-eslint/typescript-estree` | `^8.40.0` | Updated only ## 21.2.x ### 21.2.0-typescript-eslint-package-updates **Version**: 21.2.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `typescript-eslint` | `^8.29.0` | Updated only | `@typescript-eslint/eslint-plugin` | `^8.29.0` | Updated only | `@typescript-eslint/parser` | `^8.29.0` | Updated only | `@typescript-eslint/utils` | `^8.29.0` | Updated only | `@typescript-eslint/rule-tester` | `^8.29.0` | Updated only | `@typescript-eslint/scope-manager` | `^8.29.0` | Updated only | `@typescript-eslint/typescript-estree` | `^8.29.0` | Updated only ### 21.2.0-@typescript-eslint-package-updates **Version**: 21.2.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `typescript-eslint` | `^8.29.0` | Updated only | `@typescript-eslint/eslint-plugin` | `^8.29.0` | Updated only | `@typescript-eslint/parser` | `^8.29.0` | Updated only | `@typescript-eslint/utils` | `^8.29.0` | Updated only | `@typescript-eslint/rule-tester` | `^8.29.0` | Updated only | `@typescript-eslint/scope-manager` | `^8.29.0` | Updated only | `@typescript-eslint/typescript-estree` | `^8.29.0` | Updated only --- ## Java Get started with [Nx with Java](/docs/technologies/java/introduction), or browse the topics below. {% index_page_cards path="technologies/java" /%} --- ## Gradle Get started with [Nx with Gradle](/docs/technologies/java/gradle/introduction), or browse the topics below. {% index_page_cards path="technologies/java/gradle" /%} --- ## @nx/gradle Executors The @nx/gradle plugin provides various executors to help you create and configure gradle projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/gradle introduction](/docs/technologies/java/gradle/introduction). ### `gradle` The Gradle Impl executor is used to run Gradle tasks. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `taskName` | string [**required**] | The name of the Gradle task to run. | | | `__unparsed__` | array | Additional arguments to pass to the gradle command (automatically populated by Nx). | | | `args` | string | The arguments to pass to the Gradle task. | | | `excludeDependsOn` | boolean | If true, the tasks will not execute its dependsOn tasks (e.g. pass --exclude-task args to gradle command). If false, the task will execute its dependsOn tasks. | `true` | | `includeDependsOnTasks` | array | List of Gradle task paths that should be included (not excluded) even when excludeDependsOn is true. These are typically provider-based dependencies that Gradle must resolve. | | | `testClassName` | string | The full test name to run for test task (package name and class name). | | --- ## @nx/gradle Generators The @nx/gradle plugin provides various generators to help you create and configure gradle projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/gradle introduction](/docs/technologies/java/gradle/introduction). ## `ci-workflow` Setup a CI Workflow to run Nx in CI. **Usage:** ```bash nx generate @nx/gradle:ci-workflow [options] ``` **Arguments:** ```bash nx generate @nx/gradle:ci-workflow [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--ci` | string [**required**] | CI provider. | | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/gradle: --help ``` --- ## Nx with Gradle [Gradle](https://gradle.org/) is a build automation tool for JVM-based projects. Nx brings task running, caching, and graph analysis to a Gradle monorepo. The `@nx/gradle` plugin registers Gradle projects in the Nx graph so you can [set up Gradle in Nx](#setup), [run local tasks](#local-development), [configure task inference](#configuration), and [scale in CI](#ci-considerations). You can use Gradle with Nx without the plugin and still get [task caching](/docs/features/cache-task-results), [task orchestration](/docs/features/run-tasks), and the [project graph](/docs/features/explore-graph). ## Requirements | Dependency | Supported Versions | | ---------- | ------------------ | | Java | >= 17 | | Gradle | >= 8.0 | Using older Java versions is unsupported and may lead to issues. If you need support for an older version, please create an issue on [GitHub](https://github.com/nrwl/nx). ## Setup ### Install Nx Install Nx with your preferred package manager: {% tabs syncKey="package-manager" %} {% tabitem label="npm" %} ```shell npm install --global nx@latest ``` You can use `pnpm`, `yarn`, or `bun` if you prefer as well. {% /tabitem %} {% tabitem label="Homebrew (macOS, Linux)" %} ```shell brew install nx ``` {% /tabitem %} {% tabitem label="Chocolatey (Windows)" %} ```shell choco install nx ``` {% /tabitem %} {% tabitem label="apt (Ubuntu)" %} ```shell sudo add-apt-repository ppa:nrwl/nx sudo apt update sudo apt install nx ``` {% /tabitem %} {% /tabs %} ### Add Nx to a Gradle Workspace From the root of your Gradle workspace, run: ```shell nx init ``` Nx will add the `@nx/gradle` plugin and configure the Gradle companion plugin `dev.nx.gradle.project-graph` in your build files. {% aside type="note" title="Gradle Companion Plugin" %} The `dev.nx.gradle.project-graph` plugin is added to your `build.gradle` or `build.gradle.kts` so Nx can read your Gradle project graph directly from source. {% /aside %} ### Verify the setup List the projects Nx inferred from Gradle: ```shell nx show projects ``` Open the project details view for a specific project. ```shell nx show project my-app ``` ## Local development Run Gradle tasks through Nx using inferred targets: ```shell nx build my-gradle-lib nx test my-gradle-lib ``` Nx maps these targets back to the Gradle tasks (for example `build` or `test`) and executes them using the Gradle wrapper. If a task should run in watch or server mode, mark it as continuous so Nx treats it correctly. ## Configuration ### How @nx/gradle infers tasks Nx relies on the `dev.nx.gradle.project-graph` Gradle plugin to report your Gradle projects and tasks. The plugin adds an `nxProjectGraph` task that outputs JSON, which Nx uses to build the project graph. Nx also scans your Gradle build files to locate project roots and inputs so it can infer targets accurately. The `nx init` workflow adds this plugin to your Gradle build files. If you ever need to add it manually, configure it in `build.gradle` or `build.gradle.kts` and apply it to all projects. ### View inferred tasks To view inferred tasks for a project, open the project details view in Nx Console or run: ```shell nx show project my-app ``` Replace `my-app` with your project name. ### @nx/gradle configuration Configure the plugin in the `plugins` array of `nx.json`: ```json // nx.json { "plugins": [ { "plugin": "@nx/gradle", "options": { "testTargetName": "test", "ciTestTargetName": "test-ci", "targetNamePrefix": "gradle-", "gradleExecutableDirectory": "./gradle-projects" } } ] } ``` ### How options work Nx passes the plugin options to the Gradle `nxProjectGraph` task as Gradle properties. Any option that ends with `TargetName` overrides the inferred Nx target name for that Gradle task. Use this to avoid name clashes or to introduce CI-specific targets. ### Options | Option | Type | Default | Description | | --------------------------- | ------ | ------- | -------------------------------------------------------------------------- | | `testTargetName` | string | `test` | Nx target name to map the Gradle `test` task. | | `ciTestTargetName` | string | none | Enables CI test atomization and defines the base name for CI test targets. | | `targetNamePrefix` | string | none | Prefix for all inferred Gradle targets (useful in polyglot workspaces). | | `gradleExecutableDirectory` | string | none | Directory containing the Gradle wrapper to use. | | `TargetName` | string | none | Override for any Gradle task name (for example `assembleTargetName`). | ### Exclude or include specific projects Use `include`/`exclude` glob patterns to scope the plugin to specific projects: ```json {% meta="{5-6}" %} // nx.json { "plugins": [ { "plugin": "@nx/gradle", "include": ["apps/**/*"], "exclude": ["apps/legacy-app/**/*"], "options": { ... } } ] } ``` Individual Gradle projects can also configure Nx-specific settings using the `nx { }` DSL in `build.gradle` or `build.gradle.kts`. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter for inferred Gradle tasks must use the exact identifier `@nx/gradle`. {% aside type="caution" title="Legacy Gradle plugin" %} The public `@nx/gradle/plugin-v1` entry point is deprecated and will be removed in Nx 24. Replace it with `@nx/gradle`. The legacy entry recognizes Gradle build files and test files and supports `buildTargetName` (default `build`), `classesTargetName` (default `classes`), `testTargetName` (default `test`), `ciTargetName`, `includeSubprojectsTasks` (default `false`), and arbitrary `TargetName` overrides. {% /aside %} ## Set up CI for your Gradle monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). ## What's next {% cardgrid %} {% linkcard title="Gradle Tutorial" description="Add Nx to an existing Gradle workspace step-by-step." href="/docs/kb/gradle-tutorial" /%} {% linkcard title="Java with Nx" description="Overview of Java support across Gradle and Maven." href="/docs/technologies/java/introduction" /%} {% linkcard title="CI Workflow Generator" description="Generate CI workflows that run affected tasks." href="/docs/reference/workspace/generators#ci-workflow" /%} {% linkcard title="Gradle Generators Reference" description="Full reference for @nx/gradle generators." href="/docs/technologies/java/gradle/generators" /%} {% linkcard title="Gradle Executors Reference" description="Full reference for @nx/gradle executors." href="/docs/technologies/java/gradle/executors" /%} {% /cardgrid %} --- ## @nx/gradle Migrations For an overview of the plugin and setup instructions, see the [@nx/gradle introduction](/docs/technologies/java/gradle/introduction). The @nx/gradle plugin provides various migrations to help you migrate to newer versions of gradle projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.2.x ### `change-plugin-version-0-1-25` **Version**: 23.2.0-beta.11 Change dev.nx.gradle.project-graph to version 0.1.25 in build file #### Change dev.nx.gradle.project-graph to version 0.1.25 Change dev.nx.gradle.project-graph to version 0.1.25 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.24" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.25" } ``` ## 23.1.x ### `change-plugin-version-0-1-23` **Version**: 23.1.0-beta.4 Change dev.nx.gradle.project-graph to version 0.1.23 in build file #### Change dev.nx.gradle.project-graph to version 0.1.23 Change dev.nx.gradle.project-graph to version 0.1.23 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.22" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.23" } ``` ### `change-plugin-version-0-1-24` **Version**: 23.1.0-beta.7 Change dev.nx.gradle.project-graph to version 0.1.24 in build file #### Change dev.nx.gradle.project-graph to version 0.1.24 Change dev.nx.gradle.project-graph to version 0.1.24 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.23" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.24" } ``` ## 23.0.x ### `change-plugin-version-0-1-21` **Version**: 23.0.0-beta.11 Change dev.nx.gradle.project-graph to version 0.1.21 in build file #### Change dev.nx.gradle.project-graph to version 0.1.21 Change dev.nx.gradle.project-graph to version 0.1.21 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.20" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.21" } ``` ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/gradle` and `@nx/gradle/plugin-v1` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/gradle` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/gradle` and `@nx/gradle/plugin-v1` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/gradle'; ``` ##### After ```ts import { createNodes } from '@nx/gradle'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/gradle` and `@nx/gradle/plugin-v1` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ### `change-plugin-version-0-1-22` **Version**: 23.0.0-rc.2 Change dev.nx.gradle.project-graph to version 0.1.22 in build file #### Change dev.nx.gradle.project-graph to version 0.1.22 Change dev.nx.gradle.project-graph to version 0.1.22 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.21" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.22" } ``` ## 22.7.x ### `change-plugin-version-0-1-16` **Version**: 22.7.0-beta.2 Change dev.nx.gradle.project-graph to version 0.1.16 in build file #### Change dev.nx.gradle.project-graph to version 0.1.16 Change dev.nx.gradle.project-graph to version 0.1.16 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.15" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.16" } ``` ### `change-plugin-version-0-1-17` **Version**: 22.7.0-beta.4 Change dev.nx.gradle.project-graph to version 0.1.17 in build file #### Change dev.nx.gradle.project-graph to version 0.1.17 Change dev.nx.gradle.project-graph to version 0.1.17 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.16" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.17" } ``` ### `change-plugin-version-0-1-18` **Version**: 22.7.0-beta.9 Change dev.nx.gradle.project-graph to version 0.1.18 in build file #### Change dev.nx.gradle.project-graph to version 0.1.18 Change dev.nx.gradle.project-graph to version 0.1.18 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.17" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.18" } ``` ### `change-plugin-version-0-1-19` **Version**: 22.7.0-beta.11 Change dev.nx.gradle.project-graph to version 0.1.19 in build file #### Change dev.nx.gradle.project-graph to version 0.1.19 Change dev.nx.gradle.project-graph to version 0.1.19 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.18" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.19" } ``` ### `change-plugin-version-0-1-20` **Version**: 22.7.0-beta.16 Change dev.nx.gradle.project-graph to version 0.1.20 in build file #### Change dev.nx.gradle.project-graph to version 0.1.20 Change dev.nx.gradle.project-graph to version 0.1.20 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.19" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.20" } ``` ## 22.6.x ### `change-plugin-version-0-1-14` **Version**: 22.6.0-beta.11 Change dev.nx.gradle.project-graph to version 0.1.14 in build file #### Change dev.nx.gradle.project-graph to version 0.1.14 Change dev.nx.gradle.project-graph to version 0.1.14 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.13" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.14" } ``` ### `change-plugin-version-0-1-15` **Version**: 22.6.0-beta.13 Change dev.nx.gradle.project-graph to version 0.1.15 in build file #### Change dev.nx.gradle.project-graph to version 0.1.15 Change dev.nx.gradle.project-graph to version 0.1.15 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.14" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.15" } ``` ## 22.5.x ### `change-plugin-version-0-1-12` **Version**: 22.5.0-beta.5 Change dev.nx.gradle.project-graph to version 0.1.12 in build file #### Change dev.nx.gradle.project-graph to version 0.1.12 Change dev.nx.gradle.project-graph to version 0.1.12 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.11" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.12" } ``` ### `change-plugin-version-0-1-13` **Version**: 22.5.3 Change dev.nx.gradle.project-graph to version 0.1.13 in build file #### Change dev.nx.gradle.project-graph to version 0.1.13 Change dev.nx.gradle.project-graph to version 0.1.13 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.12" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.13" } ``` ## 22.4.x ### `change-plugin-version-0-1-11` **Version**: 22.4.0-beta.2 Change dev.nx.gradle.project-graph to version 0.1.11 in build file #### Change dev.nx.gradle.project-graph to version 0.1.11 Change dev.nx.gradle.project-graph to version 0.1.11 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.10" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.11" } ``` ## 22.2.x ### `change-plugin-version-0-1-10` **Version**: 22.2.0-beta.4 Change dev.nx.gradle.project-graph to version 0.1.10 in build file #### Change dev.nx.gradle.project-graph to version 0.1.10 Change dev.nx.gradle.project-graph to version 0.1.10 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.0" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.10" } ``` ## 22.1.x ### `change-plugin-version-0-1-9` **Version**: 22.1.0-rc.3 Change dev.nx.gradle.project-graph to version 0.1.9 in build file #### Change dev.nx.gradle.project-graph to version 0.1.9 Change dev.nx.gradle.project-graph to version 0.1.9 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.0" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.9" } ``` ## 21.6.x ### `change-plugin-version-0-1-8` **Version**: 21.6.1-beta.2 Change dev.nx.gradle.project-graph to version 0.1.8 in build file #### Change dev.nx.gradle.project-graph to version 0.1.8 Change dev.nx.gradle.project-graph to version 0.1.8 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.0" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.8" } ``` ## 21.5.x ### `change-plugin-version-0-1-7` **Version**: 21.5.1-beta.5 Change dev.nx.gradle.project-graph to version 0.1.7 in build file #### Change dev.nx.gradle.project-graph to version 0.1.7 Change dev.nx.gradle.project-graph to version 0.1.7 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.0" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.7" } ``` ## 21.4.x ### `change-plugin-version-0-1-5` **Version**: 21.4.0-beta.12 Change dev.nx.gradle.project-graph to version 0.1.5 in build file #### Change dev.nx.gradle.project-graph to version 0.1.5 Change dev.nx.gradle.project-graph to version 0.1.5 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.0" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.5" } ``` ### `change-plugin-version-0-1-6` **Version**: 21.4.1-beta.1 Change dev.nx.gradle.project-graph to version 0.1.6 in build file #### Change dev.nx.gradle.project-graph to version 0.1.6 Change dev.nx.gradle.project-graph to version 0.1.6 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.0" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.6" } ``` ## 21.3.x ### `change-plugin-version-0-1-2` **Version**: 21.3.0-beta.0 Change dev.nx.gradle.project-graph to version 0.1.2 in build file #### Change dev.nx.gradle.project-graph to version 0.1.2 Change dev.nx.gradle.project-graph to version 0.1.2 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.0" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.2" } ``` ### `change-plugin-version-0-1-4` **Version**: 21.3.11-beta.0 Change dev.nx.gradle.project-graph to version 0.1.4 in build file #### Change dev.nx.gradle.project-graph to version 0.1.4 Change dev.nx.gradle.project-graph to version 0.1.4 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.2" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.4" } ``` ## 21.1.x ### `change-plugin-version-0-1-0` **Version**: 21.1.2-beta.1 Change dev.nx.gradle.project-graph to version 0.1.0 in build file #### Change dev.nx.gradle.project-graph to version 0.1.0 Change dev.nx.gradle.project-graph to version 0.1.0 in build file #### Sample Code Changes ##### Before ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.0.5" } ``` ##### After ```text title="build.gradle" plugins { id "dev.nx.gradle.project-graph" version "0.1.0" } ``` ## 21.0.x ### `change-plugin-to-v1` **Version**: 21.0.0-beta.5 Change @nx/gradle plugin to version 1 #### Change @nx/gradle plugin to @nx/gradle/plugin-v1 Change @nx/gradle plugin to version 1 in nx.json #### Sample Code Changes ##### Before ```json title="nx.json" { "plugins": ["@nx/gradle"] } ``` ##### After ```json title="nx.json" {5} { "plugins": ["@nx/gradle/plugin-v1"] } ``` ### `change-ciTargetName-to-ciTestTargetName` **Version**: 21.0.0-beta.13 Change @nx/gradle option from ciTargetName to ciTestTargetName #### Change @nx/gradle plugin option ciTargetName to ciTestTargetName Change @nx/gradle plugin option ciTargetName to ciTestTargetName in nx.json #### Sample Code Changes ##### Before ```json title="nx.json" { "plugins": [ "plugin": "@nx/gradle", "options": { "ciTargetName": "ci" } ] } ``` ##### After ```json title="nx.json" {5} { "plugins": [ "plugin": "@nx/gradle", "options": { "ciTestTargetName": "ci" } ] } ``` --- ## Nx with Java Nx brings caching, affected-only builds, and a project graph to your Java monorepo, working with both Gradle and Maven build systems. Whether you're working with Spring Boot, Micronaut, Quarkus, or any other Java framework, Nx helps you build faster and more efficiently. ## Requirements | Dependency | Supported Versions | | ---------- | ------------------ | | Java | >= 17 | Using older Java versions is unsupported and may lead to issues. If you need support for an older version, please create an issue on [GitHub](https://github.com/nrwl/nx). ## Quick start ### Install Nx You can install Nx globally. Depending on your package manager, use one of the following commands: {% tabs syncKey="package-manager" %} {% tabitem label="npm" %} ```shell npm add --global nx@latest ``` {% /tabitem %} {% tabitem label="Homebrew (macOS, Linux)" %} ```shell brew install nx ``` {% /tabitem %} {% tabitem label="Chocolatey (Windows)" %} ```shell choco install nx ``` {% /tabitem %} {% tabitem label="apt (Ubuntu)" %} ```shell sudo add-apt-repository ppa:nrwl/nx sudo apt update sudo apt install nx ``` {% /tabitem %} {% /tabs %} ### Add Nx to your Java project In any Gradle or Maven project, run the following command to add Nx: ```shell nx init ``` Nx will automatically detect your build tool and you can then add the appropriate plugin (see Build System Support below). ## Build system support Nx offers dedicated plugins for the two most popular Java build tools: - **[@nx/gradle](/docs/technologies/java/gradle/introduction)** - For projects using Gradle - **[@nx/maven](/docs/technologies/java/maven/introduction)** - For projects using Maven (experimental, requires Nx 22+) Add the plugin for your build system: ```shell nx add @nx/gradle # or nx add @nx/maven ``` These Nx plugins for Gradle and Maven register your Gradle or Maven projects in your Nx workspace, and allow their tasks to be run through Nx. Nx effortlessly makes your [CI faster](/docs/kb/setup-ci). For a hands-on tutorial, check out the [Gradle tutorial](/docs/kb/gradle-tutorial). ## What Nx adds to your Java workspace Nx enhances your Java development workflow with: - **[Smart Caching](/docs/features/cache-task-results)** - Cache build and test results to avoid redundant work - **[Distributed Task Execution](/docs/features/ci-features/distribute-task-execution)** - Run tasks in parallel across multiple machines in CI - **[Affected Commands](/docs/features/ci-features/affected)** - Only build and test what changed - **[Interactive Graph](/docs/features/explore-graph)** - Visualize your project dependencies - **[CI Optimization](/docs/kb/setup-ci)** - Make your CI pipeline dramatically faster ## Set up CI for your Java monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## Maven Get started with [Nx with Maven](/docs/technologies/java/maven/introduction), or browse the topics below. {% index_page_cards path="technologies/java/maven" /%} --- ## @nx/maven Executors The @nx/maven plugin provides various executors to help you create and configure maven projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/maven introduction](/docs/technologies/java/maven/introduction). ### `maven` The Maven executor is used to run Maven phases and goals. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `__unparsed__` | array | Additional arguments to pass to the Maven command (automatically populated by Nx). | | | `args` | string | The arguments to pass to the Maven command. | | | `goals` | string | The Maven goals to execute (e.g., 'clean:clean', 'compiler:compile'). | | | `phase` | string | The Maven lifecycle phase to execute (e.g., 'compile', 'test', 'package', 'install'). | | --- ## @nx/maven Generators The @nx/maven plugin provides various generators to help you create and configure maven projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/maven introduction](/docs/technologies/java/maven/introduction). ## `ci-workflow` Setup a CI Workflow to run Nx in CI. **Usage:** ```bash nx generate @nx/maven:ci-workflow [options] ``` **Arguments:** ```bash nx generate @nx/maven:ci-workflow [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--ci` | string [**required**] | CI provider. | | ## `init` Initializes @nx/maven in the workspace. **Usage:** ```bash nx generate @nx/maven:init [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--skipFormat` | boolean | Skip formatting files | `false` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/maven: --help ``` --- ## Nx with Maven {% aside type="caution" title="Experimental Plugin" %} The `@nx/maven` plugin is currently experimental. Features and APIs may change. {% /aside %} [Apache Maven](https://maven.apache.org/) is a build tool for Java projects that uses a project object model (POM) to manage builds. Nx adds incremental builds, task caching, and the project graph on top of a Maven monorepo, so a multi-module workspace stays fast as it grows. The `@nx/maven` plugin registers Maven modules as Nx projects so you can [set up Maven with Nx](#setup), [run daily Maven tasks](#local-development), [configure task inference](#configuration), and [run in CI](#set-up-ci-for-your-maven-monorepo). You can use Maven with Nx without the plugin and still get [task caching](/docs/features/cache-task-results), [task orchestration](/docs/features/run-tasks), and the [project graph](/docs/features/explore-graph). ## Requirements | Dependency | Supported Versions | | ---------- | ------------------ | | Java | >= 17 | | Maven | >= 3.6.0 | Using older Java versions is unsupported and may lead to issues. If you need support for an older version, please create an issue on [GitHub](https://github.com/nrwl/nx). ## Setup ### Install Nx You can install Nx globally. Depending on your package manager, use one of the following commands: {% tabs syncKey="package-manager" %} {% tabitem label="Homebrew (macOS, Linux)" %} ```shell brew install nx ``` {% /tabitem %} {% tabitem label="Chocolatey (Windows)" %} ```shell choco install nx ``` {% /tabitem %} {% tabitem label="apt (Ubuntu)" %} ```shell sudo add-apt-repository ppa:nrwl/nx sudo apt update sudo apt install nx ``` {% /tabitem %} {% tabitem label="npm" %} ```shell npm install --global nx@latest ``` > You can use `pnpm`, `yarn`, or `bun` if you prefer as well. {% /tabitem %} {% /tabs %} ### Add Nx to a Maven Workspace If you are in a Maven workspace without Nx, start here. This command adds Nx and configures `@nx/maven`: ```shell nx init ``` Verify that Nx discovered your Maven modules: ```shell nx show projects ``` ```shell nx show project my-app --web ``` Replace `my-app` with a module name from your workspace. ### Add @nx/maven to an existing Nx workspace If you already have an Nx workspace, add the Maven plugin: ```shell nx add @nx/maven ``` Verify the plugin is active by inspecting a module: ```shell nx show project my-app --web ``` Replace `my-app` with a module name from your workspace. ## Local development The plugin infers targets from Maven phases and plugin goals defined in your `pom.xml` files. By default, the target names are the Maven phase names: ```shell nx compile my-module nx test my-module nx package my-module nx verify my-module ``` If you configure a `targetNamePrefix` (for example `mvn-`), use the prefixed targets instead: ```shell nx mvn-test my-module ``` ## Configuration ### How @nx/maven infers tasks The plugin scans your workspace for `pom.xml` files and analyzes the Maven build to create Nx targets for phases and plugin goals. A root `pom.xml` at the workspace root is required so the analyzer can resolve the Maven project tree. To view the inferred tasks for a module, open the [project details view](/docs/features/explore-graph#explore-projects-in-your-workspace) in Nx Console or run: ```shell nx show project my-module --web ``` ### @nx/maven configuration Configure the plugin in the `plugins` array of `nx.json`. ```json // nx.json { "plugins": [ { "plugin": "@nx/maven", "options": { "targetNamePrefix": "mvn-", "verbose": false } } ] } ``` | Option | Type | Default | Description | | ------------------ | ------- | ----------- | ------------------------------------------------------------------------------------------- | | `targetNamePrefix` | string | `undefined` | Prefix all inferred Maven targets to avoid name collisions in polyglot workspaces. | | `verbose` | boolean | `false` | Enable verbose logging for the Maven analyzer. Also settable via `NX_VERBOSE_LOGGING=true`. | ### Exclude or include specific projects Use `include`/`exclude` glob patterns to scope the plugin to specific projects: ```json {% meta="{5-6}" %} // nx.json { "plugins": [ { "plugin": "@nx/maven", "include": ["apps/**/*"], "exclude": ["apps/legacy-app/**/*"], "options": { ... } } ] } ``` There is no per-project disable for `@nx/maven`. To stop inference entirely, remove `@nx/maven` from the `plugins` array in `nx.json`. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter for inferred Maven tasks must use the exact identifier `@nx/maven`. ## Set up CI for your Maven monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). ## What's next {% cardgrid %} {% linkcard title="Java with Nx" description="Learn how Nx supports Maven and Gradle together." href="/docs/technologies/java/introduction" /%} {% linkcard title="Gradle Plugin" description="See the Nx plugin for Gradle." href="/docs/technologies/java/gradle/introduction" /%} {% linkcard title="Set Up CI" description="Configure fast, reliable CI for Nx workspaces." href="/docs/kb/setup-ci" /%} {% linkcard title="Maven Generators Reference" description="Full reference for @nx/maven generators." href="/docs/technologies/java/maven/generators" /%} {% linkcard title="Maven Executors Reference" description="Full reference for @nx/maven executors." href="/docs/technologies/java/maven/executors" /%} {% /cardgrid %} --- ## @nx/maven Migrations For an overview of the plugin and setup instructions, see the [@nx/maven introduction](/docs/technologies/java/maven/introduction). The @nx/maven plugin provides various migrations to help you migrate to newer versions of maven projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.0.x ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.26 Rename imports of `createNodesV2` from `@nx/maven` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/maven` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/maven` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/maven'; ``` ##### After ```ts import { createNodes } from '@nx/maven'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/maven` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ## 22.7.x ### `update-0-0-17` **Version**: 22.7.0-beta.11 Update Maven plugin version from 0.0.16 to 0.0.17 in pom.xml files ## 22.6.x ### `update-0-0-14` **Version**: 22.6.0-beta.1 Update Maven plugin version from 0.0.13 to 0.0.14 in pom.xml files ### `update-0-0-15` **Version**: 22.6.0-beta.12 Update Maven plugin version from 0.0.14 to 0.0.15 in pom.xml files ### `update-0-0-16` **Version**: 22.6.0-beta.14 Update Maven plugin version from 0.0.15 to 0.0.16 in pom.xml files ## 22.5.x ### `update-0-0-13` **Version**: 22.5.0-beta.4 Update Maven plugin version from 0.0.12 to 0.0.13 in pom.xml files ## 22.4.x ### `update-0-0-12` **Version**: 22.4.0-beta.0 Update Maven plugin version from 0.0.11 to 0.0.12 in pom.xml files ## 22.2.x ### `update-0-0-11` **Version**: 22.2.0-beta.4 Update Maven plugin version from 0.0.10 to 0.0.11 in pom.xml files ## 22.1.x ### `update-0.0.8` **Version**: 22.1.0-beta.4 Update Maven plugin version from 0.0.7 to 0.0.8 in pom.xml files ### `update-0.0.9` **Version**: 22.1.0-beta.6 Update Maven plugin version from 0.0.8 to 0.0.9 in pom.xml files ### `update-0-0-10` **Version**: 22.1.0-rc.3 Update Maven plugin version from 0.0.9 to 0.0.10 in pom.xml files --- ## Module Federation Get started with [Nx with Module Federation](/docs/technologies/module-federation/introduction), or browse the topics below. {% index_page_cards path="technologies/module-federation" /%} --- ## Nx with Module Federation Module Federation is an architectural pattern for splitting a frontend into separately built and deployed applications that compose at runtime. A host application loads remote modules exposed by other applications, and they share common dependencies so each library loads once. It's a common way to build micro frontends. For the full model, see the official [Module Federation documentation](https://module-federation.io/guide/start/index.html). The fastest way to try Module Federation with Nx is the React Module Federation template. It sets up a host (`apps/shell`) and two remotes (`apps/shop`, `apps/cart`) federated over Vite with the official [Module Federation](https://module-federation.io) plugins: ```shell npx create-nx-workspace@latest my-workspace --template nrwl/react-mfe-template ``` See the [template details](/docs/templates/react-mfe) for what's inside. ## Why Nx works well with Module Federation Module Federation turns one app into many independently built and served pieces. Nx knows how those pieces relate through the [project graph](/docs/features/explore-graph), so it serves, caches, and scales them as one system. You bring your own Module Federation setup, and Nx orchestrates it. Because Nx reads the topology from the graph, serving, affected rebuilds, and caching stay correct as you add remotes. ### Serve a remote and its host together Each app has a `dev` task. The host's `dev` task is [continuous](/docs/getting-started/tutorials/running-tasks#running-continuous-tasks), so it keeps running, and each remote's `dev` task depends on it: ```jsonc // apps/shop/package.json { "nx": { "targets": { "dev": { "executor": "nx:run-commands", "continuous": true, "dependsOn": ["shell:dev"], "options": { "command": "vite", "cwd": "apps/shop" }, }, }, }, } ``` Because the remote's `dev` task depends on the host's continuous `dev` task, `nx dev shop` serves the `shop` remote and starts the `shell` host alongside it. You develop a remote in isolation while the shell stays running around it. Without Nx, you start the host and each remote in separate terminals and keep them wired together by hand. To bring up several remotes at once, run `nx run-many -t dev -p shop cart`, and the shared `shell:dev` task starts only once. ### Rebuild only the remotes a shared change touches When you change a shared library, Module Federation forces every app that consumes it to redeploy. Because each host and remote is its own project, [`nx affected`](/docs/features/ci-features/affected) computes that exact set from the graph and rebuilds and retests only those apps, while Nx [caches](/docs/features/cache-task-results) the rest. Each app has its own test and e2e targets, so affected verification runs only for the apps a change touches. With [remote caching](/docs/features/ci-features/remote-cache) on [Nx Cloud](/docs/getting-started/nx-cloud), a remote already built by a teammate or in CI is restored instead of rebuilt. ### Scale builds and tests across machines A workspace with many remotes has a lot to build. With Nx Cloud, [Nx Agents](/docs/features/ci-features/distribute-task-execution) distribute those builds and tests across machines, so adding remotes stops adding CI time. Nx reports worst-case CI tracking the single largest remote instead of the sum of all of them. ### Keep shared dependencies in sync Module Federation breaks when two apps load incompatible copies of a shared library, such as two versions of React. The official plugins share libraries as singletons so each one loads once, and Nx workspaces can enforce a single version of each shared dependency, so the host and every remote resolve to the same copy. ## Add Module Federation to an existing workspace In a React workspace, generate a host with the consumer generator and a remote with the provider generator. They wire up the official Module Federation plugins for you: ```shell nx g @nx/react:consumer apps/shell nx g @nx/react:provider apps/shop --consumer=shell ``` The `--consumer` option makes the new remote's `dev` task depend on the host. With another framework, add the official Module Federation plugin to each app's bundler config, then give each app the same `continuous` and `dependsOn` tasks. Nx orchestrates the host and remotes the same way, whatever the framework. For the architecture and task-graph details, see [What is Micro Frontend Architecture?](/docs/kb/micro-frontend-architecture). --- ## @nx/module-federation Migrations For an overview of the plugin and setup instructions, see the [@nx/module-federation introduction](/docs/technologies/module-federation/introduction). The @nx/module-federation plugin provides various migrations to help you migrate to newer versions of module-federation projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.0.x ### `rewrite-module-federation-internal-subpath-imports` **Version**: 23.0.0-beta.25 Rewrites `@nx/module-federation/src/*` subpath imports now that the `./src/*` subpath is no longer exposed by `@nx/module-federation`'s exports map. Named imports/exports of public symbols are routed to `@nx/module-federation` and the rest to the new `@nx/module-federation/internal` entry; `require`, dynamic `import` and `jest.mock` calls reference the whole module and are routed to `@nx/module-federation/internal`. ## 22.6.x ### 22.6.0-package-updates **Version**: 22.6.0-beta.10 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/enhanced` | `^2.1.0` | Updated only | `@module-federation/runtime` | `^2.1.0` | Updated only | `@module-federation/sdk` | `^2.1.0` | Updated only | `@module-federation/node` | `^2.7.21` | Updated only ## 22.2.x ### 22.2.0-package-updates **Version**: 22.2.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/enhanced` | `^0.21.2` | Updated only | `@module-federation/runtime` | `^0.21.2` | Updated only | `@module-federation/sdk` | `^0.21.2` | Updated only | `@module-federation/node` | `^2.7.21` | Updated only ## 21.4.x ### 21.4.0-package-updates **Version**: 21.4.0-beta.8 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/sdk` | `^0.18.0` | Updated only | `@module-federation/enhanced` | `^0.18.0` | Updated only | `@module-federation/runtime` | `^0.18.0` | Updated only | `@module-federation/node` | `^2.7.11` | Updated only ### 21.4.0-http-proxy-middleware-package-updates **Version**: 21.4.0-beta.8 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `http-proxy-middleware` | `^3.0.5` | Updated only ## 21.3.x ### 21.3.0-package-updates **Version**: 21.3.0-beta.8 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/enhanced` | `^0.17.0` | Updated only | `@module-federation/node` | `^2.7.9` | Updated only | `@module-federation/sdk` | `^0.17.0` | Updated only ## 21.2.x ### 21.2.0-package-updates **Version**: 21.2.0-beta.6 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/enhanced` | `^0.15.0` | Updated only | `@module-federation/runtime` | `^0.15.0` | Updated only | `@module-federation/sdk` | `^0.15.0` | Updated only --- ## Node.js Get started with [Nx with Node.js](/docs/technologies/node/introduction), or browse the topics below. {% index_page_cards path="technologies/node" /%} --- ## Express Get started with [Nx with Express](/docs/technologies/node/express/introduction), or browse the topics below. {% index_page_cards path="technologies/node/express" /%} --- ## @nx/express Generators The @nx/express plugin provides various generators to help you create and configure express projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/express introduction](/docs/technologies/node/express/introduction). ## `application` Nx Application Options Schema. **Usage:** ```bash nx generate @nx/express:application [options] ``` **Aliases:** `app` **Arguments:** ```bash nx generate @nx/express:application [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--babelJest` | boolean | Use `babel` instead `ts-jest`. | `false` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--frontendProject` | string | Frontend project that needs to access this application. This sets up proxy configuration. | | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--keepExistingVersions` | boolean | Keep existing dependencies versions | `true` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | The name of the application. | | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--swcJest` | boolean | Use `@swc/jest` instead `ts-jest` for faster test compilation. | `false` | | `--tags` | string | Add tags to the application (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/express: --help ``` --- ## Nx with Express [Express](https://expressjs.com/) is a mature, minimal, and an open source web framework for making web applications and apis. Nx scales your Express monorepo, so you can grow your APIs and shared libraries without losing fast, reliable builds. ## Requirements The `@nx/express` plugin supports the following package versions. | Package | Supported Versions | | --------- | ------------------ | | `express` | ^4.0.0 \|\| ^5.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Create a new workspace Start from the [Express template](/docs/templates/express-api) to scaffold a ready-to-run Express API: ```shell npx create-nx-workspace@latest my-workspace --template nrwl/express-api-template ``` ## Setting up @nx/express ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/express` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/express` by running the following command: ```shell nx add @nx/express ``` This will install the correct version of `@nx/express`. ## Guides - [Set Up Application Proxies](/docs/kb/application-proxies) - [Wait For Tasks To Finish](/docs/kb/wait-for-tasks) ## Set up CI for your Express monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/express Migrations For an overview of the plugin and setup instructions, see the [@nx/express introduction](/docs/technologies/node/express/introduction). No migrations found for `@nx/express`. Nx only retains migrations for the last 2 major versions of Nx. It's possible migrations existed in previous versions of `@nx/express`. You can check the previous docs for [Nx v22](https://22.nx.dev/docs) or [Nx v21](https://21.nx.dev/docs). --- ## @nx/node Generators The @nx/node plugin provides various generators to help you create and configure node projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/node introduction](/docs/technologies/node/introduction). ## `application` Nx Application Options Schema. **Usage:** ```bash nx generate @nx/node:application [options] ``` **Aliases:** `app` **Arguments:** ```bash nx generate @nx/node:application [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--babelJest` | boolean | Use `babel` instead `ts-jest`. | `false` | | `--bundler` | string | Bundler which is used to package the application | `"esbuild"` | | `--docker` | boolean | Add a docker build target | | | `--e2eTestRunner` | string | Test runner to use for end-to-end tests | `"none"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--formatter` | string | The tool to use for code formatting. | | | `--framework` | string | Generate the node application using a framework | `"none"` | | `--frontendProject` | string | Frontend project that needs to access this application. This sets up proxy configuration. | | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--keepExistingVersions` | boolean | Keep existing dependencies versions | `true` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | The name of the application. | | | `--port` | number | The port which the server will be run on | `3000` | | `--rootProject` | boolean | Create node application at the root of the workspace | `false` | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipDockerPlugin` | boolean | Skip the @nx/docker plugin and use the legacy docker build target instead. | `false` | | `--skipFormat` | boolean | Skip formatting files | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--swcJest` | boolean | Use `@swc/jest` instead `ts-jest` for faster test compilation. | | | `--tags` | string | Add tags to the application (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `library` Create a Node Library for an Nx workspace. **Usage:** ```bash nx generate @nx/node:library [options] ``` **Aliases:** `lib` **Arguments:** ```bash nx generate @nx/node:library [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--babelJest` | boolean | Use `babel` instead of `ts-jest`. | `false` | | `--buildable` | boolean | Generate a buildable library. | `true` | | `--compiler` | string | The compiler used by the build and test targets. | `"tsc"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--importPath` | string | The library name used to import it, like `@myorg/my-awesome-lib`. Must be a valid npm name. | | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | Library name | | | `--publishable` | boolean | Create a publishable library. | | | `--rootDir` | string | Sets the `rootDir` for TypeScript compilation. When not defined, it uses the project's root property, or `srcRootForCompilationRoot` if it is defined. | | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--simpleModuleName` | boolean | Keep the module name simple. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipTsConfig` | boolean | Do not update `tsconfig.base.json` for development experience. | `false` | | `--strict` | boolean | Whether to enable tsconfig strict mode or not. | `false` | | `--tags` | string | Add tags to the library (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `setup-docker` Nx Node Docker Options Schema. **Usage:** ```bash nx generate @nx/node:setup-docker [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--buildTargetName` | string | The name of the build target | `"build"` | | `--outputPath` | string | The output path for the node application | | | `--project` | string | The name of the project | | | `--skipDockerPlugin` | boolean | Skip the @nx/docker plugin and use the legacy docker build target instead. | `false` | | `--targetName` | string | The name of the target to create | `"docker:build"` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/node: --help ``` --- ## Guides {% index_page_cards path="technologies/node/guides" /%} --- ## Nx with Node.js [Node.js](https://nodejs.org/) is a JavaScript runtime for building servers, CLIs, and backend services. Nx scales your Node monorepo with caching, distributed task execution, and affected-only builds. The Node plugin contains generators and executors to manage Node applications within an Nx workspace. ## Requirements Below is a reference table that matches the most recent major versions of Nx to the versions of Node.js that they officially support, and are tested against. The Nx policy is to support the LTS versions (i.e. actively maintained even numbered versions) of Node.js, but we will only remove support for older versions in a major version of Nx to avoid unexpected disruption. We may add support for newer LTS versions in a minor version of Nx as long as it would not break existing projects. {% aside type="note" title="Other Node.js versions" %} Other versions of Node.js **may** still work without issue for these versions of Nx. Those include versions which are already EOL, or odd version numbers (e.g. 23), which Node.js actively discourages using in production. {% /aside %} {% aside type="note" title="Node 26" %} Node 26 is on the Current release track and enters LTS in October 2026. Nx tests against it in CI and supports it today. {% /aside %} | Nx Version | Node Version | | -------------- | ------------------------------ | | 23.x (current) | 26.x, 24.x, ^22.12.0 | | 22.x | 26.x, 24.x, ^22.12.0, ^20.19.0 | | 21.x | 24.x, ^22.12.0, ^20.19.0 | | 20.x | 22.x, 20.x, 18.x | We intentionally do not include an `"engines"` field in the `package.json` file for Nx in order to allow for user flexibility, but this table should be considered the official compatibility matrix. This table will be updated from time to time to reflect the latest versions of Node.js that are supported. If you encounter issues with Nx, please make sure you are using a supported version of Node.js before filing an issue. ## Setting up @nx/node ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/node` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/node` by running the following command: ```shell nx add @nx/node ``` This will install the correct version of `@nx/node`. ## Using the @nx/node plugin ### Creating applications You can add a new application with the following: ```shell nx g @nx/node:application apps/my-new-app ``` You can run your application with `nx serve my-new-app`, which starts it in watch mode. ### Creating libraries Node libraries are a good way to separate features within your organization. To create a Node library run the following command: ```shell nx g @nx/node:lib libs/my-new-lib # If you want the library to be buildable or publishable to npm nx g @nx/node:lib libs/my-new-lib --buildable nx g @nx/node:lib libs/my-new-lib \ --publishable \ --importPath=@myorg/my-new-lib ``` ## Using node ### Testing projects You can run unit tests with: ```shell nx test my-new-app nx test my-new-lib ``` Replace `my-new-app` with the name or your project. This command works for both applications and libraries. ### Building projects Node applications can be build with: ```shell nx build my-new-app ``` And if you generated a library with `--buildable`, then you can build a library as well: ```shell nx build my-new-lib ``` The output is in the `dist` folder. You can customize the output folder by setting `outputPath` in the project's `project.json` file. ### Application proxies Generating Node applications has an option to configure other projects in the workspace to proxy API requests. This can be done by passing the `--frontendProject` with the project name you wish to enable proxy support for. ```shell nx g @nx/node:application apps/my-new-app \ --frontendProject my-react-app ``` ### Debugging #### VSCode integration When generating Node applications, Nx automatically creates a VSCode debugging configuration: - **Automatic setup**: A `.vscode/launch.json` file is created with pre-configured debugging settings. - **Smart port allocation**: Debug ports are automatically assigned starting from 9229, preventing conflicts between multiple applications. - **Source map support**: Webpack configurations include `devtoolModuleFilenameTemplate` for accurate source mapping. - **Multi-format support**: Supports debugging `.js`, `.mjs`, and `.cjs` output files. To debug your Node application in VSCode: 1. Open your workspace in VSCode. 2. Set breakpoints in your TypeScript source files. 3. Go to the Debug panel `(Ctrl+Shift+D)`. 4. Select "Debug `{your-app-name}` with Nx" from the dropdown. 5. Click the play button or press `F5`. The debugger will automatically: - Start your application with `nx serve`. - Attach to the Node.js debugger on the allocated port. - Map compiled JavaScript back to your TypeScript source files. #### Manual debugging For advanced debugging scenarios, you can manually configure the debug port by setting the port option in the `serve` target in the project.json, or by running the serve command with `--port `. For additional information on Node.js debugging, see the [Node.js debugging getting started guide](https://nodejs.org/en/docs/guides/debugging-getting-started/#inspector-clients). ## More documentation - [Using Cypress](/docs/technologies/test-tools/cypress/introduction) - [Using Jest](/docs/technologies/test-tools/jest/introduction) ## Set up CI for your Node.js monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/node Migrations For an overview of the plugin and setup instructions, see the [@nx/node introduction](/docs/technologies/node/introduction). The @nx/node plugin provides various migrations to help you migrate to newer versions of node projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.0.x ### `rewrite-node-internal-subpath-imports` **Version**: 23.0.0-beta.25 Rewrites `@nx/node/src/*` subpath imports now that the `./src/*` subpath is no longer exposed by `@nx/node`'s exports map. Named imports/exports of public symbols are routed to `@nx/node` and the rest to the new `@nx/node/internal` entry; `require`, dynamic `import` and `jest.mock` calls reference the whole module and are routed to `@nx/node/internal`. ## 22.6.x ### 22.6.0-package-updates **Version**: 22.6.0-beta.10 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `koa` | `^3.1.2` | Updated only ## 22.0.x ### 22.0.2-package-updates **Version**: 22.0.2-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `koa` | `^3.0.3` | Updated only | `@types/koa` | `^3.0.0` | Updated only --- ## NestJS Get started with [Nx with Nest.js](/docs/technologies/node/nest/introduction), or browse the topics below. {% index_page_cards path="technologies/node/nest" /%} --- ## @nx/nest Generators The @nx/nest plugin provides various generators to help you create and configure nest projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/nest introduction](/docs/technologies/node/nest/introduction). ## `application` Nx Application Options Schema. **Usage:** ```bash nx generate @nx/nest:application [options] ``` **Aliases:** `app` **Arguments:** ```bash nx generate @nx/nest:application [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--e2eTestRunner` | string | Test runner to use for end to end (e2e) tests | `"jest"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--formatter` | string | The tool to use for code formatting. | | | `--frontendProject` | string | Frontend project that needs to access this application. This sets up proxy configuration. | | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | The name of the application. | | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--strict` | boolean | Adds strictNullChecks, noImplicitAny, strictBindCallApply, forceConsistentCasingInFileNames and noFallthroughCasesInSwitch to tsconfig. | `false` | | `--tags` | string | Add tags to the application (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `class` Nest Class Options Schema. **Usage:** ```bash nx generate @nx/nest:class [options] ``` **Arguments:** ```bash nx generate @nx/nest:class [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--language` | string | Nest class language. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"jest"` | ## `controller` Nest Controller Options Schema. **Usage:** ```bash nx generate @nx/nest:controller [options] ``` **Arguments:** ```bash nx generate @nx/nest:controller [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--language` | string | Nest controller language. | | | `--module` | string | Allows specification of the declaring module. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipImport` | boolean | Flag to skip the module import. | `false` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"jest"` | ## `decorator` Nest Decorator Options Schema. **Usage:** ```bash nx generate @nx/nest:decorator [options] ``` **Arguments:** ```bash nx generate @nx/nest:decorator [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--language` | string | Nest decorator language. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | ## `filter` Nest Filter Options Schema. **Usage:** ```bash nx generate @nx/nest:filter [options] ``` **Arguments:** ```bash nx generate @nx/nest:filter [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--language` | string | Nest filter language. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"jest"` | ## `gateway` Nest Gateway Options Schema. **Usage:** ```bash nx generate @nx/nest:gateway [options] ``` **Arguments:** ```bash nx generate @nx/nest:gateway [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--language` | string | Nest gateway language. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"jest"` | ## `guard` Nest Guard Options Schema. **Usage:** ```bash nx generate @nx/nest:guard [options] ``` **Arguments:** ```bash nx generate @nx/nest:guard [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--language` | string | Nest guard language. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"jest"` | ## `interceptor` Nest Interceptor Options Schema. **Usage:** ```bash nx generate @nx/nest:interceptor [options] ``` **Arguments:** ```bash nx generate @nx/nest:interceptor [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--language` | string | Nest interceptor language. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"jest"` | ## `interface` Nest Interface Options Schema. **Usage:** ```bash nx generate @nx/nest:interface [options] ``` **Arguments:** ```bash nx generate @nx/nest:interface [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--skipFormat` | boolean | Skip formatting files. | `false` | ## `library` Create a NestJS Library for Nx. **Usage:** ```bash nx generate @nx/nest:library [options] ``` **Aliases:** `lib` **Arguments:** ```bash nx generate @nx/nest:library [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--buildable` | boolean | Generate a buildable library. | `false` | | `--controller` | boolean | Include a controller with the library. | `false` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--global` | boolean | Add the Global decorator to the generated module. | `false` | | `--importPath` | string | The library name used to import it, like @myorg/my-awesome-lib. Must be a valid npm name. | | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | Library name. | | | `--publishable` | boolean | Create a publishable library. | | | `--service` | boolean | Include a service with the library. | `false` | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--skipTsConfig` | boolean | Do not update tsconfig.base.json for development experience. | `false` | | `--strict` | boolean | Whether to enable tsconfig strict mode or not. | `true` | | `--tags` | string | Add tags to the library (used for linting). | | | `--target` | string | The ES target, Nest suggest using es2021 or higher. | `"es2021"` | | `--testEnvironment` | string | The test environment for the unit test runner. For node applications this should stay as node unless doing DOM testing. | `"node"` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `middleware` Nest Middleware Options Schema. **Usage:** ```bash nx generate @nx/nest:middleware [options] ``` **Arguments:** ```bash nx generate @nx/nest:middleware [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--language` | string | Nest middleware language. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"jest"` | ## `module` Nest Module Options Schema. **Usage:** ```bash nx generate @nx/nest:module [options] ``` **Arguments:** ```bash nx generate @nx/nest:module [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--language` | string | Nest module language. | | | `--module` | string | The path to import the module. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipImport` | boolean | Flag to skip the module import. | `false` | ## `pipe` Nest Pipe Options Schema. **Usage:** ```bash nx generate @nx/nest:pipe [options] ``` **Arguments:** ```bash nx generate @nx/nest:pipe [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--language` | string | Nest pipe language. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"jest"` | ## `provider` Nest Provider Options Schema. **Usage:** ```bash nx generate @nx/nest:provider [options] ``` **Arguments:** ```bash nx generate @nx/nest:provider [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--language` | string | Nest provider language. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"jest"` | ## `resolver` Nest Resolver Options Schema. **Usage:** ```bash nx generate @nx/nest:resolver [options] ``` **Arguments:** ```bash nx generate @nx/nest:resolver [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--language` | string | Nest resolver language. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"jest"` | ## `resource` Nest Resource Options Schema. **Usage:** ```bash nx generate @nx/nest:resource [options] ``` **Arguments:** ```bash nx generate @nx/nest:resource [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--crud` | boolean | When true, CRUD entry points are generated. | `true` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipImport` | boolean | Flag to skip the module import. | `false` | | `--type` | string | The transport layer. | `"rest"` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"jest"` | ## `service` Nest Service Options Schema. **Usage:** ```bash nx generate @nx/nest:service [options] ``` **Arguments:** ```bash nx generate @nx/nest:service [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--language` | string | Nest service language. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"jest"` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/nest: --help ``` --- ## Nx with Nest.js Nest.js is a framework designed for building scalable server-side applications, and Nx gives you the tooling to run a Nest.js monorepo with caching, affected-only builds, and code generators. In many ways, Nest is familiar to Angular developers: - It has excellent TypeScript support. - Its dependency injection system is similar to the one in Angular. - It emphasises testability. - Its configuration APIs are similar to Angular as well. Many conventions and best practices used in Angular applications can be also be used in Nest. ## Requirements The `@nx/nest` plugin supports the following NestJS package versions. | Package | Supported Versions | | -------------- | -------------------- | | `@nestjs/core` | ^10.0.0 \|\| ^11.0.0 | The plugin detects the installed `@nestjs/core` major and routes the rest of the `@nestjs/*` family (plus `rxjs` and `reflect-metadata`) to versions that pair with it. Fresh installs default to NestJS v11. | Nx Version | Default NestJS Version | | -------------- | ---------------------- | | 23.x (current) | ^11.0.0 | | 22.x | ^11.0.0 | | 21.x | ^11.0.0 | | 20.x | ^10.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setting up @nx/nest ### Generating a new workspace Start from the [NestJS template](/docs/templates/nestjs) to scaffold a ready-to-run NestJS API: ```shell npx create-nx-workspace@latest my-workspace --template nrwl/nestjs-template ``` ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/nest` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/nest` by running the following command: ```shell nx add @nx/nest ``` This will install the correct version of `@nx/nest`. ### Create applications You can add a new Nest application with the following command: ```shell nx g @nx/nest:app apps/my-nest-app ``` #### Application proxies Generating Nest applications has an option to configure other projects in the workspace to proxy API requests. This can be done by passing the `--frontendProject` with the project name you wish to enable proxy support for. ```shell nx g @nx/nest:app apps/my-nest-app --frontendProject my-angular-app ``` ### Create libraries You can add a new Nest library with the following command: ```shell nx g @nx/nest:lib libs/my-nest-lib ``` To make the library `buildable`, use the following command: ```shell nx g @nx/nest:lib libs/my-nest-lib --buildable ``` To make the library `publishable`, use the following command: ```shell nx g @nx/nest:lib libs/my-nest-lib --publishable --importPath=@my-workspace/my-nest-lib ``` > Read more about [building and publishing libraries here](/docs/kb/buildable-and-publishable-libraries). ### Nest generators The Nest plugin for Nx extends the generators provided by Nest. Any commands that can be used with the Nest CLI can also be used with the `nx` command. The `--project` flag should be used for all Nest generators. > `--project` is used to infer the root of the project where the generators will generate the files. ## Using nest ### Build You can build an application with the following command: ```shell nx build my-nest-app ``` This applies to `buildable` libraries as well ```shell nx build my-nest-lib ``` #### Waiting for other builds Setting the `waitUntilTargets` option with an array of projects (with the following format: `"project:target"`) will execute those commands before serving the Nest application. ### Serve You can serve an application with the following command: ```shell nx serve my-nest-app ``` The `serve` command runs the `build` target, and executes the application. By default, the serve command will run in `watch` mode. This allows code to be changed, and the Nest application to be rebuilt automatically. #### Debugging Nest applications also have the `inspect` flag set, so you can attach your debugger to the running instance. ##### VSCode integration When generating Nest applications, Nx automatically creates a VSCode debugging configuration: - **Automatic setup**: A `.vscode/launch.json` file is created with pre-configured debugging settings. - **Smart port allocation**: Debug ports are automatically assigned starting from 9229, preventing conflicts between multiple applications. - **Source map support**: Webpack configurations include `devtoolModuleFilenameTemplate` for accurate source mapping. - **Multi-format support**: Supports debugging `.js`, `.mjs`, and `.cjs` output files. To debug your Nest application in VSCode: 1. Open your workspace in VSCode. 2. Set breakpoints in your TypeScript source files. 3. Go to the Debug panel `(Ctrl+Shift+D)`. 4. Select "Debug `{your-app-name}` with Nx" from the dropdown. 5. Click the play button or press `F5`. The debugger will automatically: - Start your application with `nx serve`. - Attach to the Node.js debugger on the allocated port. - Map compiled JavaScript back to your TypeScript source files. ##### Manual debugging For advanced debugging scenarios, you can manually configure the debug port by setting the port option in the `serve` target in the `project.json`, or by running the serve command with `--port `. For additional information on Node.js debugging, see the [Node.js debugging getting started guide](https://nodejs.org/en/docs/guides/debugging-getting-started/#inspector-clients). ### Lint You can lint an application with the following command: ```shell nx lint my-nest-app ``` You can lint a library with the following command: ```shell nx lint my-nest-lib ``` ### Unit test You can run unit test for an application with the following command: ```shell nx test my-nest-app ``` You can run unit test for a library with the following command: ```shell nx test my-nest-lib ``` ## Using CLI plugins Nest supports the use of various CLI plugins to enhance the development experience. Plugins can be configured via **transformers** property in NxWebpackPlugin. As an example, to set up a [Swagger plugin](https://docs.nestjs.com/openapi/cli-plugin), modify the Nest application's Webpack configuration as follows: ```javascript const { NxWebpackPlugin } = require('@nx/webpack'); module.exports = { // ... plugins: [ new NxWebpackPlugin({ // ... transformers: [ { name: '@nestjs/swagger/plugin', options: { dtoFileNameSuffix: ['.dto.ts', '.entity.ts'], }, }, ], }), ], }; ``` ## Deployment Ensuring a smooth and reliable deployment of a Nest.js application in a production environment requires careful planning and the right strategy. Depending on your specific needs and infrastructure, you can choose from several deployment approaches. Below are four commonly used methods: 1. **Using Docker:** Create a Dockerfile that specifies the application's environment and dependencies. Build a Docker image and optionally push it to a container registry. Deploy and run the Docker container on the server. Use the `@nx/node:setup-docker` generator to set up Docker for your project. 2. **Installing Dependencies on the Server:** Transfer the build artifacts to the server, install all dependencies using the package manager of your choice, and start the application. Ensure that [NxAppWebpackPlugin](/docs/kb/webpack-plugins#nxappwebpackplugin) is configured with `generatePackageJson: true` so that the build artifacts directory includes `package.json` and `package-lock.json` (or the equivalent files for other package managers). 3. **Transferring Pre-installed Dependencies:** Install dependencies during the build process, and transfer the build artifacts along with the `node_modules` directory to the server. Typically, the artifacts are archived for faster transfer and then unarchived on the server. 4. **Bundling Dependencies:** By default, Nx/Nest creates a setup that externalizes all dependencies, meaning they are not included in the bundle. This behavior can be adjusted using the `externalDependencies` parameter in the webpack configuration with [NxAppWebpackPlugin](/docs/kb/webpack-plugins#nxappwebpackplugin). After bundling, transfer the package to the server and start the application. {% aside type="note" title="Bundling Dependencies" %} Bundling dependencies is typically not recommended for Node applications. {% /aside %} ## More documentation - [Using Jest](/docs/technologies/test-tools/jest/introduction) ## Set up CI for your Nest.js monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/nest Migrations For an overview of the plugin and setup instructions, see the [@nx/nest introduction](/docs/technologies/node/nest/introduction). The @nx/nest plugin provides various migrations to help you migrate to newer versions of nest projects within your Nx workspace. Below is a complete reference for all available migrations. ## 21.2.x ### 21.2.0-beta.2-package-updates **Version**: 21.2.0-beta.2 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@nestjs/common` | `^11.0.0` | Updated only | `@nestjs/core` | `^11.0.0` | Updated only | `@nestjs/platform-express` | `^11.0.0` | Updated only | `@nestjs/testing` | `^11.0.0` | Updated only | `@nestjs/schematics` | `^11.0.0` | Updated only | `reflect-metadata` | `^0.2.0` | Updated only --- ## Oxlint Get started with [Nx with Oxlint](/docs/technologies/oxlint/introduction), or browse the topics below. {% index_page_cards path="technologies/oxlint" /%} --- ## @nx/oxlint Generators The @nx/oxlint plugin provides various generators to help you create and configure oxlint projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/oxlint introduction](/docs/technologies/oxlint/introduction). ## `configuration` Register `@nx/oxlint` and enable this project’s Oxlint plugins. **Usage:** ```bash nx generate @nx/oxlint:configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string [**required**] | The name of the project. | | | `--keepExistingVersions` | boolean | Keep existing dependency versions when possible. | `false` | | `--plugins` | array | Oxlint plugins to enable for this project, such as `react` or `jsx-a11y`. | `[]` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/oxlint: --help ``` --- ## Nx with Oxlint [Oxlint](https://oxc.rs/docs/guide/usage/linter/) is a linter written in Rust. The `@nx/oxlint` plugin runs it as a cacheable Nx task, so a change re-lints only the projects it affects. {% aside type="caution" title="Experimental" %} `@nx/oxlint` is experimental. The module boundaries bridge depends on the Oxlint JavaScript plugin API, which Oxlint excludes from its semantic versioning policy and can change in any release. {% /aside %} ## Set up ```shell nx add @nx/oxlint ``` This installs `oxlint` if it is not already installed, registers `@nx/oxlint`, and creates a root `.oxlintrc.json` if the workspace has no Oxlint config. After this, the application and library generators follow the workspace. Run `nx g @nx/react:app` with no `--linter` and Nx sets up Oxlint, because the workspace now has it. An ESLint workspace gets ESLint, and a workspace part-way through a migration gets Oxlint. Nx asks only when the workspace has no linter at all, and sets up no linter when the prompt cannot run, such as in CI. Pass `--linter` to choose for a single project. `@nx/oxlint` requires Oxlint 1.70.0 or later. The generators stop with an error on anything older. ## Inferred tasks `@nx/oxlint` infers a lint task for projects governed by an Oxlint config. It recognizes the four config files Oxlint discovers on its own: - `.oxlintrc.json` - `.oxlintrc.jsonc` - `oxlint.config.ts` - `oxlint.config.mts` A project gets a task when it contains files Oxlint can lint. The workspace root also needs a `package.json` and a `src` or `lib` directory, so a standalone workspace that keeps its sources elsewhere gets no root task. Documentation-only and non-JavaScript projects get no target, and giving one a config of its own does not change that: Oxlint reports `No files found to lint` where it has nothing to read. A project's task skips the roots of any projects nested inside it, so Oxlint lints each file once, under the project that owns it. View the inferred task with: ```shell nx show project my-project --web ``` ### Task inputs Inferred tasks hash the files Oxlint can lint, so editing a README or a JSON file in the project does not re-lint it. They also declare the Oxlint config, every config reachable through `extends`, `.eslintignore` files in the project's ancestor directories, local `jsPlugins` files, and any `tsconfig.json` referenced from outside the project root. Changing any of them invalidates the cache. The task does not use the `default` named input, so what re-lints does not depend on how the workspace defines it. Dependencies are hashed only when the config registers the [module boundaries](#module-boundaries) bridge, because that is the one rule that looks across projects. A `jsPlugins` package or workspace project becomes a dependency of every project linted under that config, so upgrading or editing the plugin invalidates the cache and marks those projects as affected. ## Editor setup Install the official [Oxc extension](https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode) (`oxc.oxc-vscode`) for VS Code and Cursor. `nx add @nx/oxlint` appends it to `.vscode/extensions.json` when that file already exists. Oxc also ships extensions for Zed, JetBrains IDEs, and Neovim. Any editor with LSP support can use `oxlint --lsp` as its language server. ## Config file format Oxlint walks up from each file to the nearest config, and that config replaces the one above it rather than merging into it. A project config needs an `extends` pointing back at the config above it, or that config's `categories` and `rules` stop applying to the project. The generators write that `extends` whenever they create a project config, targeting the closest ancestor config rather than the workspace root. A config you wrote yourself keeps whatever `extends` it already has. Two configs in one directory is an error, not a precedence, so pick one format per directory. Prefer `.oxlintrc.json`: - `oxlint.config.ts` only works on a Node runtime that can run TypeScript directly, which means Node 22.18 or later. Node 20 cannot, even though Oxlint's error message suggests otherwise, and Oxlint's standalone binary cannot either. - Only JSON configs get their `extends` chain tracked as task inputs. Nx can't read a TypeScript config statically. - Framework generators can only register Oxlint plugins into a JSON config. When the nearest config above a project is TypeScript they can still update a project that already has its own `.oxlintrc.json`, but they can't create one. `nx g @nx/react:app --linter=oxlint` warns and leaves the `plugins` array to you. A TypeScript config further up doesn't block anything, because the nearest one is what Oxlint resolves to. ## Task naming Oxlint is designed to run alongside ESLint during a migration, so the plugin claims the first available name from: 1. `lint` 2. `oxlint` 3. `oxlint:lint` 4. `oxlint-lint` A workspace with no other linter gets `lint`. A workspace where ESLint already owns `lint` gets `oxlint`. ## Customizing the task To change how one project is linted, add arguments to the inferred target in its `project.json`. Use `args` rather than `command`: replacing the command drops the inferred `cache` and `inputs` with it, so the task stops being cached. ```json { "targets": { "lint": { "options": { "args": ["--type-aware"] } } } } ``` The plugin itself takes one option, `targetName`. `nx add @nx/oxlint` already writes it into `nx.json` with the name it picked from the list above, so edit that entry when you want a different one: ```json { "plugins": [ { "plugin": "@nx/oxlint", "options": { "targetName": "oxlint" } } ] } ``` Which files Oxlint lints is not an Nx setting. Oxlint selects those itself. Narrow them with `ignorePatterns` in `.oxlintrc.json`, or scope individual rules with `overrides`. ## Type-aware linting The Oxlint type-aware rules need the `oxlint-tsgolint` package, which bundles its own TypeScript compiler. Your workspace's TypeScript version doesn't matter. Turn them on for a single run with `nx lint my-app --type-aware`, which Nx forwards to the underlying command. To make it permanent, override the target as shown in [Customizing the task](#customizing-the-task). ## Module boundaries `@nx/oxlint` exposes the project-graph-aware `enforce-module-boundaries` rule as an Oxlint JavaScript plugin. Register the bridge in `.oxlintrc.json`: ```json { "jsPlugins": ["@nx/oxlint/boundaries-plugin"], "rules": { "@nx/enforce-module-boundaries": [ "error", { "depConstraints": [] } ] } } ``` {% aside type="caution" title="A workspace plugin can't depend on what it lints" %} A plugin that lives in your workspace must not depend on a project it lints. That dependency is circular — the plugin is already a dependency of everything linted under its config — so a pipeline such as `dependsOn: ["^build"]` fails with a circular-dependency error. Set `NX_IGNORE_CYCLES=true` to run it anyway. {% /aside %} `depConstraints` takes the same syntax as it does under ESLint. To tag projects and write constraints, see [enforce module boundaries](/docs/features/enforce-module-boundaries). {% aside type="note" title="Dependency checks aren't available" %} Oxlint doesn't lint JSON files, so the `@nx/dependency-checks` rule cannot run on Oxlint. Keep ESLint for that check. {% /aside %} --- ## @nx/oxlint Migrations For an overview of the plugin and setup instructions, see the [@nx/oxlint introduction](/docs/technologies/oxlint/introduction). No migrations found for `@nx/oxlint`. Nx only retains migrations for the last 2 major versions of Nx. It's possible migrations existed in previous versions of `@nx/oxlint`. You can check the previous docs for [Nx v22](https://22.nx.dev/docs) or [Nx v21](https://21.nx.dev/docs). --- ## React Get started with [Nx with React](/docs/technologies/react/introduction), or browse the topics below. {% index_page_cards path="technologies/react" /%} --- ## @nx/react Executors The @nx/react plugin provides various executors to help you create and configure react projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/react introduction](/docs/technologies/react/introduction). ### `module-federation-dev-server` Serve a web application. ### Examples ###### Basic Usage The Module Federation Dev Server will serve a host application and find the remote applications associated with the host and serve them statically also. See an example set up of it below: ```json { "serve": { "executor": "@nx/react:module-federation-dev-server", "configurations": { "production": { "buildTarget": "host:build:production" }, "development": { "buildTarget": "host:build:development" } }, "defaultConfiguration": "development", "options": { "port": 4200, "publicHost": "http://localhost:4200" } } } ``` ###### Serve host with remotes that can be live reloaded The Module Federation Dev Server will serve a host application and find the remote applications associated with the host and serve a set selection with live reloading enabled also. See an example set up of it below: ```json { "serve-with-hmr-remotes": { "executor": "@nx/react:module-federation-dev-server", "configurations": { "production": { "buildTarget": "host:build:production" }, "development": { "buildTarget": "host:build:development" } }, "defaultConfiguration": "development", "options": { "port": 4200, "publicHost": "http://localhost:4200", "devRemotes": [ "remote1", { "remoteName": "remote2", "configuration": "development" } ] } } } ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `allowedHosts` | string | This option allows you to whitelist services that are allowed to access the dev server. | | | `baseHref` | string | Base url for the application being built. | | | `buildTarget` | string | Target which builds the application. | | | `devRemotes` | array | List of Producer (remote) applications to run in development mode (i.e. using serve target). | | | `hmr` | boolean | Enable hot module replacement. | `false` | | `host` | string | Host to listen on. | `"localhost"` | | `isInitialHost` | boolean | Whether the Consumer (host) that is running this executor is the first in the project tree to do so. | `true` | | `liveReload` | boolean | Whether to reload the page on change, using live-reload. | `true` | | `maxWorkers` | number | Number of workers to use for type checking. | | | `memoryLimit` | number | Memory limit for type checking service process in `MB`. | | | `open` | boolean | Open the application in the browser. | `false` | | `parallel` | number | Max number of parallel processes for building static Producers (remotes) | | | `pathToManifestFile` | string | Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic Producer (remote) applications relative to the workspace root. | | | `port` | number | Port to listen on. | `4200` | | `publicHost` | string | Public URL where the application will be served. | | | `skipRemotes` | array | List of Producer (remote) applications to not automatically serve, either statically or in development mode. This will not remove the Producers (remotes) from the `module-federation.config` file, and therefore the application may still try to fetch these Producers (remotes). This option is useful if you have other means for serving the Producer (remote) application(s). **NOTE:** Producers (remotes) that are not in the workspace will be skipped automatically. | | | `ssl` | boolean | Serve using `HTTPS`. | `false` | | `sslCert` | string | SSL certificate to use for serving `HTTPS`. | | | `sslKey` | string | SSL key to use for serving `HTTPS`. | | | `static` | boolean | Whether to use a static file server instead of the webpack-dev-server. This should be used for Producer (remote) applications that are also Consumer (host) applications. | | | `staticRemotesPort` | number | The port at which to serve the file-server for the static Producers (remotes). | | | `watch` | boolean | Watches for changes and rebuilds application. | `true` | ### `module-federation-ssr-dev-server` Serve a SSR Consumer (host) application along with its known Producers (remotes). #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `browserTarget` | string [**required**] | Target which builds the browser application. | | | `serverTarget` | string [**required**] | Target which builds the server application. | | | `devRemotes` | array | List of Producer (remote) applications to run in development mode (i.e. using serve target). | | | `host` | string | Host to listen on. | `"localhost"` | | `isInitialHost` | boolean | Whether the Consumer (host) that is running this executor is the first in the project tree to do so. | `true` | | `pathToManifestFile` | string | Path to a Module Federation manifest file (e.g. `my/path/to/module-federation.manifest.json`) containing the dynamic Producer (remote) applications relative to the workspace root. | | | `port` | number | The port to be set on `process.env.PORT` for use in the server. | `4200` | | `skipRemotes` | array | List of Producer (remote) applications to not automatically serve, either statically or in development mode. | | | `ssl` | boolean | Serve using HTTPS. | `false` | | `sslCert` | string | SSL certificate to use for serving HTTPS. | | | `sslKey` | string | SSL key to use for serving HTTPS. | | | `staticRemotesPort` | number | The port at which to serve the file-server for the static Producers (remotes). | | ### `module-federation-static-server` Serve a Consumer (host) application statically along with its Producers (remotes). #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `serveTarget` | string [**required**] | | | --- ## Expo Get started with [Nx with Expo](/docs/technologies/react/expo/introduction), or browse the topics below. {% index_page_cards path="technologies/react/expo" /%} --- ## @nx/expo Executors The @nx/expo plugin provides various executors to help you create and configure expo projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/expo introduction](/docs/technologies/react/expo/introduction). ### `build` Start an EAS build for your expo project. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `autoSubmit` | boolean | Submit on build complete using the submit profile with the same name as the build profile | `false` | | `autoSubmitWithProfile` | string | Submit on build complete using the submit profile with provided name | | | `buildLoggerLevel` | string | The level of logs to output during the build process. | `"info"` | | `clearCache` | boolean | Clear cache before the build | `false` | | `freezeCredentials` | boolean | Prevent the build from updating credentials in non-interactive mode | `false` | | `interactive` | boolean | Run command in interactive mode | `true` | | `json` | boolean | Enable JSON output, non-JSON messages will be printed to stderr | `false` | | `local` | boolean | Run build locally [experimental] | `false` | | `message` | string | A short message describing the build | | | `output` | string | Output path for local build | | | `platform` | any | The platform to build the app, exaple values: ios, android, all. | | | `profile` | string | Name of the build profile from eas.json. Defaults to "production" if defined in eas.json. | | | `wait` | boolean | Wait for build(s) to complete | `true` | ### `build-list` List all Expo Application Services (EAS) builds for your Expo project. The `build-list` command allows to check the details of your Expo Application Services (EAS) build status. `project.json`: ```json { "name": "mobile", //... "targets": { //... "build-list": { "executor": "@nx/expo:build-list", "options": {} } //... } } ``` ```shell nx run mobile:build-list ``` ### Examples ###### Get Status of Different Platforms The `platform` option allows you to check build status of different platform (e.g. android, ios, all): ```json "build-list": { "executor": "@nx/expo:build-list", "options": { "platform": "ios" } } ``` ###### Get Status Interactively The `interactive` option allows you to specify whether to use interactive mode: ```json "build-list": { "executor": "@nx/expo:build-list", "options": { "interactive": true } } ``` ###### Get Status in JSON Format The `json` option allows you to print the output in JSON format: ```json "build-list": { "executor": "@nx/expo:build-list", "options": { "interactive": false, "json": true } } ``` --- #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `appBuildVersion` | string | App build version of EAS build | | | `appIdentifier` | string | App identifier of EAS build | | | `appVersion` | string | App version of EAS build | | | `buildProfile` | string | Build profile of EAS build | | | `channel` | string | Channel of EAS build | | | `distribution` | any | Distribution of EAS build | | | `gitCommitHash` | string | Git commit hash of EAS build | | | `interactive` | boolean | Run the command in interactive mode. | | | `json` | boolean | Enable JSON output, non-JSON messages will be printed to stderr | | | `limit` | number | Limit of numbers to list EAS builds | | | `platform` | any | The platform to build the app. | | | `runtimeVersion` | string | Runtime version of EAS build | | | `sdkVersion` | string | SDK version of EAS build | | | `status` | any | Status of EAS build | | ### `ensure-symlink` Ensure workspace node_modules is symlink under app's node_modules folder. ### `export` Export the JavaScript and assets for your app using Metro/webpack bundler. `project.json`: ```json { "name": "mobile", //... "targets": { //... "export": { "executor": "@nx/expo:export", "options": { "outputs": ["{options.outputDir}"], "platform": "all", "outputDir": "dist/apps/mobile" }, "dependsOn": ["sync-deps"] } //... } } ``` ```shell nx run mobile:export ``` ### Examples ###### Specify outputDir The `outputDir` option allows you to specify the output directory of your bundle: ```json "export": { "executor": "@nx/expo:export", "outputs": ["{options.outputDir}"], "options": { "platform": "all", "bundler": "metro", "outputDir": "dist/apps/mobile" }, "dependsOn": ["sync-deps"] }, ``` or run command: `nx run mobile:export --outputDir=dist/apps/mobile`. ###### Specify the platform The `platform` option allows you to specify the platform to compile with metro bundler: "ios", "android", "all", and "web". For example, to bundle for web: ```json "export": { "executor": "@nx/expo:export", "outputs": ["{options.outputDir}"], "options": { "platform": "web", "bundler": "metro", "outputDir": "dist/apps/dogs" }, "dependsOn": ["sync-deps"] }, ``` or run command `nx export mobile --platform=web`. ###### Bundle for development The `dev` option allows you to bundle for development environments. ```json "export": { "executor": "@nx/expo:export", "outputs": ["{options.outputDir}"], "options": { "platform": "web", "bundler": "metro", "outputDir": "dist/apps/dogs", "dev": true }, "dependsOn": ["sync-deps"] }, ``` or run command `nx export mobile --dev`. ###### Clear bundle cache The `clear` option allows you to clear bundle cache. ```json "export": { "executor": "@nx/expo:export", "outputs": ["{options.outputDir}"], "options": { "platform": "web", "bundler": "metro", "outputDir": "dist/apps/dogs", "clear": true }, "dependsOn": ["sync-deps"] }, ``` or run command `nx export mobile --clear`. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `platform` | any [**required**] | Choose the platform to compile for | `"all"` | | `clear` | boolean | Clear the bundler cache before exporting | | | `dev` | boolean | Configure static files for developing locally using a non-https server | | | `dumpAssetmap` | boolean | When bundler is metro, whether to dump the asset map for further processing | | | `maxWorkers` | number | When bundler is metro, the maximum number of tasks to allow the bundler to spawn | | | `minify` | boolean | Minify source | | | `outputDir` | string | Relative to workspace root, the directory to export the static files to. Default: dist | | | `sourceMaps` | boolean | When bundler is metro, whether to emit JavaScript source maps | | ### `install` Install a module or other package to a project. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `check` | boolean | Check which installed packages need to be updated | `false` | | `fix` | boolean | Automatically update any invalid package versions | `false` | | `force` | boolean | Force the installation of a package, even if it is already installed | `false` | | `packages` | array | The names of packages to install | `[]` | ### `prebuild` Create native iOS and Android project files for building natively. The `prebuild` command generates native code before a native app can compile. `project.json`: ```json { "name": "mobile", //... "targets": { //... "prebuild": { "executor": "@nx/expo:prebuild", "options": {} } //... } } ``` ```shell nx run mobile:prebuild ``` ### Examples ###### Generate Native Code for Different Platforms The `platform` option allows you to specify the platform to generate native code for (e.g. android, ios, all). ```json "prebuild": { "executor": "@nx/expo:prebuild", "options": { "platform": "android" } } ``` ###### Regenerate Native Code The `clean` option allows you to delete the native folders and regenerate them before apply changes. ```json "prebuild": { "executor": "@nx/expo:prebuild", "options": { "clean": true } } ``` ###### Install NPM Packages and CocoaPods The `install` option allows you to install NPM Packages and CocoaPods. ```json "prebuild": { "executor": "@nx/expo:prebuild", "options": { "install": true } } ``` --- #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `platform` | any [**required**] | Platforms to sync | `"all"` | | `clean` | boolean | Delete the native folders and regenerate them before applying changes | `false` | | `install` | boolean | Installing npm packages and CocoaPods. | `true` | | `template` | string | Project template to clone from. File path pointing to a local tar file or a github repo | | ### `run` Run Expo target options. The `run` command allows you to compile your app locally. `project.json`: ```json { "name": "mobile", //... "targets": { //... "run-ios": { "executor": "@nx/expo:run", "options": { "platform": "ios" } }, "run-android": { "executor": "@nx/expo:run", "options": { "platform": "android" } } //... } } ``` ```shell nx run mobile:run-ios nx run mobile:run-android ``` ### Examples ###### Compile Android with Different Variants The `variant` option allows you to specify the compile Android app with variants defined in `build.gradle` file (e.g. debug, release). ```json "run-android": { "executor": "@nx/expo:run", "options": { "platform": "android", "variant": "release" } } ``` ###### Compile iOS with Different Configurations The `xcodeConfiguration` option allows you to specify Xcode configuration to use (e.g. Debug or Release). ```json "run-ios": { "executor": "@nx/expo:run", "options": { "platform": "ios", "xcodeConfiguration": "Release" } } ``` ###### Run on a device The `device` option allows you to launch your app in a specific device name or UDID. To see all your iOS simulators: run `xcrun simctl list devices available`. To see all your Android emulators, run: `emulator -list-avds`. ```json "run-ios": { "executor": "@nx/expo:run", "options": { "platform": "ios", "device": "iPhone 14" } }, "run-android": { "executor": "@nx/expo:run", "options": { "platform": "android", "device": "Pixel_XL_API_Tiramisu" } } ``` --- #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `platform` | any [**required**] | Platform to run for (ios, android). | `"ios"` | | `buildCache` | boolean | Should use derived data for builds. | | | `bundler` | boolean | Whether to skip starting the Metro bundler. True to start it, false to skip it. | | | `clean` | boolean | Delete the native folders and regenerate them before applying changes | `false` | | `device` | string | Device name or UDID to build the app on. The value is not required if you have a single device connected. | | | `install` | boolean | Installing npm packages and CocoaPods before building. | `true` | | `port` | number | Port to start the Metro bundler on | `8081` | | `scheme` | string | (iOS) Explicitly set the Xcode scheme to use | | | `variant` | string | (Android) Specify your app's build variant (e.g. debug, release). | `"debug"` | | `xcodeConfiguration` | string | (iOS) Xcode configuration to use. Debug or Release | `"Debug"` | ### `serve` Packager Server target options. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `clear` | boolean | Clear the Metro bundler cache | | | `dev` | boolean | Turn development mode on or off | | | `maxWorkers` | number | Maximum number of tasks to allow Metro to spawn | | | `minify` | boolean | Whether or not to minify code | | | `port` | number | Port to start the native Metro bundler on (does not apply to web or tunnel) | `4200` | ### `start` Packager Server target options. `project.json`: ```json { "name": "mobile", //... "targets": { //... "start": { "executor": "@nx/expo:start", "options": { "port": 8081 } } //... } } ``` ```shell nx run mobile:start ``` ### Examples ###### Specify starting on platform The `ios`, `android` and `web` option allows you to start the server on different platforms. Opens your app in Expo Go in a currently running iOS simulator on your computer: ```json "start": { "executor": "@nx/expo:start", "options": { "port": 8081, "ios": true } } ``` or run command `nx start --ios`. Opens your app in Expo Go on a connected Android device ```json "start": { "executor": "@nx/expo:start", "options": { "port": 8081, "android": true } } ``` or run command `nx start --android`. Opens your app in a web browser: ```json "start": { "executor": "@nx/expo:start", "options": { "port": 8081, "web": true } } ``` or run command `nx start --web`. ###### Specify the host The `host` option allows you to specify the type of host to use. `lan` uses the local network; `tunnel` ues any network by tunnel through ngrok; `localhost` connects to the dev server over localhost. ```json "start": { "executor": "@nx/expo:start", "options": { "port": 8081, "host": "localhost" } } ``` ###### Starts the server with cache reset The `clear` option allows you to remove Metro bundler cache. ```json "start": { "executor": "@nx/expo:start", "options": { "port": 8081, "clear": true } } ``` --- #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `android` | boolean | Opens your app in Expo Go on a connected Android device | | | `clear` | boolean | Clear the Metro bundler cache | | | `dev` | boolean | Turn development mode on or off | | | `devClient` | boolean | Experimental: Starts the bundler for use with the expo-development-client | | | `forceManifestType` | string | Override auto detection of manifest type. | | | `host` | string | lan (default), tunnel, localhost. Type of host to use. lan uses the local network; tunnel ues any network by tunnel through ngrok; localhost connects to the dev server over localhost. | | | `https` | boolean | To start webpack with https or http protocol | | | `ios` | boolean | Opens your app in Expo Go in a currently running iOS simulator on your computer | | | `lan` | boolean | Same as --host lan | | | `localhost` | boolean | Same as --host localhost | | | `maxWorkers` | number | Maximum number of tasks to allow Metro to spawn | | | `minify` | boolean | Whether or not to minify code | | | `offline` | boolean | Allows this command to run while offline | | | `port` | number | Port to start the native Metro bundler on (does not apply to web or tunnel) | `19000` | | `privateKeyPath` | string | Path to private key for code signing. Default: 'private-key.pem' in the same directory as the certificate specified by the expo-updates configuration in app.json. | | | `scheme` | string | Custom URI protocol to use with a development build | | | `tunnel` | boolean | Same as --host tunnel | | | `web` | boolean | Opens your app in a web browser | | ### `submit` Submit app binary to App Store and/or Play Store. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `id` | string | Build ID to submit | | | `interactive` | boolean | Run command in interactive mode | `true` | | `latest` | boolean | Submit the latest build for specified platform | | | `path` | string | Path to the .apk/.aab/.ipa file | | | `platform` | any | The platform to build the app, example values: ios, android, all. | | | `profile` | string | Name of the build profile from eas.json. Defaults to "production" if defined in eas.json. | | | `url` | string | URL to the .apk/.aab/.ipa file, app archive url | | | `wait` | boolean | Wait for build(s) to complete | `true` | ### `sync-deps` Updates package.json with project dependencies. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `all` | boolean | Copy all dependencies and devDependencies from the workspace root package.json. | `false` | | `exclude` | array | An array of npm packages to exclude. | `[]` | | `excludeImplicit` | boolean | This will ignore npm packages from projects listed in implicitDependencies (e.g. backend API projects) | `false` | | `include` | array | An array of additional npm packages to include. | `[]` | ### `update` Start an EAS update for your expo project. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `auto` | boolean | Use the current git branch and commit message for the EAS branch and update message | `false` | | `branch` | string | Branch to publish the update group on | | | `group` | string | Update group to republish | | | `inputDir` | string | Location of the bundle | | | `interactive` | boolean | Run command in interactive mode | `true` | | `json` | boolean | Enable JSON output, non-JSON messages will be printed to stderr | `false` | | `message` | string | A short message describing the update | | | `platform` | any | The platform to build the app, example values: ios, android, all. | `"all"` | | `privateKeyPath` | string | File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory. | | | `republish` | boolean | Republish a previous update within a branch | `false` | | `skipBundler` | boolean | Skip running Expo CLI to bundle the app before publishing | `false` | --- ## @nx/expo Generators The @nx/expo plugin provides various generators to help you create and configure expo projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/expo introduction](/docs/technologies/react/expo/introduction). ## `application` Create an Expo Application for Nx. **Usage:** ```bash nx generate @nx/expo:application [options] ``` **Aliases:** `app` **Arguments:** ```bash nx generate @nx/expo:application [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--displayName` | string | The display name to show in the application. Defaults to name. | | | `--e2eTestRunner` | string | Adds the specified e2e test runner | `"none"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--formatter` | string | The tool to use for code formatting. | | | `--js` | boolean | Generate JavaScript files rather than TypeScript files | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | The name of the application. | | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--tags` | string | Add tags to the application (used for linting) | | | `--unitTestRunner` | string | Test runner to use for unit tests | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `component` Create a Expo Component for Nx. **Usage:** ```bash nx generate @nx/expo:component [options] ``` **Aliases:** `c` **Arguments:** ```bash nx generate @nx/expo:component [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--classComponent` | boolean | Use class components instead of functional component. | `false` | | `--export` | boolean | When true, the component is exported from the project index.ts (if it exists). | `false` | | `--name` | string | The component symbol name. Defaults to the last segment of the file path. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipTests` | boolean | When true, does not create "spec.ts" test files for the new component. | `false` | ## `convert-to-inferred` Convert existing Expo project(s) using `@nx/expo:*` executors to use `@nx/expo/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target. **Usage:** ```bash nx generate @nx/expo:convert-to-inferred [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/expo:*` executors to use `@nx/expo/plugin`. | | | `--skipFormat` | boolean | Whether to format files at the end of the migration. | `false` | ## `library` Create a Expo Library for Nx. **Usage:** ```bash nx generate @nx/expo:library [options] ``` **Aliases:** `lib` **Arguments:** ```bash nx generate @nx/expo:library [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--buildable` | boolean | Generate a buildable library. | `false` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--importPath` | string | The library name used to import it, like @myorg/my-awesome-lib | | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | Library name | | | `--publishable` | boolean | Create a publishable library. | | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--skipTsConfig` | boolean | Do not update tsconfig.json for development experience. | `false` | | `--strict` | boolean | Whether to enable tsconfig strict mode or not. | `true` | | `--tags` | string | Add tags to the library (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/expo: --help ``` --- ## Nx with Expo Expo is an open-source framework for apps that run natively on Android, iOS, and the web. In an Expo monorepo, Nx lets you share code across native and web targets while keeping builds and tests fast. Expo brings together the best of mobile and the web and enables many important features for building and scaling an app. Expo is a set of tools built on top of React Native. The Nx Plugin for Expo contains generators for managing Expo applications and libraries within an Nx workspace. ## Requirements The `@nx/expo` plugin supports the following package versions. | Package | Supported Versions | | -------------- | --------------------------------- | | `metro-config` | >= 0.82.0 | | `expo` | ^53.0.0 \|\| ^54.0.0 \|\| ^55.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setting up Expo To create a new workspace with Expo, run the following command: ```shell npx create-nx-workspace@latest --preset=expo --appName=your-app-name ``` ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/expo` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/expo` by running the following command: ```shell nx add @nx/expo ``` This will install the correct version of `@nx/expo`. ### How @nx/expo infers tasks The `@nx/expo` plugin will create a task for any project that has an app configuration file present. Any of the following files will be recognized as an app configuration file: - `app.config.js` - `app.config.ts` - `app.json` The directory must contain both `package.json` and `metro.config.js`. Nx treats the project as Expo when the app configuration has an `expo` key or `package.json` lists `expo` as a dependency or development dependency: ```json // app.json { "expo": { "name": "MyProject", "slug": "my-project" } } ``` ### View inferred tasks To view inferred tasks for a project, open the [project details view](/docs/concepts/mental-model#inferred-tasks) in Nx Console or run `nx show project my-project --web` in the command line. ### @nx/expo configuration The `@nx/expo/plugin` is configured in the `plugins` array in `nx.json`. ```json // nx.json { "plugins": [ { "plugin": "@nx/expo/plugin", "options": { "startTargetName": "start", "serveTargetName": "serve", "runIosTargetName": "run-ios", "runAndroidTargetName": "run-android", "exportTargetName": "export", "prebuildTargetName": "prebuild", "installTargetName": "install", "buildTargetName": "build", "submitTargetName": "submit", "buildDepsTargetName": "build-deps", "watchDepsTargetName": "watch-deps" } } ] } ``` | Option | Default | Inferred behavior | | ---------------------- | ------------- | ----------------------------------------------------- | | `startTargetName` | `start` | Runs the `@nx/expo:start` executor continuously. | | `serveTargetName` | `serve` | Runs `expo start --web` continuously. | | `runIosTargetName` | `run-ios` | Runs `expo run:ios` continuously. | | `runAndroidTargetName` | `run-android` | Runs `expo run:android` continuously. | | `exportTargetName` | `export` | Runs and caches `expo export`, including its outputs. | | `prebuildTargetName` | `prebuild` | Runs the `@nx/expo:prebuild` executor. | | `installTargetName` | `install` | Runs the `@nx/expo:install` executor. | | `buildTargetName` | `build` | Runs the `@nx/expo:build` executor. | | `submitTargetName` | `submit` | Runs `eas submit`. | | `buildDepsTargetName` | none | Name of an optional dependency build task. | | `watchDepsTargetName` | none | Name of an optional dependency watch task. | Use `include` and `exclude` glob patterns on the plugin entry to scope inference. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/expo/plugin`. ### Creating applications Add a new application to your workspace with the following command: ```shell nx g @nx/expo:app apps/my-app ``` Start the application by running: ```shell nx start my-app ``` ### Generating libraries To generate a new library run: ```shell npx nx g @nx/expo:lib libs/your-lib-name ``` ### Generating components To generate a new component inside library run: ```shell npx nx g @nx/expo:component libs/your-lib-name/src/your-component-name --export ``` Replace `your-lib-name` with the app's name as defined in your `tsconfig.base.json` file or the `name` property of your `package.json` ## Using Expo ### Start a development server You can start a development server by running: ```shell nx start ``` ### Compile app locally You can compile your app locally with `run-ios` and `run-android` commands: {% tabs syncKey="platform" %} {% tabitem label="iOS" %} Compile for iOS: ```shell nx run-ios ``` {% /tabitem %} {% tabitem label="Android" %} Compile for Android: ```shell nx run-android ``` {% /tabitem %} {% /tabs %} To run these commands, you need to have your development environment setup. To run an iOS app,it can only be run on a Mac, and Xcode must be installed. Similarly, to run an Android app, it requires Android Studio and Java to be installed and configured on your computer. Setup steps: https://reactnative.dev/docs/environment-setup. ### Compile web assets You can build your JavaScript bundle using Metro bundler by running: ```shell nx export ``` {% tabs syncKey="platform" %} {% tabitem label="All Platforms" %} Compile for all platforms: ```shell nx export --platform=all ``` {% /tabitem %} {% tabitem label="iOS" %} Compile for iOS: ```shell nx export --platform=ios ``` {% /tabitem %} {% tabitem label="Android" %} Compile for Android: ```shell nx export --platform=android ``` {% /tabitem %} {% tabitem label="Web" %} Compile for Web: ```shell nx export --platform=web ``` {% /tabitem %} {% /tabs %} ### Generate native code To generate native code, run: ```shell nx prebuild ``` {% tabs syncKey="platform" %} {% tabitem label="All Platforms" %} Generate for all platforms: ```shell nx prebuild --platform=all ``` {% /tabitem %} {% tabitem label="iOS" %} Generate for iOS: ```shell nx prebuild --platform=ios ``` {% /tabitem %} {% tabitem label="Android" %} Generate for Android: ```shell nx prebuild --platform=android ``` {% /tabitem %} {% /tabs %} ### Install compatible npm packages To install packages that is compatible with current version of Expo, run: ```shell nx install ``` Unlike npm's `install` command, this `install` command will install the exact right version for currently installed copy of Expo. To install a specify NPM package, run: ```shell nx install --packages= nx install --packages=,, ``` To check and fix package versions, run: {% tabs %} {% tabitem label="check" %} Check which packages needed to be updated: ```shell nx install --check ``` {% /tabitem %} {% tabitem label="fix" %} Automatically update invalid packages versions: ```shell nx install --fix ``` {% /tabitem %} {% /tabs %} ### Run an EAS build Expo Application Services (EAS) are deeply integrated cloud services for Expo and React Native apps. EAS Build is a hosted service for building app binaries for your Expo and React Native projects. To run an EAS build: ```shell nx build ``` If you are not signed into an EAS account, run the following command to log in: ```shell npx eas login ``` To check the details of your build status, run: ```shell nx build-list ``` ### Submit an EAS build EAS Submit is a hosted service for uploading and submitting your app binaries to the app stores. Since it's a hosted service, you can submit your app to both stores as long as you can run EAS CLI on your machine. To submit an EAS build: ```shell nx submit ``` ### Update an EAS build EAS Update is a hosted service that serves updates for projects using the `expo-updates` library. EAS Update makes fixing small bugs and pushing quick fixes a snap in between app store submissions. It accomplishes this by allowing an end-user's app to swap out the non-native parts of their app (for example, JS, styling, and image changes) with a new update that contains bug fixes and other updates. To update an EAS build: ```shell nx update ``` ### Testing projects You can run unit tests with: ```shell nx test ``` ## Expo commands Below table is a map between expo commands and Nx commands: | Expo Commands | Nx Commands | | ------------------ | --------------------------- | | `expo start` | `nx start ` | | `expo run:ios` | `nx run-ios ` | | `expo run:android` | `nx run-android ` | | `expo export` | `nx export ` | | `expo prebuild` | `nx prebuild ` | | `expo install` | `nx install ` | | `eas build` | `nx build ` | | `eas build:list` | `nx build-list ` | | `eas update` | `nx update ` | | `eas submit` | `nx submit ` | ## More documentation - [Using Detox](/docs/technologies/test-tools/detox/introduction) - [Using Jest](/docs/technologies/test-tools/jest/introduction) ## Set up CI for your Expo monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/expo Migrations For an overview of the plugin and setup instructions, see the [@nx/expo introduction](/docs/technologies/react/expo/introduction). The @nx/expo plugin provides various migrations to help you migrate to newer versions of expo projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.1.x ### `update-23-1-0-update-metro-config-for-expo-56` **Version**: 23.1.0-beta.1 Update generated metro.config.js to use @expo/metro for Expo SDK 56 #### Requires | Name | Version | |------|---------| `expo` | `>= 56.0.0` | ### `update-23-1-0-replace-standalone-metro-for-expo-56` **Version**: 23.1.0-beta.1 Replace standalone metro-config/metro-resolver with @expo/metro for Expo SDK 56 #### Requires | Name | Version | |------|---------| `expo` | `>= 56.0.0` | ### `update-23-1-0-update-jest-winter-runtime-for-expo-56` **Version**: 23.1.0-beta.1 Update Jest test-setup for the Expo SDK 56 winter runtime #### Requires | Name | Version | |------|---------| `expo` | `>= 56.0.0` | ### 23.1.0-package-updates **Version**: 23.1.0-beta.1 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `expo` | `~56.0.0` | Updated only | `expo-splash-screen` | `~56.0.10` | Updated only | `expo-status-bar` | `~56.0.4` | Updated only | `expo-system-ui` | `~56.0.5` | Updated only | `@expo/cli` | `~56.1.14` | Updated only | `babel-preset-expo` | `~56.0.14` | Updated only | `react` | `^19.2.0` | Updated only | `react-dom` | `^19.2.0` | Updated only | `@types/react` | `^19.2.0` | Updated only | `react-native` | `0.85.3` | Updated only | `react-native-web` | `~0.21.0` | Updated only | `@expo/metro` | `~56.0.0` | Updated only | `@expo/metro-config` | `~56.0.13` | Updated only | `@expo/metro-runtime` | `~56.0.14` | Updated only | `@expo/vector-icons` | `~15.1.0` | Updated only | `react-native-svg-transformer` | `~1.5.1` | Updated only | `react-native-svg` | `15.12.1` | Updated only | `@testing-library/react-native` | `~13.2.0` | Updated only | `jest-expo` | `~56.0.4` | Updated only | `@babel/runtime` | `~7.27.6` | Updated only ## 23.0.x ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/expo/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/expo` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/expo/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/expo/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/expo/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/expo/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ### 23.0.0-package-updates **Version**: 23.0.0-beta.25 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `expo` | `~55.0.26` | Updated only | `expo-splash-screen` | `~55.0.21` | Updated only | `expo-status-bar` | `~55.0.6` | Updated only | `expo-system-ui` | `~55.0.18` | Updated only | `@expo/cli` | `~55.0.11` | Updated only | `babel-preset-expo` | `~55.0.7` | Updated only | `react` | `^19.2.0` | Updated only | `react-dom` | `^19.2.0` | Updated only | `@types/react` | `^19.2.0` | Updated only | `react-native` | `~0.83.6` | Updated only | `react-native-web` | `~0.21.0` | Updated only | `@expo/metro-config` | `~55.0.8` | Updated only | `@expo/metro-runtime` | `~55.0.11` | Updated only | `react-native-svg-transformer` | `~1.5.1` | Updated only | `react-native-svg` | `15.12.1` | Updated only | `@testing-library/react-native` | `~13.2.0` | Updated only | `jest-expo` | `~55.0.18` | Updated only | `@babel/runtime` | `~7.27.6` | Updated only | `metro-config` | `~0.83.0` | Updated only | `metro-resolver` | `~0.83.0` | Updated only ## 22.2.x ### `update-22-2-0-create-ai-instructions-for-expo-54` **Version**: 22.2.0-beta.3 Create AI migration instructions for Expo SDK 54 upgrade ### `update-22-2-0-add-expo-system-ui` **Version**: 22.2.0-beta.3 Add expo-system-ui dependency for Expo SDK 54 #### Requires | Name | Version | |------|---------| `expo` | `>= 54.0.0` | ### `update-22-2-0-update-jest-for-expo-54` **Version**: 22.2.0-beta.3 Update Jest configuration for Expo SDK 54 #### Requires | Name | Version | |------|---------| `expo` | `>= 54.0.0` | ### 22.2.0-package-updates **Version**: 22.2.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `expo` | `~54.0.0` | Updated only | `expo-splash-screen` | `~31.0.11` | Updated only | `expo-status-bar` | `~3.0.8` | Updated only | `expo-system-ui` | `~6.0.8` | Updated only | `@expo/cli` | `~54.0.16` | Updated only | `babel-preset-expo` | `~54.0.7` | Updated only | `react` | `^19.1.0` | Updated only | `react-dom` | `^19.1.0` | Updated only | `@types/react` | `^19.1.0` | Updated only | `react-native` | `~0.81.5` | Updated only | `react-native-web` | `~0.21.0` | Updated only | `@expo/metro-config` | `~54.0.9` | Updated only | `@expo/metro-runtime` | `~6.1.2` | Updated only | `react-native-svg-transformer` | `~1.5.1` | Updated only | `react-native-svg` | `15.12.1` | Updated only | `@testing-library/react-native` | `~13.2.0` | Updated only | `jest-expo` | `~54.0.13` | Updated only | `@babel/runtime` | `~7.27.6` | Updated only | `metro-config` | `~0.83.0` | Updated only | `metro-resolver` | `~0.83.0` | Updated only ## 21.4.x ### `update-21-4-0-remove-deprecated-deps` **Version**: 21.4.0-beta.0 Remove deprecated dependencies from package.json #### Requires | Name | Version | |------|---------| `expo` | `>= 53.0.0` | ### `update-21-4-0-update-splash-screen-config` **Version**: 21.4.0-beta.0 Update Expo splash screen configuration to use the new format #### Requires | Name | Version | |------|---------| `expo` | `>= 53.0.0` | ### `update-21-4-0-add-jest-resolver` **Version**: 21.4.0-beta.0 Add custom Jest resolver to handle Expo winter runtime issues #### Requires | Name | Version | |------|---------| `expo` | `>= 53.0.0 < 54.0.0` | ### 21.4.0-package-updates **Version**: 21.4.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `expo` | `~53.0.10` | Updated only | `expo-splash-screen` | `~0.30.9` | Updated only | `expo-status-bar` | `~2.2.3` | Updated only | `@expo/cli` | `~0.24.14` | Updated only | `babel-preset-expo` | `~13.2.0` | Updated only | `react` | `19.0.0` | Updated only | `react-dom` | `19.0.0` | Updated only | `@types/react` | `~19.0.10` | Updated only | `react-native` | `~0.79.3` | Updated only | `react-native-web` | `~0.20.0` | Updated only | `@expo/metro-config` | `~0.20.14` | Updated only | `@expo/metro-runtime` | `~5.0.4` | Updated only | `react-native-svg-transformer` | `~1.5.1` | Updated only | `react-native-svg` | `~15.11.2` | Updated only | `@testing-library/react-native` | `~13.2.0` | Updated only | `jest-expo` | `~53.0.7` | Updated only | `@babel/runtime` | `~7.27.6` | Updated only --- ## @nx/react Generators The @nx/react plugin provides various generators to help you create and configure react projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/react introduction](/docs/technologies/react/introduction). ## `application` Create a React application for Nx. ### Examples ###### Simple Application Create an application named `my-app`: ```bash nx g @nx/react:application apps/my-app ``` ###### Application using Vite as bundler Create an application named `my-app`: ```bash nx g @nx/react:app apps/my-app --bundler=vite ``` When choosing `vite` as the bundler, your unit tests will be set up with `vitest`, unless you choose `none` for `unitTestRunner`. ###### Specify style extension Create an application named `my-app` in the `my-dir` directory and use `scss` for styles: ```bash nx g @nx/react:app apps/my-dir/my-app --style=scss ``` ###### Add tags Add tags to the application (used for linting). ```bash nx g @nx/react:app apps/my-app --tags=scope:admin,type:ui ``` **Usage:** ```bash nx generate @nx/react:application [options] ``` **Aliases:** `app` **Arguments:** ```bash nx generate @nx/react:application [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--bundler` | string | The bundler to use. | `"vite"` | | `--classComponent` | boolean | Use class components instead of functional component. | `false` | | `--compiler` | string | The compiler to use. | `"babel"` | | `--e2eTestRunner` | string | Test runner to use for end to end (E2E) tests. | `"playwright"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--formatter` | string | The tool to use for code formatting. | | | `--globalCss` | boolean | Default is `false`. When `true`, the component is generated with `*.css`/`*.scss` instead of `*.module.css`/`*.module.scss`. | `false` | | `--inSourceTests` | boolean | When using Vitest, separate spec files will not be generated and instead will be included within the source files. Read more on the Vitest docs site: https://vitest.dev/guide/in-source.html | `false` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--minimal` | boolean | Generate a React app with a minimal setup, no separate test files. | `false` | | `--name` | string | The name of the application. | | | `--port` | number | The port to use for the development server. Defaults to 4200. | | | `--rootProject` | boolean | Create a application at the root of the workspace | `false` | | `--routing` | boolean | Generate application with routes. | `false` | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipNxJson` | boolean | Skip updating `nx.json` with default options based on values provided to this app. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--strict` | boolean | Creates an application with strict mode and strict type checking. | `true` | | `--style` | string | The file extension to be used for style files. | `"css"` | | `--tags` | string | Add tags to the application (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | | `--useReactRouter` | boolean | Use React Router for routing. | `false` | ## `component` Create a React Component for Nx. ### Examples ###### Simple Component Create a component named `MyComponent` at `libs/ui/src/my-component.tsx`: ```shell nx g @nx/react:component libs/ui/src/my-component.tsx ``` ###### With a Different Symbol Name Create a component named `Custom` at `libs/ui/src/my-component.tsx`: ```shell nx g @nx/react:component libs/ui/src/my-component.tsx --name=custom ``` ###### Omitting the File Extension Create a component named `MyComponent` at `libs/ui/src/my-component.tsx` without specifying the file extension: ```shell nx g @nx/react:component libs/ui/src/my-component ``` ###### Class Component Create a class component named `MyComponent` at `libs/ui/src/my-component.tsx`: ```shell nx g @nx/react:component libs/ui/src/my-component --classComponent ``` **Usage:** ```bash nx generate @nx/react:component [options] ``` **Aliases:** `c` **Arguments:** ```bash nx generate @nx/react:component [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--classComponent` | boolean | Use class components instead of functional component. | `false` | | `--export` | boolean | When true, the component is exported from the project `index.ts` (if it exists). | `false` | | `--globalCss` | boolean | Default is `false`. When `true`, the component is generated with `*.css`/`*.scss` instead of `*.module.css`/`*.module.scss`. | `false` | | `--inSourceTests` | boolean | When using Vitest, separate spec files will not be generated and instead will be included within the source files. Read more on the Vitest docs site: https://vitest.dev/guide/in-source.html | `false` | | `--name` | string | The component symbol name. Defaults to the last segment of the file path. | | | `--routing` | boolean | Generate a library with routes. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipTests` | boolean | When true, does not create `spec.ts` test files for the new component. | `false` | | `--style` | string | The file extension to be used for style files. | `"css"` | ## `component-story` Generate storybook story for a react component. **Usage:** ```bash nx generate @nx/react:component-story [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--componentPath` | string [**required**] | Relative path to the component file from the library root. | | | `--project` | string [**required**] | The project where to add the components. | | | `--interactionTests` | boolean | Set up Storybook interaction tests. | `true` | | `--skipFormat` | boolean | Skip formatting files. | `false` | ## `component-test` Add a Cypress component test for a component. ### Examples :::caution[Can I use component testing?] React component testing with Nx requires **Cypress version 10** and up. You can migrate with to v11 via the [migrate-to-cypress-11 generator](/nx-api/cypress/generators/migrate-to-cypress-11). This generator is for Cypress based component testing. If you're wanting to create Storybook stories for a component, then check out the [stories generator docs](/nx-api/react/generators/stories) ::: This generator is used to create a Cypress component test file for a given React component. ```shell nx g @nx/react:component-test --project=my-cool-react-project --componentPath=src/my-fancy-button.tsx ``` Test file are generated with the `.cy.` suffix. this is to prevent colliding with any existing `.spec.` files contained in the project. It's currently expected the generated `.cy.` file will live side by side with the component. It is also assumed the project is already setup for component testing. If it isn't, then you can run the [cypress-component-project generator](/nx-api/react/generators/cypress-component-configuration) to set up the project for component testing. **Usage:** ```bash nx generate @nx/react:component-test [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--componentPath` | string [**required**] | Path to component, from the project source root | | | `--project` | string [**required**] | The name of the project the component is apart of | | ## `consumer` Scaffold a React Module Federation consumer. The bundler is selected at generation time; remotes are registered dynamically at runtime from an inline PROVIDERS list in src/mf.ts (no build-time remotes list). **Usage:** ```bash nx generate @nx/react:consumer [options] ``` **Arguments:** ```bash nx generate @nx/react:consumer [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--bundler` | string | Bundler used by the consumer. Final choice; cannot be switched later. | `"vite"` | | `--port` | number | Dev-server port the consumer listens on. | | | `--providerNames` | array | Comma-separated list of provider project names to generate alongside this consumer. Each becomes an `@nx/react:provider` app sibling to the consumer and an entry in the `PROVIDERS` list in `src/mf.ts`. Defaults to `[my-provider]` as a placeholder (no provider is generated until you pass the flag explicitly). | | ## `cypress-component-configuration` Add a Cypress component testing configuration to an existing project. :::caution[Can I use component testing?] React component testing with Nx requires **Cypress version 10.7.0** and up. You can migrate with to v11 via the [migrate-to-cypress-11 generator](/nx-api/cypress/generators/migrate-to-cypress-11). This generator is for Cypress based component testing. If you want to test components via Storybook with Cypress, then check out the [storybook-configuration generator docs](/nx-api/react/generators/storybook-configuration). However, this functionality is deprecated, and will be removed on Nx version 19. ::: This generator is designed to get your React project up and running with Cypress Component Testing. ```shell nx g @nx/react:cypress-component-configuration --project=my-cool-react-project ``` Running this generator, adds the required files to the specified project with a preconfigured `cypress.config.ts` designed for Nx workspaces. The following file will be added to projects where the Component Testing build target is using `webpack` for bundling: ```ts title="cypress.config.ts" import { defineConfig } from 'cypress'; import { nxComponentTestingPreset } from '@nx/react/plugins/component-testing'; export default defineConfig({ component: nxComponentTestingPreset(__filename, { bundler: 'webpack', }), }); ``` The following file will be added to projects where the Component Testing build target is using `vite` for bundling: ```ts title="cypress.config.ts" import { defineConfig } from 'cypress'; import { nxComponentTestingPreset } from '@nx/react/plugins/component-testing'; export default defineConfig({ component: nxComponentTestingPreset(__filename, { bundler: 'vite', }), }); ``` Here is an example on how to add custom options to the configuration ```ts title="cypress.config.ts" import { defineConfig } from 'cypress'; import { nxComponentTestingPreset } from '@nx/react/plugins/component-testing'; export default defineConfig({ component: { ...nxComponentTestingPreset(__filename, { bundler: 'webpack', }), // extra options here }, }); ``` ### The `bundler` option Component testing supports two different bundlers: `webpack` and `vite`. The Nx generator will pick up the bundler used in the specified project's build target. If the build target is using `@nx/webpack:webpack`, then the generator will use `webpack` as the bundler. If the build target is using `@nx/vite:build`, then the generator will use `vite` as the bundler. You can manually set the bundler by passing `--bundler=webpack` or `--bundler=vite` to the generator, but that is not needed since the generator will pick up the correct bundler for you. However, if you want to use a different bundler than the one that is used in the build target, then you can manually set it using that flag. ### Specifying a Build Target Component testing requires a _build target_ to correctly run the component test dev server. This option can be manually specified with `--build-target=some-react-app:build`, but Nx will infer this usage from the [project graph](/concepts/mental-model#the-project-graph) if one isn't provided. For React projects, the build target needs to be using the `@nx/webpack:webpack` executor. The generator will throw an error if a build target can't be found and suggest passing one in manually. Letting Nx infer the build target by default ```shell nx g @nx/react:cypress-component-configuration --project=my-cool-react-project ``` Manually specifying the build target ```shell nx g @nx/react:cypress-component-configuration --project=my-cool-react-project --build-target:some-react-app:build --generate-tests ``` :::note[Build Target with Configuration] If you're wanting to use a build target with a specific configuration. i.e. `my-app:build:production`, then manually providing `--build-target=my-app:build:production` is the best way to do that. ::: ### Auto Generating Tests You can optionally use the `--generate-tests` flag to generate a test file for each component in your project. ```shell nx g @nx/react:cypress-component-configuration --project=my-cool-react-project --generate-tests ``` ### Running Component Tests A new `component-test` target will be added to the specified project to run your component tests. ```shell nx g component-test my-cool-react-project ``` Here is an example of the project configuration that is generated. The `--build-target` option is added as the `devServerTarget` which can be changed as needed. ```json title="project.json" { "targets" { "component-test": { "executor": "@nx/cypress:cypress", "options": { "cypressConfig": "/cypress.config.ts", "testingType": "component", "devServerTarget": "some-react-app:build", "skipServe": true } } } } ``` Nx also supports [Angular component testing](/nx-api/angular/generators/cypress-component-configuration). **Usage:** ```bash nx generate @nx/react:cypress-component-configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string [**required**] | The name of the project to add cypress component testing configuration to | | | `--buildTarget` | string | A build target used to configure Cypress component testing in the format of `project:target[:configuration]`. The build target should be from a React app. If not provided we will try to infer it from your projects usage. | | | `--bundler` | string | The bundler to use for Cypress Component Testing. | | | `--generateTests` | boolean | Generate default component tests for existing components in the project | `false` | | `--skipFormat` | boolean | Skip formatting files | `false` | ## `federate-module` Create a federated module, which can be loaded by a Consumer (host) via a Producer (remote). **Usage:** ```bash nx generate @nx/react:federate-module [options] ``` **Arguments:** ```bash nx generate @nx/react:federate-module [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--name` | string [**required**] | The name of the module. | | | `--remote` | string [**required**] | The name of the Producer (remote). | | | `--bundler` | string | The bundler to use. | `"rspack"` | | `--e2eTestRunner` | string | Test runner to use for end to end (E2E) tests. | `"cypress"` | | `--host` | string | The Consumer (host) application for this Producer (remote). | | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--remoteDirectory` | string | The directory of the new Producer (remote) application if one needs to be created. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--style` | string | The file extension to be used for style files. | `"none"` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"jest"` | ## `hook` Create a React component using Hooks in a dedicated React project. **Usage:** ```bash nx generate @nx/react:hook [options] ``` **Aliases:** `c` **Arguments:** ```bash nx generate @nx/react:hook [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--export` | boolean | When true, the hook is exported from the project `index.ts` (if it exists). | `false` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | | | `--name` | string | The hook symbol name. Defaults to the last segment of the file path. | | | `--skipTests` | boolean | When true, does not create `spec.ts` test files for the new hook. | `false` | ## `host` Create Module Federation configuration files for given React Consumer (Host) Application. **Usage:** ```bash nx generate @nx/react:host [options] ``` **Arguments:** ```bash nx generate @nx/react:host [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--bundler` | string | The bundler to use. | `"rspack"` | | `--classComponent` | boolean | Use class components instead of functional component. | `false` | | `--compiler` | string | The compiler to use | `"babel"` | | `--dynamic` | boolean | Should the Consumer (host) application use dynamic federation? | `false` | | `--e2eTestRunner` | string | Test runner to use for end to end (E2E) tests. | `"playwright"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--globalCss` | boolean | Default is false. When true, the component is generated with *.css/*.scss instead of *.module.css/*.module.scss | `false` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--minimal` | boolean | Generate a React app with a minimal setup. No nx starter template. | `false` | | `--name` | string | The name of the Consumer (host) application to generate the Module Federation configuration | | | `--port` | number | The port at which the Consumer (host) application should be served. Defaults to 4200. | | | `--remotes` | array | A list of Producer (remote) application names that the Consumer (host) application should consume. | `[]` | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipNxJson` | boolean | Skip updating nx.json with default options based on values provided to this app (e.g. babel, style). | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--ssr` | boolean | Whether to configure SSR for the Consumer (host) application | `false` | | `--strict` | boolean | Creates an application with strict mode and strict type checking | `true` | | `--style` | string | The file extension to be used for style files. | `"css"` | | `--tags` | string | Add tags to the application (used for linting). | | | `--typescriptConfiguration` | boolean | Whether the module federation configuration and webpack configuration files should use TS. When --js is used, this flag is ignored. | `true` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"jest"` | ## `library` Create a React Library for an Nx workspace. **Usage:** ```bash nx generate @nx/react:library [options] ``` **Aliases:** `lib` **Arguments:** ```bash nx generate @nx/react:library [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--appProject` | string | The application project to add the library route to. | | | `--buildable` | boolean | Generate a buildable library that uses rollup to bundle. | `false` | | `--bundler` | string | The bundler to use. Choosing 'none' means this library is not buildable. | `"none"` | | `--compiler` | string | Which compiler to use. | `"babel"` | | `--component` | boolean | Generate a default component. | `true` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--globalCss` | boolean | When `true`, the stylesheet is generated using global CSS instead of CSS modules (e.g. file is `*.css` rather than `*.module.css`). | `false` | | `--importPath` | string | The library name used to import it, like `@myorg/my-awesome-lib`. | | | `--inSourceTests` | boolean | When using Vitest, separate spec files will not be generated and instead will be included within the source files. | `false` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--minimal` | boolean | Create a React library with a minimal setup, no separate test files. | `false` | | `--name` | string | Library name | | | `--publishable` | boolean | Create a publishable library. | | | `--routing` | boolean | Generate library with routes. | | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--skipTsConfig` | boolean | Do not update `tsconfig.json` for development experience. | `false` | | `--strict` | boolean | Whether to enable tsconfig strict mode or not. | `true` | | `--style` | string | The file extension to be used for style files. | `"css"` | | `--tags` | string | Add tags to the library (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `provider` Scaffold a React Module Federation provider that exposes a federated component. The bundler is selected at generation time; the generated project has no Nx-wrapped bundler config. **Usage:** ```bash nx generate @nx/react:provider [options] ``` **Arguments:** ```bash nx generate @nx/react:provider [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--bundler` | string | Bundler used by the provider. The choice is final - the bundler config is too different to switch later. | `"vite"` | | `--consumer` | string | Name of a consumer project whose `serve` target this provider should depend on. When set, `nx serve ` also spins up the consumer (the 'serve a remote, host comes along' UX from the deprecated host/remote generators). Set automatically when the provider is generated as part of `@nx/react:consumer --providerNames=...`. | | | `--exposeName` | string | Name under which the federated component is exposed (consumers reference `/`). | `"App"` | | `--port` | number | Dev-server port the provider listens on. Defaults to a non-5000 port to avoid the macOS AirTunes collision on Vite. | | ## `redux` Create a Redux state slice for a React project. **Usage:** ```bash nx generate @nx/react:redux [options] ``` **Aliases:** `slice` **Arguments:** ```bash nx generate @nx/react:redux [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--appProject` | string | The application project to add the slice to. | | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | | | `--name` | string | The Redux state slice symbol name. Defaults to the last segment of the file path. | | ## `remote` Create Module Federation configuration files for given React Producer (Remote) Application. **Usage:** ```bash nx generate @nx/react:remote [options] ``` **Arguments:** ```bash nx generate @nx/react:remote [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--bundler` | string | The bundler to use. | `"rspack"` | | `--classComponent` | boolean | Use class components instead of functional component. | `false` | | `--compiler` | string | The compiler to use. | `"babel"` | | `--dynamic` | boolean | Should the Consumer (host) application use dynamic federation? | `false` | | `--e2eTestRunner` | string | Test runner to use for end to end (E2E) tests. | `"playwright"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--globalCss` | boolean | Default is false. When true, the component is generated with *.css/*.scss instead of *.module.css/*.module.scss. | `false` | | `--host` | string | The Consumer (host) application for this Producer (remote). | | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | The name of the Producer (remote) application to generate the Module Federation configuration | | | `--port` | number | The port on which this Producer (remote) app should be served. | | | `--routing` | boolean | Generate application with routes. | `false` | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipNxJson` | boolean | Skip updating nx.json with default options based on values provided to this app (e.g. babel, style). | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--ssr` | boolean | Whether to configure SSR for the Consumer (host) application | `false` | | `--strict` | boolean | Creates an application with strict mode and strict type checking. | `true` | | `--style` | string | The file extension to be used for style files. | `"css"` | | `--tags` | string | Add tags to the application (used for linting). | | | `--typescriptConfiguration` | boolean | Whether the module federation configuration and webpack configuration files should use TS. When --js is used, this flag is ignored. | `true` | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"jest"` | ## `setup-ssr` Create the additional configuration required to enable SSR via Express for a React application. **Usage:** ```bash nx generate @nx/react:setup-ssr [options] ``` **Arguments:** ```bash nx generate @nx/react:setup-ssr [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--appComponentImportPath` | string | The import path of the component, relative to project sourceRoot. | `"app/app"` | | `--bundler` | string | The bundler to use. | `"webpack"` | | `--extraInclude` | array | Extra include entries in tsconfig. | `[]` | | `--serverPort` | number | The port for the Express server. | `4200` | | `--skipFormat` | boolean | Skip formatting the workspace after the generator completes. | | ## `stories` Generate stories/specs for all components declared in a project. This generator will generate stories for all your components in your project. The stories will be generated using [Component Story Format 3 (CSF3)](https://storybook.js.org/blog/storybook-csf3-is-here/). ```bash nx g @nx/react:stories project-name ``` You can read more about how this generator works, in the [Storybook for React overview page](/recipes/storybook/overview-react#auto-generate-stories). When running this generator, you will be prompted to provide the following: - The `name` of the project you want to generate the configuration for. - Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, a `play` function will be added to your stories, and all the necessary dependencies will be installed. You can read more about this in the [Nx Storybook interaction tests documentation page](/recipes/storybook/storybook-interaction-tests#setup-storybook-interaction-tests).. You must provide a `name` for the generator to work. By default, this generator will also set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing). If you don't want to set up Storybook interaction tests, you can pass the `--interactionTests=false` option, but it's not recommended. There are a number of other options available. Let's take a look at some examples. ### Examples #### Ignore certain paths when generating stories ```bash nx g @nx/react:stories --name=ui --ignorePaths=libs/ui/src/not-stories/**,**/**/src/**/*.other.* ``` This will generate stories for all the components in the `ui` project, except for the ones in the `libs/ui/src/not-stories` directory, and also for components that their file name is of the pattern `*.other.*`. This is useful if you have a project that contains components that are not meant to be used in isolation, but rather as part of a larger component. By default, Nx will ignore the following paths: ```text *.stories.ts, *.stories.tsx, *.stories.js, *.stories.jsx, *.stories.mdx ``` but you can change this behaviour easily, as explained above. #### Generate stories using JavaScript instead of TypeScript ```bash nx g @nx/react:stories --name=ui --js=true ``` This will generate stories for all the components in the `ui` project using JavaScript instead of TypeScript. So, you will have `.stories.js` files next to your components. **Usage:** ```bash nx generate @nx/react:stories [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string [**required**] | Project for which to generate stories. | | | `--ignorePaths` | array | Paths to ignore when looking for components. | `["*.stories.ts,*.stories.tsx,*.stories.js,*.stories.jsx,*.stories.mdx"]` | | `--interactionTests` | boolean | Set up Storybook interaction tests. | `true` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | ## `storybook-configuration` Set up Storybook for a React app or library. This generator will set up Storybook for your **React** project. You can also use this generator to generate Storybook configuration for your **Next.js** project. By default, Storybook v10 is used. ```bash nx g @nx/react:storybook-configuration project-name ``` You can read more about how this generator works, in the [Storybook for React overview page](/recipes/storybook/overview-react#generate-storybook-configuration-for-an-react-project). When running this generator, you will be prompted to provide the following: - The `name` of the project you want to generate the configuration for. - Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/react/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, a `play` function will be added to your stories, and all the necessary dependencies will be installed. Also, a `test-storybook` target will be generated in your project's `project.json`, with a command to invoke the [Storybook `test-runner`](https://storybook.js.org/docs/react/writing-tests/test-runner). You can read more about this in the [Nx Storybook interaction tests documentation page](/recipes/storybook/storybook-interaction-tests#setup-storybook-interaction-tests).. - Whether you want to `generateStories` for the components in your project. If you choose `yes`, a `.stories.ts` file will be generated next to each of your components in your project. You must provide a `name` for the generator to work. By default, this generator will also set up [Storybook interaction tests](https://storybook.js.org/docs/react/writing-tests/interaction-testing). If you don't want to set up Storybook interaction tests, you can pass the `--interactionTests=false` option, but it's not recommended. There are a number of other options available. Let's take a look at some examples. ### Examples #### Generate Storybook configuration ```bash nx g @nx/react:storybook-configuration ui ``` This will generate Storybook configuration for the `ui` project using TypeScript for the Storybook configuration files (the files inside the `.storybook` directory, eg. `.storybook/main.ts`). #### Ignore certain paths when generating stories ```bash nx g @nx/react:storybook-configuration ui --generateStories=true --ignorePaths=libs/ui/src/not-stories/**,**/**/src/**/*.other.*,apps/my-app/**/*.something.ts ``` This will generate a Storybook configuration for the `ui` project and generate stories for all components in the `libs/ui/src/lib` directory, except for the ones in the `libs/ui/src/not-stories` directory, and the ones in the `apps/my-app` directory that end with `.something.ts`, and also for components that their file name is of the pattern `*.other.*`. This is useful if you have a project that contains components that are not meant to be used in isolation, but rather as part of a larger component. By default, Nx will ignore the following paths: ```text *.stories.ts, *.stories.tsx, *.stories.js, *.stories.jsx, *.stories.mdx ``` but you can change this behaviour easily, as explained above. #### Generate stories using JavaScript instead of TypeScript ```bash nx g @nx/react:storybook-configuration ui --generateStories=true --js=true ``` This will generate stories for all the components in the `ui` project using JavaScript instead of TypeScript. So, you will have `.stories.js` files next to your components. #### Generate Storybook configuration using JavaScript ```bash nx g @nx/react:storybook-configuration ui --tsConfiguration=false ``` By default, our generator generates TypeScript Storybook configuration files. You can choose to use JavaScript for the Storybook configuration files of your project (the files inside the `.storybook` directory, eg. `.storybook/main.js`). **Usage:** ```bash nx generate @nx/react:storybook-configuration [options] ``` **Arguments:** ```bash nx generate @nx/react:storybook-configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--configureStaticServe` | boolean | Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times. | `true` | | `--generateStories` | boolean | Automatically generate `*.stories.ts` files for components declared in this project? | `true` | | `--ignorePaths` | array | Paths to ignore when looking for components. | `["*.stories.ts,*.stories.tsx,*.stories.js,*.stories.jsx,*.stories.mdx"]` | | `--interactionTests` | boolean | Set up Storybook interaction tests. | `true` | | `--js` | boolean | Generate JavaScript story files rather than TypeScript story files. | `false` | | `--linter` | string | The tool to use for running lint checks. | | | `--tsConfiguration` | boolean | Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js. | `true` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/react: --help ``` --- ## Guides {% index_page_cards path="technologies/react/guides" /%} --- ## Nx with React [React](https://react.dev/) is a popular library for building user interfaces. In a React monorepo, every project benefits from Nx [caching](/docs/features/cache-task-results), [task orchestration](/docs/features/run-tasks), and the [project graph](/docs/features/explore-graph). The React plugin for Nx, `@nx/react`, provides generators for [applications and libraries](#generate-react-applications-and-libraries), generators for [Module Federation](/docs/kb/react-micro-frontends), and [library build support](/docs/kb/buildable-and-publishable-libraries). It integrates with popular bundlers and test runners so you can configure each project to match your team's toolchain. You don't need the plugin to use React with Nx - it simplifies scaffolding and code generation. ## Requirements The `@nx/react` plugin supports the following package versions. | Package | Supported Versions | | ------- | -------------------- | | `react` | ^18.0.0 \|\| ^19.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. {% aside type="tip" title="Using a React Meta-Framework?" %} If you're building with **Next.js** or **Remix**, use their dedicated plugins which include inferred task support. See the [Next.js plugin](/docs/technologies/react/next/introduction) or [Remix plugin](/docs/technologies/react/remix/introduction) hub pages. For **React Router** in framework mode, see the [React Router guide](/docs/kb/react-router). {% /aside %} ## Setting up @nx/react ### Add to an existing Nx workspace {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/react` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). Using `nx add` helps ensure the correct version is installed and configured. {% /aside %} ```shell nx add @nx/react ``` ### Verification {% steps %} 1. Check that `@nx/react` is listed: ```shell nx report ``` 2. Verify generators are available: ```shell nx list @nx/react ``` {% /steps %} ### Create a new workspace ```shell npx create-nx-workspace@latest --template=nrwl/react-template ``` {% aside type="tip" title="Tutorial" %} For a guided walkthrough, follow the [Learn Nx Tutorial](/docs/getting-started/tutorials/crafting-your-workspace). {% /aside %} ### Generate a new application ```shell nx g @nx/react:app apps/my-app ``` To start the application in development mode, run `nx serve my-app`. Read more about the options available for the [application generator](/docs/technologies/react/generators#application). #### Choose a Bundler The `--bundler` option selects the build tool for your application or buildable library. You can learn more about the specific bundler you scaffolded your project with on the bundler's plugin page: - [Vite](/docs/technologies/build-tools/vite/introduction) (Recommended) - [Webpack](/docs/technologies/build-tools/webpack/introduction) - [Rspack](/docs/technologies/build-tools/rspack/introduction) - [Rollup](/docs/technologies/build-tools/rollup/introduction) ### Generate a new library ```shell nx g @nx/react:lib libs/my-lib # With a bundler for buildable/publishable libraries nx g @nx/react:lib libs/my-lib --bundler=vite nx g @nx/react:lib libs/my-lib --bundler=rollup nx g @nx/react:lib libs/my-lib --publishable --importPath=@myorg/my-lib ``` The same [bundler options](#choose-a-bundler) apply when creating buildable libraries. Read more about the options available for the [library generator](/docs/technologies/react/generators#library). You can also read more about [buildable and publishable libraries](/docs/kb/buildable-and-publishable-libraries). ### Serve applications ```shell nx serve my-app ``` This starts the development server with hot module replacement so you can iterate on your application locally. ### Build projects ```shell nx build my-app nx build my-lib ``` Build output goes to the `dist` folder by default. You can customize the output folder by setting `outputPath` in the [project's configuration file](/docs/reference/project-configuration). Nx [caches build results](/docs/features/cache-task-results) automatically. Subsequent builds of unchanged projects restore from cache. ### View project configuration To see the full configuration for a project: ```shell nx show project my-app ``` Or open the [project details view in Nx Console](/docs/kb/console-project-details#_top). For full generator and executor option lists, see the [generators reference](/docs/technologies/react/generators) and [executors reference](/docs/technologies/react/executors). ## Set up CI for your React monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). ## What's next {% cardgrid %} {% linkcard title="Learn Nx Tutorial" description="Build a monorepo step-by-step with Nx." href="/docs/getting-started/tutorials/crafting-your-workspace" /%} {% linkcard title="React Router Guide" description="Set up React Router in framework mode with Nx." href="/docs/kb/react-router" /%} {% linkcard title="Micro Frontends with React" description="Build React micro frontends with Module Federation." href="/docs/kb/react-micro-frontends" /%} {% linkcard title="Next.js Plugin" description="Use Nx with Next.js for inferred tasks and generators." href="/docs/technologies/react/next/introduction" /%} {% linkcard title="Generators Reference" description="Full API reference for @nx/react generators." href="/docs/technologies/react/generators" /%} {% linkcard title="Executors Reference" description="Full API reference for @nx/react executors." href="/docs/technologies/react/executors" /%} {% /cardgrid %} --- ## @nx/react Migrations For an overview of the plugin and setup instructions, see the [@nx/react introduction](/docs/technologies/react/introduction). The @nx/react plugin provides various migrations to help you migrate to newer versions of react projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.2.x ### `update-23-2-0-add-optional-module-federation-packages` **Version**: 23.2.0-beta.4 Add optional Module Federation packages when existing targets require them. #### Add Optional Module Federation Packages Adds `@nx/module-federation`, `express`, and `http-proxy-middleware` to the workspace when existing targets require them. These packages are no longer direct dependencies of `@nx/react`; they are now optional peer dependencies, so installing `@nx/react` no longer pulls the Module Federation toolchain into workspaces that never use it. This migration backfills them for workspaces that already use Module Federation so those builds keep working after upgrading. Packages that are already present are left untouched. A package is added only when a matching target exists: - `@nx/module-federation`: an `@nx/react:module-federation-dev-server`, `@nx/react:module-federation-ssr-dev-server`, or `@nx/react:module-federation-static-server` target, or a project with a `module-federation.config.{js,ts}` file (covers remotes whose host lives in another workspace). - `express` and `http-proxy-middleware`: an `@nx/react:module-federation-static-server` target, which proxies the static remotes. Targets that inherit their executor from an `nx.json` `targetDefaults` entry are detected too. #### Examples For a workspace with an `@nx/react:module-federation-dev-server` serve target, the migration adds the Module Federation packages to `devDependencies`. ##### Before ```jsonc title="package.json" { "devDependencies": { "@nx/react": "23.1.0", }, } ``` ##### After ```jsonc title="package.json" { "devDependencies": { "@nx/module-federation": "23.2.0", "@nx/react": "23.1.0", }, } ``` ### `update-23-2-0-add-svgr-webpack-if-used` **Version**: 23.2.0-beta.4 Add `@svgr/webpack` when a webpack config references it. #### Add `@svgr/webpack` If Used Adds `@svgr/webpack` to the workspace when a webpack config references it. `@svgr/webpack` is no longer a dependency of `@nx/react`, so workspaces whose webpack configs resolve it - which is what the Nx 22 `add-svgr-to-webpack-config` migration inlined - must declare it themselves. This migration backfills it for those workspaces. Configs referenced by executor targets are checked, as well as the conventional config file names at each project root. Workspaces that already have `@svgr/webpack` are left untouched. #### Examples ##### Before ```jsonc title="package.json" { "devDependencies": { "@nx/react": "23.1.0", }, } ``` ##### After ```jsonc title="package.json" { "devDependencies": { "@nx/react": "23.1.0", "@svgr/webpack": "^8.0.1", }, } ``` ## 23.1.x ### `update-23-1-0-create-ai-instructions-for-react-19` **Version**: 23.1.0-beta.0 Create AI instructions to help migrate workspaces from React 18 to 19. #### Requires | Name | Version | |------|---------| `react` | `>=19.0.0` | #### Upgrade React 18 to 19 Bumps `react`, `react-dom`, and the React type packages from 18 to 19. React 19 removes several long-deprecated APIs; the change every project hits is the root API (`ReactDOM.render` -> `createRoot`). Read more in the [React 19 upgrade guide](https://react.dev/blog/2024/04/25/react-19-upgrade-guide). The paired AI instructions migration walks an agent through the full set of changes. The common ones are shown below. #### Examples ##### Before ```tsx title="src/main.tsx" import ReactDOM from 'react-dom'; ReactDOM.render(, document.getElementById('root')); ``` ##### After ```tsx title="src/main.tsx" import { createRoot } from 'react-dom/client'; createRoot(document.getElementById('root')!).render(); ``` `defaultProps` on a function component becomes a default parameter: ##### Before ```tsx function Badge({ color }) {} Badge.defaultProps = { color: 'gray' }; ``` ##### After ```tsx function Badge({ color = 'gray' }) {} ``` ### 23.1.0-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `react` | `^19.0.0` | Updated only | `react-dom` | `^19.0.0` | Updated only | `react-is` | `^19.0.0` | Updated only | `@types/react` | `^19.0.0` | Updated only | `@types/react-dom` | `^19.0.0` | Updated only | `@types/react-is` | `^19.0.0` | Updated only ### 23.1.0-eslint-plugin-react-package-updates **Version**: 23.1.0-beta.5 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `eslint-plugin-react` | `^7.35.0` | Updated only ## 23.0.x ### `update-23-0-0-remove-nx-react-webpack-plugin-import` **Version**: 23.0.0-beta.10 Rewrites imports of NxReactWebpackPlugin from '@nx/react' to the sub-path '@nx/react/webpack-plugin'. #### Rewrite `NxReactWebpackPlugin` Imports to the `@nx/react/webpack-plugin` Sub-path The deprecated re-export of `NxReactWebpackPlugin` from `@nx/react` is removed in v23. The migration rewrites both ES module imports and CJS `require()` calls to use the `@nx/react/webpack-plugin` sub-path. Imports that combine the deprecated symbol with other named imports are split into two declarations so the rest of the original import still resolves from `@nx/react`. Aliases (`as Plugin`, `: Plugin`) are preserved. #### Sample Code Changes ES module import. ##### Before ```ts title="apps/my-app/webpack.config.ts" {1} import { NxReactWebpackPlugin } from '@nx/react'; export default { plugins: [new NxReactWebpackPlugin()] }; ``` ##### After ```ts title="apps/my-app/webpack.config.ts" import { NxReactWebpackPlugin } from '@nx/react/webpack-plugin'; export default { plugins: [new NxReactWebpackPlugin()] }; ``` ES module import combined with other named imports. ##### Before ```ts title="apps/my-app/webpack.config.ts" {1} import { NxReactWebpackPlugin, withReact } from '@nx/react'; ``` ##### After ```ts title="apps/my-app/webpack.config.ts" import { NxReactWebpackPlugin } from '@nx/react/webpack-plugin'; import { withReact } from '@nx/react'; ``` CJS `require()`. ##### Before ```js title="apps/my-app/webpack.config.js" {1} const { NxReactWebpackPlugin } = require('@nx/react'); ``` ##### After ```js title="apps/my-app/webpack.config.js" const { NxReactWebpackPlugin } = require('@nx/react/webpack-plugin'); ``` ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/react/router-plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/react` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/react/router-plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/react/router-plugin'; ``` ##### After ```ts import { createNodes } from '@nx/react/router-plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/react/router-plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ### `rewrite-react-internal-subpath-imports` **Version**: 23.0.0-beta.25 Rewrites `@nx/react/src/*` subpath imports now that the `./src/*` subpath is no longer exposed by `@nx/react`'s exports map. Named imports/exports of public symbols are routed to `@nx/react` and the rest to the new `@nx/react/internal` entry; `require`, dynamic `import` and `jest.mock` calls reference the whole module and are routed to `@nx/react/internal`. ## 22.7.x ### 22.7.0-package-updates **Version**: 22.7.0-beta.18 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `react-router` | `7.14.2` | Updated only | `@react-router/dev` | `7.14.2` | Updated only | `@react-router/node` | `7.14.2` | Updated only | `@react-router/serve` | `7.14.2` | Updated only | `@react-router/express` | `7.14.2` | Updated only | `@react-router/fs-routes` | `7.14.2` | Updated only | `@react-router/cloudflare` | `7.14.2` | Updated only | `@react-router/architect` | `7.14.2` | Updated only | `@react-router/remix-routes-option-adapter` | `7.14.2` | Updated only ## 22.6.x ### 22.6.0-package-updates **Version**: 22.6.0-beta.10 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/enhanced` | `^2.1.0` | Updated only | `@module-federation/node` | `^2.7.21` | Updated only ## 22.3.x ### 22.3.4-package-updates **Version**: 22.3.4-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `react-router-dom` | `6.30.3` | Updated only ### 22.3.4-react-router-package-updates **Version**: 22.3.4-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `react-router` | `7.12.0` | Updated only | `@react-router/dev` | `7.12.0` | Updated only | `@react-router/node` | `7.12.0` | Updated only | `@react-router/serve` | `7.12.0` | Updated only | `@react-router/express` | `7.12.0` | Updated only | `@react-router/fs-routes` | `7.12.0` | Updated only | `@react-router/cloudflare` | `7.12.0` | Updated only | `@react-router/architect` | `7.12.0` | Updated only | `@react-router/remix-routes-option-adapter` | `7.12.0` | Updated only ## 22.2.x ### 22.2.0-package-updates **Version**: 22.2.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@module-federation/enhanced` | `^0.21.2` | Updated only | `@module-federation/runtime` | `^0.21.2` | Updated only | `@module-federation/sdk` | `^0.21.2` | Updated only | `@module-federation/node` | `^2.7.21` | Updated only ### 22.2.0-emotion-package-updates **Version**: 22.2.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@emotion/react` | `11.14.0` | Updated only | `@emotion/styled` | `11.14.1` | Updated only | `@emotion/babel-plugin` | `11.13.5` | Updated only ## 22.0.x ### `update-22-0-0-add-svgr-to-webpack-config` **Version**: 22.0.0-beta.0 Updates webpack configs using React to use the new withSvgr composable function instead of the svgr option in withReact or NxReactWebpackPlugin. ## 21.4.x ### 21.4.0-package-updates **Version**: 21.4.0-beta.8 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `http-proxy-middleware` | `^3.0.5` | Updated only ## 21.0.x ### `update-21-0-0-update-babel-loose` **Version**: 21.0.0-beta.11 Replaces `classProperties.loose` option with `loose`. #### Replace `classProperties.loose` option in `.babelrc` The `classProperties.loose` option is replaced by `loose` in `.babelrc` files. #### Sample Code Changes ##### Before ```json title=".babelrc" { "presets": [ [ "@nx/react/babel", { "runtime": "automatic", "classProperties": { "loose": true }, "useBuiltIns": "usage" } ] ], "plugins": [] } ``` ##### After ```json title=".babelrc" {7} { "presets": [ [ "@nx/react/babel", { "runtime": "automatic", "loose": true, "useBuiltIns": "usage" } ] ], "plugins": [] } ``` --- ## Next.js Get started with [Nx with Next.js](/docs/technologies/react/next/introduction), or browse the topics below. {% index_page_cards path="technologies/react/next" /%} --- ## @nx/next Executors The @nx/next plugin provides various executors to help you create and configure next projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/next introduction](/docs/technologies/react/next/introduction). ### `build` Build a Next.js app. `project.json`: ```json //... { "name": "acme", "$schema": "node_modules/nx/schemas/project-schema.json", "sourceRoot": ".", "projectType": "application", "targets": { //... "build": { "executor": "@nx/next:build", "outputs": ["{options.outputPath}"], "defaultConfiguration": "production", "options": { "outputPath": "dist/acme" } } //... } } ``` ```bash nx run acme:build ``` ### Examples #### For Next.js Standalone projects ###### Default configuration This is the default configuration for Next.js standalone projects. Our `@nx/next:build` executor is integrated to use Next.js' CLI. You can read more about the build options at [Next.js CLI Options](https://nextjs.org/docs/app/api-reference/next-cli) ```json "build": { "executor": "@nx/next:build", "outputs": ["{options.outputPath}"], "defaultConfiguration": "production", "options": { "outputPath": "dist/acme" }, "configurations": { "development": { "outputPath": "." }, "production": {} } }, ``` ###### Enable debug You can create a debug build for more verbose output by: Using the `--debug` flag ```shell nx run acme:build:development --debug ``` Updating the build options to include `debug`. ```json "build": { "executor": "@nx/next:build", "outputs": ["{options.outputPath}"], "defaultConfiguration": "production", "options": { "outputPath": "dist/acme" }, "configurations": { "development": { "outputPath": ".", "debug": true }, "production": {} } }, ``` ```bash nx run acme:build:development ``` ###### Adding profiling You can enable profiing for React by Using the `--profile` flag ```shell nx run acme:build:production --profile ``` Updating the build options to include `profile`. ```json "build": { "executor": "@nx/next:build", "outputs": ["{options.outputPath}"], "defaultConfiguration": "production", "options": { "outputPath": "dist/acme" }, "configurations": { "development": { "outputPath": ".", }, "production": { "profile": true } } }, ``` ```shell nx run acme:build:production ``` ###### Enable experimental app only Since Next.js 13 the `app/` directory it is reserved. You can enable to build only `app/` routes by Using the `--experimentalAppOnly` flag ```shell nx run acme:build:production --experimentalAppOnly ``` Updating the build options to include `experimentalAppOnly`. ```json "build": { "executor": "@nx/next:build", "outputs": ["{options.outputPath}"], "defaultConfiguration": "production", "options": { "outputPath": "dist/acme" }, "configurations": { "development": { "outputPath": ".", "experimentalAppOnly": true }, "production": {} } }, ``` ```shell nx run acme:build:production ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `outputPath` | string [**required**] | The output path of the generated files. | | | `buildLibsFromSource` | boolean | Read buildable libraries from source instead of building them separately. | `true` | | `debug` | boolean | Enable Next.js debug build logging | | | `experimentalAppOnly` | boolean | Only build 'app' routes | | | `experimentalBuildMode` | string | Change the build mode. | | | `fileReplacements` | array | Replace files with other files in the build. | `[]` | | `generateLockfile` | boolean | Generate a lockfile (e.g. package-lock.json) that matches the workspace lockfile to ensure package versions match. | `false` | | `includeDevDependenciesInPackageJson` | boolean | Include `devDependencies` in the generated package.json file. By default only production `dependencies` are included. | `false` | | `nextConfig` | string | Path (relative to workspace root) to a function which takes phase, config, and builder options, and returns the resulting config. This is an advanced option and should not be used with a normal Next.js config file (i.e. `next.config.js`). | | | `profile` | boolean | Used to enable React Production Profiling | | | `skipOverrides` | boolean | Do not add a `overrides` and `resolutions` entries to the generated package.json file. Only works in conjunction with `generatePackageJson` option. | | | `skipPackageManager` | boolean | Do not add a `packageManager` entry to the generated package.json file. | | | `turbo` | boolean | Use Turbopack for building (Next.js 15 and below). In Next.js 16+, Turbopack is enabled by default. | | | `webpack` | boolean | Use webpack bundler instead of Turbopack (Next.js 16+ only). This flag is only applicable in Next.js 16 and above where Turbopack is the default. | | ### `server` Serve a Next.js app. `project.json`: ```json //... { "name": "acme", "$schema": "node_modules/nx/schemas/project-schema.json", "sourceRoot": ".", "projectType": "application", "targets": { //... "serve": { "executor": "@nx/next:server", "defaultConfiguration": "production", "options": { "buildTarget": "acme:build", "dev": true } } //... } } ``` ```bash nx run acme:serve ``` ### Examples #### For Next.js Standalone projects ###### Default configuration This is the default configuration for Next.js standalone projects. Our `@nx/next:server` executor is integrated to use Next.js' CLI. You can read more about the serve options at [Next.js CLI Options](https://nextjs.org/docs/app/api-reference/next-cli) ```json "serve": { "executor": "@nx/next:server", "defaultConfiguration": "development", "options": { "buildTarget": "acme:build", "dev": true }, "configurations": { "development": { "buildTarget": "acme:build:development", "dev": true }, "production": { "buildTarget": "acme:build:production", "dev": false } } }, ``` ###### Choosing your bundler Turbopack is a cutting-edge bundler designed for JavaScript and TypeScript. To read more about supported features see [Next.js Turbopack Documentation](https://turbo.build/pack/docs/features) **Important: Next.js 16 changed the default bundler** - **Next.js 15 and below**: Webpack is the default bundler. Use `--turbo` to enable Turbopack. - **Next.js 16 and above**: Turbopack is the default bundler. Use `--webpack` to use Webpack instead. #### Using Turbopack in Next.js 15 and below Append the `--turbo` flag while executing the Nx development server: ```shell nx run acme:serve --turbo ``` Or update the serve options to include `turbo`: ```json "serve": { "executor": "@nx/next:server", "defaultConfiguration": "development", "options": { "buildTarget": "acme:build", "dev": true }, "configurations": { "development": { "buildTarget": "acme:build:development", "dev": true, "turbo": true } } } ``` #### Using Webpack in Next.js 16 and above If you need to use Webpack instead of the default Turbopack in Next.js 16+: ```shell nx run acme:serve --webpack ``` Or update the serve options to include `webpack`: ```json "serve": { "executor": "@nx/next:server", "defaultConfiguration": "development", "options": { "buildTarget": "acme:build", "dev": true }, "configurations": { "development": { "buildTarget": "acme:build:development", "dev": true, "webpack": true } } } ``` ###### Adding keep alive timeout When using Nx with Next.js behind a downstream proxy, it's important to make sure that the `keep-alive timeouts` of Next.js' HTTP server are set to longer durations than the timeouts of the proxy. If you don't do this, Node.js will unexpectedly end TCP connections without notifying the proxy when the `keep-alive timeout` is reached. This can lead to a proxy error when the proxy tries to reuse a connection that Node.js has already terminated. To configure timeout values (in milliseconds) you can: Pass `--keepAliveTimeout` ```shell nx run acme:serve --keepAliveTimeout 60000 ``` Updating the serve options to include `keepAliveTimeout`. ```json "serve": { "executor": "@nx/next:server", "defaultConfiguration": "development", "options": { "buildTarget": "acme:build", "dev": true }, "configurations": { "development": { "buildTarget": "acme:build:development", "dev": true, "keepAliveTimeout": 60000 }, // } } ``` ```shell nx run acme:serve ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `buildTarget` | string [**required**] | Target which builds the application. | | | `buildLibsFromSource` | boolean | Read buildable libraries from source instead of building them separately. | `true` | | `customServerHttps:` | boolean | Enable HTTPS support for the custom server. | | | `customServerTarget` | string | Target which builds the custom server. | | | `dev` | boolean | Serve the application in the dev mode. | `true` | | `experimentalHttps` | boolean | Enable HTTPS support for the Next.js development server. | | | `experimentalHttpsCa` | string | Path to a HTTPS certificate authority file. | | | `experimentalHttpsCert` | string | Path to a HTTPS certificate file. | | | `experimentalHttpsKey` | string | Path to a HTTPS key file. | | | `hostname` | string | Hostname on which the application is served. | | | `keepAliveTimeout` | number | Max milliseconds to wait before closing inactive connection. | | | `port` | number | Port to listen on. | `4200` | | `quiet` | boolean | Hide error messages containing server information. | `false` | | `staticMarkup` | boolean | Static markup. | `false` | | `turbo` | boolean | Activate Turbopack for Next.js (Next.js 15 and below). In Next.js 16+, Turbopack is enabled by default for development mode. | | | `webpack` | boolean | Use webpack bundler instead of Turbopack (Next.js 16+ only). This flag is only applicable in Next.js 16 and above where Turbopack is the default for development mode. | | --- ## @nx/next Generators The @nx/next plugin provides various generators to help you create and configure next projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/next introduction](/docs/technologies/react/next/introduction). ## `application` Create a Next.js Application for Nx. ### Examples ###### Create app in a nested directory ```shell nx g app apps/nested/myapp ``` ###### Use a custom Express server ```shell nx g app apps/myapp --custom-server ``` ###### Use plain JavaScript (not TypeScript) ```shell nx g app apps/myapp --js ``` **Usage:** ```bash nx generate @nx/next:application [options] ``` **Aliases:** `app` **Arguments:** ```bash nx generate @nx/next:application [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--appDir` | boolean | Enable the App Router for this project. | `true` | | `--customServer` | boolean | Use a custom Express server for the Next.js application. | `false` | | `--e2eTestRunner` | string | Test runner to use for end to end (E2E) tests. | `"playwright"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--formatter` | string | The tool to use for code formatting. | | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | The name of the application. | | | `--rootProject` | boolean | Create an application at the root of the workspace. | `false` | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--src` | boolean | Generate a `src` directory for the project. | `true` | | `--style` | string | The file extension to be used for style files. | `"css"` | | `--swc` | boolean | Enable the Rust-based compiler SWC to compile JS/TS files. | `true` | | `--tags` | string | Add tags to the application (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `component` Create a React Component for Next. ### Examples ###### Create a Component Generate a component named `MyComponent` at `apps/my-app/src/app/my-component/my-component.tsx`: ```shell nx g component apps/my-app/src/app/my-component/my-component.tsx ``` ###### Create a Component with a Different Symbol Name Generate a component named `Custom` at `apps/my-app/src/app/my-component/my-component.tsx`: ```shell nx g component apps/my-app/src/app/my-component/my-component.tsx --name=custom ``` ###### Create a Component Omitting the File Extension Generate a component named `MyComponent` at `apps/my-app/src/app/my-component/my-component.tsx` without specifying the file extension: ```shell nx g component apps/my-app/src/app/my-component/my-component ``` **Usage:** ```bash nx generate @nx/next:component [options] ``` **Arguments:** ```bash nx generate @nx/next:component [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--export` | boolean | When true, the component is exported from the project index.ts (if it exists). | `false` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | | | `--name` | string | The component symbol name. Defaults to the last segment of the file path. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipTests` | boolean | When true, does not create `spec.ts` test files for the new component. | `false` | | `--style` | string | The file extension to be used for style files. | `"css"` | ## `convert-to-inferred` Convert existing Next.js project(s) using `@nx/next:build` executor to use `@nx/next/plugin`. **Usage:** ```bash nx generate @nx/next:convert-to-inferred [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/next:build` executor to use `@nx/next/plugin`. If not provided, all projects using the `@nx/next:build` executor will be converted. | | | `--skipFormat` | boolean | Whether to format files. | `false` | ## `custom-server` Add a custom server to existing Next.js application. ### Examples ###### Add a custom server to existing app ```shell nx g custom-server my-app ``` **Usage:** ```bash nx generate @nx/next:custom-server [options] ``` **Arguments:** ```bash nx generate @nx/next:custom-server [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--compiler` | string | The compiler used to build the custom server. | `"tsc"` | ## `cypress-component-configuration` Add Cypress Componet Testing to an existing NextJS project. :::caution[Can I use component testing?] Next component testing with Nx requires **Cypress version 10.7.0** and up. You can migrate with to v11 via the [migrate-to-cypress-11 generator](/nx-api/cypress/generators/migrate-to-cypress-11). This generator is for Cypress based component testing. If you want to test components via Storybook with Cypress, then check out the [storybook-configuration generator docs](/nx-api/react/generators/storybook-configuration). However, this functionality is deprecated, and will be removed on Nx version 19. ::: This generator is designed to get your Next project up and running with Cypress Component Testing. ```shell nx g @nx/next:cypress-component-configuration --project=my-cool-next-project ``` Running this generator, adds the required files to the specified project with a preconfigured `cypress.config.ts` designed for Nx workspaces. ```ts title="cypress.config.ts" import { defineConfig } from 'cypress'; import { nxComponentTestingPreset } from '@nx/next/plugins/component-testing'; export default defineConfig({ component: nxComponentTestingPreset(__filename), }); ``` Here is an example on how to add custom options to the configuration ```ts title="cypress.config.ts" import { defineConfig } from 'cypress'; import { nxComponentTestingPreset } from '@nx/next/plugins/component-testing'; export default defineConfig({ component: { ...nxComponentTestingPreset(__filename), // extra options here }, }); ``` ```shell nx g @nx/next:cypress-component-project --project=my-cool-next-project ``` ### Auto Generating Tests You can optionally use the `--generate-tests` flag to generate a test file for each component in your project. ```shell nx g @nx/next:cypress-component-configuration --project=my-cool-next-project --generate-tests ``` ### Running Component Tests A new `component-test` target will be added to the specified project to run your component tests. ```shell nx g component-test my-cool-next-project ``` Here is an example of the project configuration that is generated. ```json title="project.json" { "targets" { "component-test": { "executor": "@nx/cypress:cypress", "options": { "cypressConfig": "/cypress.config.ts", "testingType": "component", "skipServe": true } } } } ``` Nx also supports [Angular component testing](/nx-api/angular/generators/cypress-component-configuration). **Usage:** ```bash nx generate @nx/next:cypress-component-configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string [**required**] | The name of the project to add cypress component testing configuration to | | | `--generateTests` | boolean | Generate default component tests for existing components in the project | `false` | | `--skipFormat` | boolean | Skip formatting files | `false` | ## `library` Create a Next.js library for an Nx workspace. Same output as `@nx/react:library`, plus a `server` entry point for React Server Components and the Next.js type declarations. ### Examples ###### Create a new lib ```shell nx g lib libs/my-lib ``` ###### Create a new lib under a directory The following will create a library at `libs/shared/my-lib`. ```shell nx g lib libs/shared/my-lib ``` ###### Export React Server Components Unlike a React library, a Next.js library has a second entry point, `src/server.ts`, for React Server Components. Exporting a server component from `src/index.ts` marks that whole file as server-only and breaks imports from client components, so keep client components in `src/index.ts` and server components in `src/server.ts`. ```typescript // apps/my-app/app/page.tsx import { MyComponent } from '@myorg/my-lib'; import { HelloServer } from '@myorg/my-lib/server'; ``` **Usage:** ```bash nx generate @nx/next:library [options] ``` **Aliases:** `lib` **Arguments:** ```bash nx generate @nx/next:library [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--appProject` | string | The application project to add the library route to. | | | `--buildable` | boolean | Generate a buildable library that uses rollup to bundle. | `false` | | `--bundler` | string | The bundler to use. Choosing 'none' means this library is not buildable. | `"none"` | | `--compiler` | string | Which compiler to use. | `"babel"` | | `--component` | boolean | Generate a default component. | `true` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--globalCss` | boolean | When `true`, the stylesheet is generated using global CSS instead of CSS modules (e.g. file is `*.css` rather than `*.module.css`). | `false` | | `--importPath` | string | The library name used to import it, like `@myorg/my-awesome-lib`. | | | `--inSourceTests` | boolean | When using Vitest, separate spec files will not be generated and instead will be included within the source files. | `false` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | Library name | | | `--publishable` | boolean | Create a publishable library. | | | `--routing` | boolean | Generate library with routes. | | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--skipTsConfig` | boolean | Do not update `tsconfig.json` for development experience. | `false` | | `--strict` | boolean | Whether to enable tsconfig strict mode or not. | `true` | | `--style` | string | The file extension to be used for style files. | `"css"` | | `--tags` | string | Add tags to the library (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `page` Create a Page for Next. ### Examples ###### Create a Static Page Generate a static page named `MyPage` at `apps/my-app/pages/my-page/page.tsx`: ```shell nx g page apps/my-app/pages/my-page ``` ###### Create a Dynamic Page Generate a dynamic page at `apps/my-app/pages/products/[id]/page.tsx`: ```shell nx g page "apps/my-app/pages/products/[id]" ``` **Usage:** ```bash nx generate @nx/next:page [options] ``` **Arguments:** ```bash nx generate @nx/next:page [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--export` | boolean | When true, the component is exported from the project `index.ts` (if it exists). | `false` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--name` | string | The page symbol name. Defaults to the page directory name. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--style` | string | The file extension to be used for style files. | `"css"` | | `--withTests` | boolean | When true, creates a `spec.ts` test file for the new page. | `false` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/next: --help ``` --- ## Guides {% index_page_cards path="technologies/react/next/guides" /%} --- ## Nx with Next.js Nx helps you organize a Next.js monorepo where apps and shared libraries live side by side. The Next.js plugin for Nx, `@nx/next`, automatically [infers `build`, `dev`, and `start` tasks](#how-nxnext-infers-tasks) from your Next.js configuration and provides [generators for creating applications and libraries](#develop-nextjs-applications). You don't need the plugin to use Next.js with Nx. Any project already benefits from [caching](/docs/features/cache-task-results), [task orchestration](/docs/features/run-tasks), and the [project graph](/docs/features/explore-graph). The plugin adds automatic task inference, code generators, and simplified configuration. ## Requirements The `@nx/next` plugin supports the following package versions. | Package | Supported Versions | | ------- | ------------------ | | `next` | >=15.0.0 <17.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setting up @nx/next ### Add to an existing Nx workspace {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/next` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). Using `nx add` helps ensure the correct version is installed and configured. {% /aside %} ```shell nx add @nx/next ``` Verify the plugin is setup: {% steps %} 1. Check for `@nx/next` listed as a plugin: ```shell nx report ``` 2. Ensure inferred tasks are working: ```shell nx show projects --with-target=build ``` 3. Inspect a specific project configuration: ```shell nx show project my-next-app ``` {% /steps %} ### Create a new workspace Start from the [Next.js template](/docs/templates/nextjs) to scaffold a Next.js app with a shared UI library: ```shell npx create-nx-workspace@latest my-workspace --template nrwl/nextjs-template ``` ### Generate a new application ```shell nx g @nx/next:app apps/my-app ``` To start the application in development mode, run `nx dev my-app`. Read more about the options available for the [application generator](/docs/technologies/react/next/generators#application). ### Generate a library ```shell nx g @nx/next:lib libs/my-lib # For a publishable library nx g @nx/next:lib libs/my-lib --publishable --importPath=@myorg/my-lib ``` Read more about the options available for the [library generator](/docs/technologies/react/next/generators#library). ## Develop Next.js applications ### Serve for development ```shell nx dev my-app ``` Starts the Next.js development server at http://localhost:3000 by default. ### Build for production ```shell nx build my-app ``` The output goes to the `.next` folder inside your app's project directory by default. Customize the output directory with `distDir` in your `next.config.js`: ```javascript // apps/my-app/next.config.js const nextConfig = { distDir: 'dist', }; module.exports = nextConfig; ``` ### Start production server ```shell nx start my-app ``` Serves the production build. Depends on `build` completing first. ### Use libraries in your application ```typescript // apps/my-app/pages/index.tsx import { MyComponent } from '@myorg/my-lib'; export default function Index() { return ; } ``` There is no need to build the library prior to using it. When you update your library, the Next.js application automatically picks up the changes. ## Configure @nx/next ### How @nx/next infers tasks The `@nx/next` plugin creates tasks for any project that has a Next.js configuration file. Any of the following files are recognized: - `next.config.js` - `next.config.cjs` - `next.config.mjs` - `next.config.ts` The configuration directory must also contain a `package.json` or `project.json`. ### Plugin options Configure `@nx/next/plugin` in the `plugins` array in `nx.json`: ```json // nx.json { "plugins": [ { "plugin": "@nx/next/plugin", "options": { "buildTargetName": "build", "devTargetName": "dev", "startTargetName": "start", "buildDepsTargetName": "build-deps", "watchDepsTargetName": "watch-deps" } } ] } ``` | Option | Description | Default | | ----------------------- | ------------------------------------------ | -------------- | | `buildTargetName` | Name of the cached `next build` task. | `build` | | `devTargetName` | Name of the continuous `next dev` task. | `dev` | | `startTargetName` | Name of the continuous `next start` task. | `start` | | `serveStaticTargetName` | Deprecated alias for `startTargetName`. | `serve-static` | | `buildDepsTargetName` | Name of an optional dependency build task. | none | | `watchDepsTargetName` | Name of an optional dependency watch task. | none | The build task is cached, with outputs based on your Next.js configuration, and depends on builds of project dependencies. The start task depends on the build task. ### Exclude or include specific projects Use `include`/`exclude` glob patterns in the plugin configuration to scope which projects the plugin applies to: ```json // nx.json { "plugins": [ { "plugin": "@nx/next/plugin", "include": ["apps/**/*"], "exclude": ["apps/legacy-app/**/*"], "options": { "buildTargetName": "build", "devTargetName": "dev", "startTargetName": "start", "buildDepsTargetName": "build-deps", "watchDepsTargetName": "watch-deps" } } ] } ``` ### View inferred tasks To see what tasks Nx inferred for a project: ```shell nx show project my-app ``` Or open the [project details view in Nx Console](/docs/kb/console-project-details#_top). A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter for these tasks must use the exact identifier `@nx/next/plugin`. ## Set up CI for your Next.js monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). ## What's next {% cardgrid %} {% linkcard title="Deploy Next.js to Vercel" description="Deploy your Next.js application to Vercel with Nx." href="/docs/kb/deploy-nextjs-to-vercel" /%} {% linkcard title="Next.js Config Setup" description="Configure Next.js settings for your Nx workspace." href="/docs/kb/next-config-setup" /%} {% linkcard title="Learn Nx Tutorial" description="Build a monorepo step-by-step with Nx." href="/docs/getting-started/tutorials/crafting-your-workspace" /%} {% linkcard title="Generators Reference" description="Full API reference for @nx/next generators." href="/docs/technologies/react/next/generators" /%} {% linkcard title="Executors Reference" description="Full API reference for @nx/next executors." href="/docs/technologies/react/next/executors" /%} {% linkcard title="Migrations Reference" description="Full reference for @nx/next migrations." href="/docs/technologies/react/next/migrations" /%} {% /cardgrid %} --- ## @nx/next Migrations For an overview of the plugin and setup instructions, see the [@nx/next introduction](/docs/technologies/react/next/introduction). The @nx/next plugin provides various migrations to help you migrate to newer versions of next projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.2.x ### `update-23-2-0-add-svgr-webpack-if-used` **Version**: 23.2.0-beta.4 Add `@svgr/webpack` when a next config references it. #### Add `@svgr/webpack` If Used Adds `@svgr/webpack` to the workspace when a next config references it. `@svgr/webpack` is no longer a dependency of `@nx/next`, so workspaces whose next configs resolve it - which is what the Nx 22 `add-svgr-to-next-config` migration inlined - must declare it themselves. This migration backfills it for those workspaces. Workspaces that already have `@svgr/webpack` are left untouched. #### Examples ##### Before ```jsonc title="package.json" { "devDependencies": { "@nx/next": "23.1.0", }, } ``` ##### After ```jsonc title="package.json" { "devDependencies": { "@nx/next": "23.1.0", "@svgr/webpack": "^8.0.1", }, } ``` ## 23.1.x ### `update-23-1-0-create-ai-instructions-for-next-15` **Version**: 23.1.0-beta.0 Create AI instructions to help migrate workspaces from Next.js 14 to 15. #### Requires | Name | Version | |------|---------| `next` | `>=15.0.0 <16.0.0` | #### Upgrade Next.js 14 to 15 Bumps Next.js from 14 to 15 (and `eslint-config-next` to match). The main breaking change is that the request APIs (`params`, `searchParams`, `cookies`, `headers`, `draftMode`) are now asynchronous. App Router projects also require React 19; Page Router projects can stay on React 18. Read more in the [Next.js 15 upgrade guide](https://nextjs.org/docs/app/guides/upgrading/version-15). The paired AI instructions migration walks an agent through the full set of changes. The common ones are shown below. #### Examples ##### Before ```tsx title="app/blog/[slug]/page.tsx" export default function Page({ params, searchParams }) { const { slug } = params; const query = searchParams.q; return

{slug}

; } ``` ##### After ```tsx title="app/blog/[slug]/page.tsx" export default async function Page(props) { const { slug } = await props.params; const { q: query } = await props.searchParams; return

{slug}

; } ``` `cookies`, `headers`, and `draftMode` are awaited the same way (`const store = await cookies();`). #### Page Router Pages Router data functions are not affected: `getServerSideProps` / `getStaticProps` / `getStaticPaths` `context.params` stays synchronous - leave it unchanged. ### 23.1.0-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `next` | `~15.5.18` | Updated only | `eslint-config-next` | `^15.5.18` | Updated only ## 23.0.x ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/next/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/next` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/next/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/next/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/next/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/next/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ## 22.6.x ### 22.6.0-package-updates **Version**: 22.6.0-beta.10 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `next` | `~16.1.6` | Updated only | `eslint-config-next` | `^16.1.6` | Updated only ## 22.2.x ### `update-22-2-0-create-ai-instructions-for-next-16` **Version**: 22.2.0-beta.1 Create AI Instructions to help migrate users workspaces to Next.js 16. #### Requires | Name | Version | |------|---------| `next` | `>=16.0.0` | ### 22.2.0-beta.1-package-updates **Version**: 22.2.0-beta.1 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `next` | `~16.0.1` | Updated only | `eslint-config-next` | `^16.0.1` | Updated only ## 22.0.x ### `update-22-0-0-add-svgr-to-next-config` **Version**: 22.0.0-beta.0 Updates next.config.js files to add SVGR webpack configuration directly instead of using the nx.svgr option in withNx. --- ## React Native Get started with [Nx with React Native](/docs/technologies/react/react-native/introduction), or browse the topics below. {% index_page_cards path="technologies/react/react-native" /%} --- ## @nx/react-native Executors The @nx/react-native plugin provides various executors to help you create and configure react-native projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/react-native introduction](/docs/technologies/react/react-native/introduction). ### `build-android` Build target options for Android. `project.json`: ```json { "name": "mobile", //... "targets": { //... "build-android": { "executor": "@nx/react-native:build-android", "outputs": [ "{projectRoot}/build/outputs/bundle", "{projectRoot}/build/outputs/apk" ], "options": {} } } } ``` ```bash nx run mobile:build-android ``` ### Examples ###### Build with custom tasks The `tasks` option accepts any custom gradle task, such as `assembleDebug`, `assembleRelease`, `bundleDebug`, `bundleRelease`, `installDebug`, `installRelease`. For example, pass in `bundleRelease` or `bundleRelease` to tasks, it will create with `.aab` extension under bundle folder. Pass in `assembleDebug` or `assembleRelease` to tasks, it will create a build with `.apk` extension under apk folder. Pass in `installDebug` or `installRelease` to tasks, it will create a build with `.apk` extension and immediately install it on a running emulator or connected device. ```json "build-android": { "executor": "@nx/react-native:build-android", "outputs": [ "{projectRoot}/build/outputs/bundle", "{projectRoot}/build/outputs/apk" ], "options": { "tasks": ["bundleRelease"] } } ``` ###### Build for debug/release The `mode` option allows you determine whether to build for debug/release apk. ```json "build-android": { "executor": "@nx/react-native:build-android", "outputs": [ "{projectRoot}/build/outputs/bundle", "{projectRoot}/build/outputs/apk" ], "options": { "mode": "debug" } } ``` ###### Build for current device architecture The `activeArchOnly` option allows you to build native libraries only for the current device architecture for debug builds. ```json "build-android": { "executor": "@nx/react-native:build-android", "outputs": [ "{projectRoot}/build/outputs/bundle", "{projectRoot}/build/outputs/apk" ], "options": { "activeArchOnly": true } } ``` --- #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `activeArchOnly` | boolean | Build native libraries only for the current device architecture for debug builds. | `false` | | `extraParams` | string | Custom params passed to gradle build command | | | `interactive` | boolean | Explicitly select build type and flavour to use before running a build | | | `mode` | string | Specify your app's build variant | `"debug"` | | `port` | number | The port where the packager server is listening on. | `8081` | | `resetCache` | boolean | Resets metro cache. | `false` | | `tasks` | string | Run custom Gradle tasks. By default it's "assembleDebug". Will override passed mode and variant arguments. | | ### `build-ios` Build iOS app. `project.json`: ```json { "name": "mobile", //... "targets": { //... "build-ios": { "executor": "@nx/react-native:build-ios", "options": {} } } } ``` ```bash nx run mobile:build-ios ``` ### Examples ###### Build in Specific Location The `buildFolder` option allows to specify the location for ios build artifacts. It corresponds to Xcode's -derivedDataPath. ```json "build-ios": { "executor": "@nx/react-native:build-ios", "options": { "buildFolder": "dist/ios/build" } } ``` ```bash nx build-ios --buildFolder=dist/ios/build ``` ###### Build the Debug/Release app The `mode` option allows to specify the xcode configuartion, such as `Debug` or `Release`. ```json "build-ios": { "executor": "@nx/react-native:build-ios", "options": { "mode": "Release" } } ``` ```bash nx build-ios --mode=Debug nx build-ios --mode=Release ``` ###### Build for a simulator The `simulator` option allows you to launch your iOS app in a specific simulator: To see all the available simulators, run command: ```bash xcrun simctl list devices available ``` ```json "build-ios": { "executor": "@nx/react-native:build-ios", "options": { "simulator": "iPhone 14 Pro" } } ``` ```bash nx build-ios --simulator="iPhone 14 Pro" ``` ###### Build for a device The `device` option allows you to launch your iOS app in a specific device. To see all the available device, run command: ```bash xcrun simctl list devices available ``` ```json "build-ios": { "executor": "@nx/react-native:build-ios", "options": { "device": "deviceName" } } ``` ```bash nx build-ios --device="deviceName" ``` ###### Set Device by udid The `udid` option allows you to explicitly set device to use by udid. To see all the available simulators and devices with udid, run command: ```bash xcrun simctl list devices available ``` ```json "build-ios": { "executor": "@nx/react-native:build-ios", "options": { "udid": "device udid" } } ``` ```bash nx build-ios --udid="device udid" ``` --- #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `buildFolder` | string | Location for iOS build artifacts. Corresponds to Xcode's "-derivedDataPath". Relative to ios directory | `"./build"` | | `device` | string | Explicitly set device to use by name. The value is not required if you have a single device connected. | | | `extraParams` | string | Custom params that will be passed to xcodebuild command. | | | `interactive` | boolean | Explicitly select which scheme and configuration to use before running a build | | | `mode` | string | Explicitly set the scheme configuration to use | `"Debug"` | | `port` | number | The port where the packager server is listening on. | `8081` | | `resetCache` | boolean | Resets metro cache. | `false` | | `scheme` | string | Explicitly set Xcode scheme to use | | | `simulator` | string | Explicitly set simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version: "iPhone 6 (10.0)" | | | `udid` | string | Explicitly set device to use by udid | | | `verbose` | boolean | Do not use xcbeautify or xcpretty even if installed | | | `xcconfig` | string | Explicitly set xcconfig to use | | ### `bundle` JS Bundle target options. `project.json`: ```json { "name": "mobile", //... "targets": { //... "bundle-ios": { "executor": "@nx/react-native:bundle", "outputs": ["{projectRoot}/build"], "options": { "entryFile": "src/main.tsx", "platform": "ios", "bundleOutput": "dist/apps/mobile/ios/main.jsbundle" } }, "bundle-android": { "executor": "@nx/react-native:bundle", "options": { "entryFile": "src/main.tsx", "platform": "android", "bundleOutput": "dist/apps/mobile/android/main.jsbundle" } } } } ``` ```bash nx run mobile:bundle-ios nx run mobile:bundle-android ``` ### Examples ###### Bundle with sourcemap The `sourcemapOutput` option allows you to specify the path of the source map relative to app folder: ```json "bundle-ios": { "executor": "@nx/react-native:bundle", "options": { "entryFile": "src/main.tsx", "platform": "ios", "bundleOutput": "dist/apps/mobile/ios/main.jsbundle", "sourcemapOutput": "../../dist/apps/mobile/ios/main.map", } }, "bundle-android": { "executor": "@nx/react-native:bundle", "options": { "entryFile": "src/main.tsx", "platform": "android", "bundleOutput": "dist/apps/mobile/android/main.jsbundle", "sourcemapOutput": "../../dist/apps/mobile/android/main.map", } } ``` ###### Create a dev/release bundle The `dev` option determines whether to create a dev or release bundle. The default value is `true`, by setting it as `false`, warnings are disabled and the bundle is minified. ```json "bundle-ios": { "executor": "@nx/react-native:bundle", "options": { "entryFile": "src/main.tsx", "platform": "ios", "bundleOutput": "dist/apps/mobile/ios/main.jsbundle", "dev": false } }, "bundle-android": { "executor": "@nx/react-native:bundle", "options": { "entryFile": "src/main.tsx", "platform": "android", "bundleOutput": "dist/apps/mobile/android/main.jsbundle", "dev": false } } ``` ###### Create a minified bundle The `minify` option allows you to create a minified bundle: ```json "bundle-ios": { "executor": "@nx/react-native:bundle", "options": { "entryFile": "src/main.tsx", "platform": "ios", "bundleOutput": "dist/apps/mobile/ios/main.jsbundle", "minify": true } }, "bundle-android": { "executor": "@nx/react-native:bundle", "options": { "entryFile": "src/main.tsx", "platform": "android", "bundleOutput": "dist/apps/mobile/android/main.jsbundle", "minify": true } } ``` --- #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `bundleOutput` | string [**required**] | The output path of the generated files. | | | `entryFile` | string [**required**] | The entry file relative to project root. | | | `platform` | any [**required**] | Platform to build for. | | | `assetsDest` | string | Directory name where to store assets referenced in the bundle. | | | `dev` | boolean | Generate a development build. | `true` | | `maxWorkers` | number | The number of workers we should parallelize the transformer on. | | | `minify` | boolean | Allows overriding whether bundle is minified. | | | `readGlobalCache` | boolean | Try to fetch transformed JS code from the global cache, if configured. | `false` | | `resetCache` | boolean | Removes cached files. | `false` | | `sourcemapOutput` | string | File name where to store the sourcemap file for resulting bundle, ex. /tmp/groups.map. | | | `sourcemapSourcesRoot` | string | Path to make sourcemaps sources entries relative to, ex. /root/dir. | | | `sourcemapUseAbsolutePath` | boolean | Report SourceMapURL using its full path. | `false` | | `transformer` | string | Specify a custom transformer to be used. | | ### `ensure-symlink` Ensure workspace node_modules is symlink under app's node_modules folder. ### `pod-install` Run `pod install` for React Native iOS Project. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `buildFolder` | string [**required**] | Location for iOS build artifacts. Corresponds to Xcode's "-derivedDataPath". Relative to ios directory. | `"./build"` | | `deployment` | boolean | Disallow any changes to the Podfile or the Podfile.lock during installation. | `false` | | `repoUpdate` | boolean | Force running `pod repo update` before install. | `false` | | `useBundler` | boolean | Run cocoapods within a Bundler environment, i.e. with the `bundle exec pod install` command | `false` | ### `run-android` Run Android target options. `project.json`: ```json { "name": "mobile", //... "targets": { //... "run-android": { "executor": "@nx/react-native:run-android", "options": {} } } } ``` ```bash nx run mobile:run-android ``` ### Examples ###### Run on a specific device/simulator To see all the available emulators, run command: ```bash emulator -list-avds ``` The `deviceId` option allows you to launch your android app in a specific device/simulator: ```json "run-android": { "executor": "@nx/react-native:run-android", "options": { "deviceId": "Pixel_5_API_30" } } ``` ###### Run the debug/release app The `mode` option allows to specify the build variant, such as `debug` or `release`. ```json "run-android": { "executor": "@nx/react-native:run-android", "options": { "mode": "release" } } ``` --- #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `activeArchOnly` | boolean | Build native libraries only for the current device architecture for debug builds. | `false` | | `appId` | string | Specify an `applicationId` to launch after build. If not specified, `package` from `AndroidManifest.xml` will be used. | | | `appIdSuffix` | string | Specify an `applicationIdSuffix` to launch after build. | | | `binaryPath` | string | Path relative to project root where pre-built .apk binary lives. | | | `deviceId` | string | Builds your app and starts it on a specific device/simulator with the given device id (listed by running `adb devices` on the command line). | | | `extraParams` | string | Custom params passed to gradle build command | | | `interactive` | boolean | Explicitly select build type and flavour to use before running a build | | | `listDevices` | boolean | Lists all available Android devices and simulators and let you choose one to run the app | | | `mainActivity` | string | Name of the activity to start. | `"MainActivity"` | | `mode` | string | Specify your app's build variant | `"debug"` | | `port` | number | The port where the packager server is listening on. | `8081` | | `resetCache` | boolean | Resets metro cache. | `false` | | `tasks` | string | Run custom Gradle tasks. By default it's "assembleDebug". Will override passed mode and variant arguments. | | ### `run-ios` Run iOS target options. `project.json`: ```json { "name": "mobile", //... "targets": { //... "run-ios": { "executor": "@nx/react-native:run-ios", "options": {} } } } ``` ```bash nx run mobile:run-ios ``` ### Examples ###### Build the Debug/Release app The `mode` option allows to specify the xcode configuration schema, such as `Debug` or `Release`. ```json "run-ios": { "executor": "@nx/react-native:run-ios", "options": { "mode": "Release" } } ``` ```bash nx run-ios --mode=Debug ``` ###### Run on a simulator The `simulator` option allows you to launch your iOS app in a specific simulator. To see all the available simulators, run command: ```bash xcrun simctl list devices available ``` ```json "run-ios": { "executor": "@nx/react-native:run-ios", "options": { "simulator": "iPhone 14 Pro (16.2)" } } ``` ```bash nx run-ios --simulator="iPhone 14 Pro (16.2)" ``` ###### Run on a device The `device` option allows you to launch your iOS app in a specific device. To see all the available devices, run command: ```bash xcrun simctl list devices available ``` ```json "run-ios": { "executor": "@nx/react-native:run-ios", "options": { "device": "deviceName" } } ``` ```bash nx run-ios --device="deviceName" ``` ###### Set Device by udid The `udid` option allows you to explicitly set device to use by udid. To see all the available simulators and devices with udid, run command: ```bash xcrun simctl list devices available ``` ```json "run-ios": { "executor": "@nx/react-native:run-ios", "options": { "udid": "device udid" } } ``` ```bash nx run-ios --udid="device udid" ``` --- #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `binaryPath` | string | Path relative to project root where pre-built .app binary lives. | | | `buildFolder` | string | Location for iOS build artifacts. Corresponds to Xcode's "-derivedDataPath". Relative to ios directory. | | | `device` | string | Explicitly set device to use by name. The value is not required if you have a single device connected. | | | `extraParams` | string | Custom params that will be passed to xcodebuild command. | | | `interactive` | boolean | Explicitly select which scheme and configuration to use before running a build | | | `mode` | string | Explicitly set the scheme configuration to use | `"Debug"` | | `port` | number | The port where the packager server is listening on. | `8081` | | `resetCache` | boolean | Resets metro cache. | `false` | | `scheme` | string | Explicitly set Xcode scheme to use | | | `simulator` | string | Explicitly set simulator to use. Optionally include iOS version between parenthesis at the end to match an exact version: "iPhone 6 (10.0)" | | | `udid` | string | Explicitly set device to use by udid | | | `verbose` | boolean | Do not use xcbeautify or xcpretty even if installed | | | `xcconfig` | string | Explicitly set xcconfig to use | | ### `start` Packager Server target options. `project.json`: ```json { "name": "mobile", //... "targets": { //... "start": { "executor": "@nx/react-native:start", "options": { "port": 8081 } } } } ``` ```bash nx run mobile:start ``` ### Examples ###### Starts the server non-interactively The `interactive` option allows you to specify whether to use interactive mode: ```json "start": { "executor": "@nx/react-native:start", "options": { "port": 8081, "interactive": false } } ``` ###### Starts the server with cache reset The `resetCache` option allows you to remove cached files. ```json "start": { "executor": "@nx/react-native:start", "options": { "port": 8081, "resetCache": true } } ``` --- #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `interactive` | boolean | Run packager server in interactive mode. | `true` | | `port` | number | The port to listen on. | `8081` | | `resetCache` | boolean | Resets metro cache. | `false` | ### `storybook` Load stories for react native. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `outputFile` | string [**required**] | The output file that will be written. It is relative to the project directory. | `"./.storybook/story-loader.ts"` | | `pattern` | string [**required**] | The pattern of files to look at. It can be a specific file, or any valid glob. Note: if using the CLI, globs with `**/*...` must be escaped with quotes | `"**/*.stories.@(js|jsx|ts|tsx|md)"` | | `searchDir` | array [**required**] | The directory or directories, relative to the project root, to search for files in. | `[]` | ### `sync-deps` Updates `package.json` with project dependencies. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `all` | boolean | Copy all dependencies and devDependencies from the workspace root package.json. | `false` | | `exclude` | array | An array of npm packages to exclude. | `[]` | | `include` | array | An array of additional npm packages to include. | `[]` | ### `upgrade` Upgrade React Native code for project. --- ## @nx/react-native Generators The @nx/react-native plugin provides various generators to help you create and configure react-native projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/react-native introduction](/docs/technologies/react/react-native/introduction). ## `application` Create a React Application for Nx. **Usage:** ```bash nx generate @nx/react-native:application [options] ``` **Aliases:** `app` **Arguments:** ```bash nx generate @nx/react-native:application [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--bundler` | string | The bundler to use. | `"vite"` | | `--displayName` | string | The display name to show in the application. Defaults to name. | | | `--e2eTestRunner` | string | Adds the specified e2e test runner. | `"none"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--formatter` | string | The tool to use for code formatting. | | | `--install` | boolean | Runs `pod install` for native modules before building iOS app. | `true` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | The name of the application. | | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--tags` | string | Add tags to the application (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `component` Create a React native Component for Nx. **Usage:** ```bash nx generate @nx/react-native:component [options] ``` **Aliases:** `c` **Arguments:** ```bash nx generate @nx/react-native:component [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--classComponent` | boolean | Use class components instead of functional component. | `false` | | `--export` | boolean | When true, the component is exported from the project `index.ts` (if it exists). | `false` | | `--name` | string | The component symbol name. Defaults to the last segment of the file path. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipTests` | boolean | When true, does not create `spec.ts` test files for the new component. | `false` | ## `convert-to-inferred` Convert existing React Native project(s) using `@nx/react-native:*` executors to use `@nx/react-native/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target. **Usage:** ```bash nx generate @nx/react-native:convert-to-inferred [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/react-native:*` executors to use `@nx/react-native/plugin`. | | | `--skipFormat` | boolean | Whether to format files at the end of the migration. | `false` | ## `library` Create a React Native Library for Nx. **Usage:** ```bash nx generate @nx/react-native:library [options] ``` **Aliases:** `lib` **Arguments:** ```bash nx generate @nx/react-native:library [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--buildable` | boolean | Generate a buildable library. | `false` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--importPath` | string | The library name used to import it, like `@myorg/my-awesome-lib`. | | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | Library name. | | | `--publishable` | boolean | Create a publishable library. | | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--skipTsConfig` | boolean | Do not update `tsconfig.json` for development experience. | `false` | | `--strict` | boolean | Whether to enable tsconfig strict mode or not. | `true` | | `--tags` | string | Add tags to the library (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `web-configuration` Setup web configuration to React Native apps using react-native-web. **Usage:** ```bash nx generate @nx/react-native:web-configuration [options] ``` **Arguments:** ```bash nx generate @nx/react-native:web-configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--bundler` | string [**required**] | The bundler to use. | `"vite"` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/react-native: --help ``` --- ## Nx with React Native In a React Native monorepo, Nx lets you share libraries across apps and run only the tasks a change affects. React Native brings the React declarative UI framework to iOS and Android. With React Native, you use native UI controls and have full access to the native platform. The Nx Plugin for React Native contains generators for managing React Native applications and libraries within an Nx workspace. It provides: - Integration with libraries such as Jest, Detox, and Storybook. - Scaffolding for creating buildable libraries that can be published to npm. - Utilities for automatic workspace refactoring. ## Requirements The `@nx/react-native` plugin supports the following package versions. | Package | Supported Versions | | -------------- | -------------------- | | `metro-config` | >= 0.82.0 | | `react-native` | ^0.83.0 \|\| ^0.84.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setting up React native ### Create a new workspace To create a new workspace with React Native, run the following command: ```shell npx create-nx-workspace@latest your-workspace-name --preset=react-native --appName=your-app-name ``` {% aside type="note" title="Don't know what you need?" %} You can also run the command without arguments to go through the interactive prompts. {% /aside %} ```shell npx create-nx-workspace your-workspace-name ``` ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/react-native` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/react-native` by running the following command: ```shell nx add @nx/react-native ``` This will install the correct version of `@nx/react-native`. ### How @nx/react-native infers tasks {% aside type="note" title="Inferred Tasks" %} Nx plugins can infer tasks for your projects based on the configuration of different tools. You can read more about it at the [Inferred Tasks concept page](/docs/concepts/mental-model#inferred-tasks). {% /aside %} The `@nx/react-native` plugin will create a task for any project that has an app configuration file present. Any of the following files will be recognized as an app configuration file: - `app.config.js` - `app.config.ts` - `app.json` The directory must contain both `package.json` and `metro.config.js`. React Native inference excludes projects whose app configuration has an `expo` key or whose `package.json` lists `expo` as a dependency or development dependency. The `@nx/expo/plugin` handles those projects. ### View inferred tasks To view inferred tasks for a project, open the [project details view](/docs/concepts/mental-model#inferred-tasks) in Nx Console or run `nx show project my-project --web` in the command line. ### @nx/react-native configuration The `@nx/react-native/plugin` is configured in the `plugins` array in `nx.json`. ```json // nx.json { "plugins": [ { "plugin": "@nx/react-native/plugin", "options": { "startTargetName": "start", "podInstallTargetName": "pod-install", "bundleTargetName": "bundle", "runIosTargetName": "run-ios", "runAndroidTargetName": "run-android", "buildIosTargetName": "build-ios", "buildAndroidTargetName": "build-android", "syncDepsTargetName": "sync-deps", "upgradeTargetName": "upgrade" } } ] } ``` | Option | Default | Inferred behavior | | ------------------------ | --------------- | -------------------------------------------------------------------- | | `startTargetName` | `start` | Runs `react-native start` continuously. | | `podInstallTargetName` | `pod-install` | Runs `pod install` after the dependency synchronization task. | | `bundleTargetName` | `bundle` | Runs `react-native bundle` after bundling project dependencies. | | `runIosTargetName` | `run-ios` | Runs `react-native run-ios` continuously. | | `runAndroidTargetName` | `run-android` | Runs `react-native run-android` continuously. | | `buildIosTargetName` | `build-ios` | Runs and caches `react-native build-ios` with the iOS build outputs. | | `buildAndroidTargetName` | `build-android` | Runs and caches `react-native build-android` with Android outputs. | | `syncDepsTargetName` | `sync-deps` | Runs the `@nx/react-native:sync-deps` executor. | | `upgradeTargetName` | `upgrade` | Runs `react-native upgrade`. | Use `include` and `exclude` glob patterns on the plugin entry to scope inference. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/react-native/plugin`. ### Generating applications To create additional React Native apps run: ```shell nx g @nx/react-native:app apps/ ``` ### Generating libraries To generate a new library run: ```shell nx g @nx/react-native:lib libs/ ``` ### Generating components To generate a new component inside library run: ```shell nx g @nx/react-native:component --export ``` Replace `` with the directory where you want to place the component. It must be a path to a directory relative to the workspace root and located inside the library project root. ### Upgrade React Native The Nx CLI provides the [`migrate` command](/docs/features/automate-updating-dependencies) to help you stay up to date with the latest version of Nx. #### Use upgrade-native generator To upgrade native iOS and Android code to latest, you can use the [upgrade-native](/docs/technologies/react/react-native/generators#upgrade-native) generator: ```shell nx generate @nx/react-native:upgrade-native apps/ ``` This is a command that will replace the iOS and Android native code folder entirely. #### Upgrade manually You can also upgrade React Native iOS and Android code using the [rn-diff-purge](https://react-native-community.github.io/upgrade-helper/) project. ### Start metro server To start the server that communicates with connected devices: ```shell nx start ``` ### Run iOS To build your app and start it on iOS simulator or device: ```shell nx run-ios ``` ### Run android To build your app and start it on a connected Android emulator or device: ```shell nx run-android ``` ### Build iOS To build an iOS app: ```shell nx build-ios ``` The build artifacts will be located under `/ios/build`. You can specify the build folder by setting the `buildFolder` option: ```shell nx build-ios --buildFolder="./build" ``` ### Build android To build an Android app, run: ```shell nx build-android ``` The build artifacts will be located under `/android/app/build`. ## More documentation - [Using Detox](/docs/technologies/test-tools/detox/introduction) - [Using Jest](/docs/technologies/test-tools/jest/introduction) ## Set up CI for your React Native monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/react-native Migrations For an overview of the plugin and setup instructions, see the [@nx/react-native introduction](/docs/technologies/react/react-native/introduction). The @nx/react-native plugin provides various migrations to help you migrate to newer versions of react-native projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.0.x ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/react-native/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/react-native` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/react-native/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/react-native/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/react-native/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/react-native/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ### 23.0.0-package-updates **Version**: 23.0.0-beta.25 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `react-native` | `~0.84.1` | Updated only | `@react-native-community/cli` | `~20.1.0` | Added if not installed | `@react-native-community/cli-platform-android` | `~20.1.0` | Added if not installed | `@react-native-community/cli-platform-ios` | `~20.1.0` | Added if not installed | `@react-native/babel-preset` | `~0.84.1` | Updated only | `@react-native/metro-config` | `~0.84.1` | Updated only | `react-native-web` | `~0.21.0` | Updated only | `react` | `^19.2.0` | Updated only | `react-dom` | `^19.2.0` | Updated only | `react-test-renderer` | `^19.2.0` | Updated only | `@types/react` | `^19.2.0` | Updated only | `@types/react-dom` | `^19.2.0` | Updated only | `@testing-library/react-native` | `~13.2.0` | Updated only | `react-native-svg-transformer` | `~1.5.1` | Updated only | `react-native-svg` | `~15.15.0` | Updated only | `react-native-svg-web` | `~1.0.9` | Updated only | `@babel/runtime` | `~7.27.6` | Updated only ## 21.4.x ### `update-21-4-0-remove-deprecated-deps` **Version**: 21.4.0-beta.0 Remove deprecated dependencies from package.json #### Requires | Name | Version | |------|---------| `react-native` | `>=0.76.0 <0.79.0` | ### `update-21-4-0-upgrade-react-native-projects` **Version**: 21.4.0-beta.0 Run nx upgrade for each React Native project ### 21.4.0-package-updates **Version**: 21.4.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `react-native` | `~0.79.3` | Updated only | `@react-native-community/cli` | `~18.0.0` | Added if not installed | `@react-native-community/cli-platform-android` | `~18.0.0` | Added if not installed | `@react-native-community/cli-platform-ios` | `~18.0.0` | Added if not installed | `@react-native/babel-preset` | `~0.79.3` | Updated only | `@react-native/metro-config` | `~0.79.3` | Updated only | `react-native-web` | `~0.20.0` | Updated only | `react` | `19.0.0` | Updated only | `react-dom` | `19.0.0` | Updated only | `react-test-renderer` | `~19.0.0` | Updated only | `@types/react` | `~19.0.10` | Updated only | `@types/react-dom` | `~19.0.6` | Updated only | `@testing-library/react-native` | `~13.2.0` | Updated only | `react-native-svg-transformer` | `~1.5.1` | Updated only | `react-native-svg` | `~15.11.2` | Updated only | `react-native-svg-web` | `~1.0.9` | Updated only | `@babel/runtime` | `~7.27.6` | Updated only --- ## Remix Get started with [Nx with Remix](/docs/technologies/react/remix/introduction), or browse the topics below. {% index_page_cards path="technologies/react/remix" /%} --- ## @nx/remix Executors The @nx/remix plugin provides various executors to help you create and configure remix projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/remix introduction](/docs/technologies/react/remix/introduction). ### `build` Build a Remix app. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `outputPath` | string [**required**] | The output path of the generated files. | | | `generateLockfile` | boolean | Generate a lockfile (e.g. package-lock.json) that matches the workspace lockfile to ensure package versions match. | `false` | | `generatePackageJson` | boolean | Generate package.json file in the output folder. | `false` | | `includeDevDependenciesInPackageJson` | boolean | Include `devDependencies` in the generated package.json file. By default only production `dependencies` are included. | `false` | | `skipOverrides` | boolean | Do not add a `overrides` and `resolutions` entries to the generated package.json file. Only works in conjunction with `generatePackageJson` option. | | | `skipPackageManager` | boolean | Do not add a `packageManager` entry to the generated package.json file. Only works in conjunction with `generatePackageJson` option. | | | `sourcemap` | boolean | Generate source maps for production. | `false` | ### `serve` Serve a Remix app. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `command` | string | Command used to run your app server. | | | `debug` | boolean | Attach a Node.js inspector. | `false` | | `devServerPort` | number | Port to start the dev server on. | | | `manual` | boolean | Enable manual mode | `false` | | `port` | number | Set PORT environment variable that can be used to serve the Remix application. | `4200` | | `tlsCert` | string | Path to TLS certificate (cert.pem). | | | `tlsKey` | string | Path to TLS key (key.pem). | | --- ## @nx/remix Generators The @nx/remix plugin provides various generators to help you create and configure remix projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/remix introduction](/docs/technologies/react/remix/introduction). ## `action` Generate an action for a given route. **Usage:** ```bash nx generate @nx/remix:action [options] ``` **Arguments:** ```bash nx generate @nx/remix:action [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| ## `application` Generate a new Remix application. **Usage:** ```bash nx generate @nx/remix:application [options] ``` **Aliases:** `app` **Arguments:** ```bash nx generate @nx/remix:application [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--e2eTestRunner` | string | Test runner to use for e2e tests | `"none"` | | `--formatter` | string | The tool to use for code formatting. | | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | The name of the application. | | | `--rootProject` | boolean | | `false` | | `--skipFormat` | boolean | Skip formatting files | `false` | | `--tags` | string | Add tags to the project (used for linting) | | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `convert-to-inferred` Convert existing Remix project(s) using `@nx/remix:*` executors to use `@nx/remix/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target. **Usage:** ```bash nx generate @nx/remix:convert-to-inferred [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/remix:*` executors to use `@nx/remix/plugin`. | | | `--skipFormat` | boolean | Whether to format files at the end of the migration. | `false` | ## `cypress-component-configuration` Add a Cypress component testing configuration to an existing project. **Usage:** ```bash nx generate @nx/remix:cypress-component-configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string [**required**] | The name of the project to add cypress component testing configuration to | | | `--generateTests` | boolean | Generate default component tests for existing components in the project | `false` | | `--skipFormat` | boolean | Skip formatting files | `false` | ## `error-boundary` Generate an ErrorBoundary for a given route. **Usage:** ```bash nx generate @nx/remix:error-boundary [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--path` | string [**required**] | The path to route file relative to the project root. | | | `--skipFormat` | boolean | Skip formatting files after generation. | `false` | ## `library` Generate a Remix library to help structure workspace and application. **Usage:** ```bash nx generate @nx/remix:library [options] ``` **Aliases:** `lib` **Arguments:** ```bash nx generate @nx/remix:library [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--buildable` | boolean | Generate a buildable library that uses rollup to bundle. | `false` | | `--bundler` | string | The bundler to use. Choosing 'none' means this library is not buildable. | `"none"` | | `--importPath` | string | The library name used to import it, like @myorg/my-awesome-lib | | | `--js` | boolean | Generate JavaScript files rather than TypeScript files | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | Library name | | | `--skipFormat` | boolean | Skip formatting files after generator runs | `false` | | `--style` | string | Generate a stylesheet | `"css"` | | `--tags` | string | Add tags to the library (used for linting) | | | `--unitTestRunner` | string | Test Runner to use for Unit Tests | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `loader` Generate an loader for a given route. **Usage:** ```bash nx generate @nx/remix:loader [options] ``` **Arguments:** ```bash nx generate @nx/remix:loader [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| ## `meta` Generate a meta function for a given route. **Usage:** ```bash nx generate @nx/remix:meta [options] ``` **Arguments:** ```bash nx generate @nx/remix:meta [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| ## `resource-route` Generate a resource route. **Usage:** ```bash nx generate @nx/remix:resource-route [options] ``` **Arguments:** ```bash nx generate @nx/remix:resource-route [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--action` | boolean | Generate an action function | `false` | | `--loader` | boolean | Generate a loader function | `true` | | `--skipChecks` | boolean | Skip route error detection | `false` | ## `route` Generate a route. **Usage:** ```bash nx generate @nx/remix:route [options] ``` **Arguments:** ```bash nx generate @nx/remix:route [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--action` | boolean | Generate an action function | `false` | | `--loader` | boolean | Generate a loader function | `false` | | `--meta` | boolean | Generate a meta function | `false` | | `--skipChecks` | boolean | Skip route error detection | `false` | | `--style` | string | Generate a stylesheet | `"css"` | ## `storybook-configuration` Set up Storybook for a Remix library. **Usage:** ```bash nx generate @nx/remix:storybook-configuration [options] ``` **Arguments:** ```bash nx generate @nx/remix:storybook-configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--configureStaticServe` | boolean | Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times. | `true` | | `--configureTestRunner` | boolean | Add a Storybook Test-Runner target. | | | `--generateStories` | boolean | Automatically generate `*.stories.ts` files for components declared in this project? | `true` | | `--ignorePaths` | array | Paths to ignore when looking for components. | | | `--interactionTests` | boolean | Set up Storybook interaction tests. | `true` | | `--js` | boolean | Generate JavaScript story files rather than TypeScript story files. | `false` | | `--linter` | string | The tool to use for running lint checks. | | | `--tsConfiguration` | boolean | Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js. | `false` | ## `style` Generate a style import and file for a given route. **Usage:** ```bash nx generate @nx/remix:style [options] ``` **Arguments:** ```bash nx generate @nx/remix:style [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/remix: --help ``` --- ## Nx with Remix [Remix](https://remix.run/) is a full stack React framework for building web applications. In a Remix monorepo, every project benefits from Nx [caching](/docs/features/cache-task-results), [task orchestration](/docs/features/run-tasks), and the [project graph](/docs/features/explore-graph). The Remix plugin for Nx, `@nx/remix`, automatically [infers `build`, `dev`, `start`, and `typecheck` tasks](#how-nxremix-infers-tasks) from your Remix configuration and provides [generators for applications, libraries, routes, loaders, actions, and meta functions](#develop-remix-applications). You don't need the plugin to use Remix with Nx - it adds automatic task inference, route scaffolding, and simplified configuration. {% aside type="note" %} React Router v7 is the successor to Remix. `@nx/remix` supports Remix v2. [`@nx/react`](/docs/kb/react-router) handles React Router v7. For new projects, use React Router instead. Existing Remix v2 projects continue to work with `@nx/remix`. {% /aside %} ## Requirements The `@nx/remix` plugin supports the following package versions. | Package | Supported Versions | | ---------------- | ------------------ | | `@remix-run/dev` | ^2.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setting up @nx/remix ### Add to an existing Nx workspace {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/remix` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). Using `nx add` helps ensure the correct version is installed and configured. {% /aside %} ```shell nx add @nx/remix ``` Verify the plugin is setup: {% steps %} 1. Check for `@nx/remix` listed as a plugin: ```shell nx report ``` 2. Ensure inferred tasks are working: ```shell nx show projects --with-target=build ``` 3. Inspect a specific project configuration for the inferred tasks: ```shell nx show project my-remix-app ``` {% /steps %} ### Generate a new application ```shell nx g @nx/remix:app apps/my-app ``` To start the application in development mode, run `nx dev my-app`. Read more about the options available for the [application generator](/docs/technologies/react/remix/generators#application). ### Generate a library When developing your application, it often makes sense to split your codebase into smaller, more focused libraries. ```shell nx g @nx/remix:lib libs/my-lib ``` Read more about the options available for the [library generator](/docs/technologies/react/remix/generators#library). Libraries let you share code between applications, enforce architectural boundaries with the [project graph](/docs/features/explore-graph), and speed up CI by only rebuilding what changed. ## Develop Remix applications ### Generate routes ```shell nx g @nx/remix:route apps/my-app/app/routes/admin ``` This creates a new route file in your application's `routes` directory following Remix's file-based routing conventions. Read more about the options available for the [route generator](/docs/technologies/react/remix/generators#route). ### Add loaders, actions, and meta ```shell nx g @nx/remix:loader apps/my-app/app/routes/admin nx g @nx/remix:action apps/my-app/app/routes/admin nx g @nx/remix:meta apps/my-app/app/routes/admin ``` These generators add loader, action, and meta functions to existing route files. Read more about the options available for the [loader](/docs/technologies/react/remix/generators#loader), [action](/docs/technologies/react/remix/generators#action), and [meta](/docs/technologies/react/remix/generators#meta) generators. ### Serve for development ```shell nx dev my-app ``` This starts the Remix development server with hot module replacement at `http://localhost:3000`. ### Build for production ```shell nx build my-app ``` Compiles the application for production deployment. The output is written to the `build` directory inside your project folder by default. ### Start production server ```shell nx start my-app ``` Serves the production build using `remix-serve`. Depends on `build` completing first. ### Use libraries in your application Import library code directly into your route files: ```tsx // apps/my-app/app/routes/_index.tsx import { MyComponent } from '@myorg/my-lib'; export default function Index() { return ; } ``` You can also use library code in loaders by exporting from a separate server entry point: ```ts // libs/my-lib/src/server.ts export { myLoader } from './lib/my-loader'; ``` ```tsx // apps/my-app/app/routes/admin.tsx import { myLoader } from '@myorg/my-lib/server'; export const loader = myLoader; ``` ## Configure @nx/remix ### How @nx/remix infers tasks The `@nx/remix` plugin creates tasks for any project that has a Remix configuration file. The plugin recognizes two setups: **Classic Remix Compiler** -- looks for any of the following files: - `remix.config.js` - `remix.config.mjs` - `remix.config.cjs` **Remix with Vite** -- looks for `vite.config.{js,ts,mjs,mts,cjs,cts}` files that import the Remix plugin from `@remix-run/dev`. The configuration directory must also contain a `package.json` or `project.json`. ### Plugin options Configure `@nx/remix/plugin` in the `plugins` array in `nx.json`: ```json // nx.json { "plugins": [ { "plugin": "@nx/remix/plugin", "options": { "buildTargetName": "build", "devTargetName": "dev", "startTargetName": "start", "typecheckTargetName": "typecheck", "serveStaticTargetName": "serve-static", "buildDepsTargetName": "build-deps", "watchDepsTargetName": "watch-deps" } } ] } ``` | Option | Description | Default | | ----------------------- | ------------------------------------------------ | -------------- | | `buildTargetName` | Name of the cached production build task. | `build` | | `devTargetName` | Name of the continuous development server task. | `dev` | | `startTargetName` | Name of the continuous production server task. | `start` | | `typecheckTargetName` | Name of the cached TypeScript typecheck task. | `typecheck` | | `serveStaticTargetName` | Name of an alias for the production server task. | `serve-static` | | `buildDepsTargetName` | Name of an optional dependency build task. | none | | `watchDepsTargetName` | Name of an optional dependency watch task. | none | The build and typecheck tasks are cached, with build outputs based on your Remix configuration. The production server tasks depend on the build task. ### Exclude or include specific projects Use `include`/`exclude` glob patterns in the plugin configuration: ```json // nx.json { "plugins": [ { "plugin": "@nx/remix/plugin", "include": ["apps/**/*"], "exclude": ["apps/legacy-app/**/*"], "options": { "buildTargetName": "build", "devTargetName": "dev", "startTargetName": "start", "typecheckTargetName": "typecheck", "serveStaticTargetName": "serve-static", "buildDepsTargetName": "build-deps", "watchDepsTargetName": "watch-deps" } } ] } ``` ### View inferred tasks To see what tasks Nx inferred for a project: ```shell nx show project my-app ``` Or open the [project details view in Nx Console](/docs/kb/console-project-details#_top). A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter for these tasks must use the exact identifier `@nx/remix/plugin`. ## Set up CI for your Remix monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). ## What's next {% cardgrid %} {% linkcard title="React Router Guide" description="React Router is the successor to Remix. Learn how to use it with Nx." href="/docs/kb/react-router" /%} {% linkcard title="Learn Nx Tutorial" description="Build a monorepo step-by-step with Nx." href="/docs/getting-started/tutorials/crafting-your-workspace" /%} {% linkcard title="Generators Reference" description="Full API reference for @nx/remix generators." href="/docs/technologies/react/remix/generators" /%} {% linkcard title="Executors Reference" description="Full API reference for @nx/remix executors." href="/docs/technologies/react/remix/executors" /%} {% linkcard title="Migrations Reference" description="Full reference for @nx/remix migrations." href="/docs/technologies/react/remix/migrations" /%} {% /cardgrid %} --- ## @nx/remix Migrations For an overview of the plugin and setup instructions, see the [@nx/remix introduction](/docs/technologies/react/remix/introduction). The @nx/remix plugin provides various migrations to help you migrate to newer versions of remix projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.1.x ### `update-23-1-0-remove-remix-eslint-config` **Version**: 23.1.0-beta.0 Remove the unused @remix-run/eslint-config dependency (ESLint v8 only) that earlier Nx versions added but never wired into the generated lint setup. ## 23.0.x ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/remix/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/remix` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/remix/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/remix/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/remix/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/remix/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ## 22.3.x ### 22.3.4-package-updates **Version**: 22.3.4-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@remix-run/node` | `2.17.3` | Added if not installed | `@remix-run/react` | `2.17.3` | Added if not installed | `@remix-run/serve` | `2.17.3` | Added if not installed | `@remix-run/dev` | `2.17.3` | Added if not installed | `@remix-run/css-bundle` | `2.17.3` | Added if not installed | `@remix-run/eslint-config` | `2.17.3` | Updated only | `@remix-run/server-runtime` | `2.17.3` | Updated only | `@remix-run/testing` | `2.17.3` | Updated only | `@remix-run/express` | `2.17.3` | Updated only | `@remix-run/cloudflare` | `2.17.3` | Updated only | `@remix-run/cloudflare-pages` | `2.17.3` | Updated only | `@remix-run/cloudflare-workers` | `2.17.3` | Updated only | `@remix-run/architect` | `2.17.3` | Updated only | `@remix-run/deno` | `2.17.3` | Updated only | `@remix-run/route-config` | `2.17.3` | Updated only | `@remix-run/fs-routes` | `2.17.3` | Updated only --- ## Test Tools {% index_page_cards path="technologies/test-tools" /%} --- ## Cypress Get started with [Nx with Cypress](/docs/technologies/test-tools/cypress/introduction), or browse the topics below. {% index_page_cards path="technologies/test-tools/cypress" /%} --- ## @nx/cypress Executors The @nx/cypress plugin provides various executors to help you create and configure cypress projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/cypress introduction](/docs/technologies/test-tools/cypress/introduction). ### `cypress` Run Cypress for e2e, integration and component testing. Depending on your testing type, the Cypress executor is configured in different ways. The following are sample configurations that are created via the [configuration](/nx-api/cypress/generators/configuration) and [component-configuration](/nx-api/cypress/generators/component-configuration) generators. ###### E2E Testing ```json "targets": { "e2e": { "executor": "@nx/cypress:cypress", "options": { "cypressConfig": "apps/app-e2e/cypres.config.ts", "devServerTarget": "my-react-app:serve", "testingType": "e2e" } } } ``` :::note[API Testing] API testing with Cypress is the same setup as e2e testing. Just change which `devServerTarget` is used! ::: #### Providing a Base URL If `devServerTarget` is provided, the url returned from started the dev server will be passed to cypress as the `baseUrl` option. Defining a `baseUrl` in the executor options will override the inferred `baseUrl` from the `devServerTarget`. The `baseUrl` defined in the Cypress config file is the last value used if no url is found in the `devServerTarget` or executor options. #### Static Serving When running in CI it doesn't make sense to start up a dev server since there aren't changes to watch for. You can use [`@nx/web:file-server`](/nx-api/web/executors/file-server) to serve the pre-built static files of your frontend project. In some _frontend_ application, add a 'static-serve' target. ```json "serve-static": { "executor": "@nx/web:file-server", "options":{ "buildTarget": "frontend:build" } } ``` In the _e2e_ application add a configuration to change `devServerTarget` to point to the `static-serve` from the _frontend_ application ```json "e2e": { //... "configurations": { "ci": { "devServerTarget": "frontend:serve-static" } } } ``` :::note[What about Node projects?] The same can be done for backend node apps with [`@nx/js:node` executor](/nx-api/js/executors/node) ::: ```bash nx e2e my-app-e2e ``` ###### Component Testing :::note[Cypress Component Testing] When adding component testing to a project, it's best to use the framework specific generator, instead `cypress-component-project` directly. - [React component testing](/nx-api/react/generators/cypress-component-configuration) - [Angular component testing](/nx-api/angular/generators/cypress-component-configuration) ::: ```json "targets": { "component-test": { "executor": "@nx/cypress:cypress", "options": { "cypressConfig": "apps/app/cypres.config.ts", "devServerTarget": "my-react-app:build", "testingType": "component", "skipServe": true } } } ``` It's important `skipServe` is set to true. Nx doesn't need to run the `devServerTarget`, Cypress creates its own dev server for component testing. Instead, Nx needs to know what build target to create the correct configuration to pass to Cypress, which is why it's still used in component testing. #### Environment Variables Using [executor configurations](/concepts/executors-and-configurations#executors-and-configurations) offers flexibility to set environment variables ```json "targets": { "e2e": { "executor": "@nx/cypress:cypress", "options": { "cypressConfig": "apps/app-e2e/cypres.config.ts", "devServerTarget": "my-react-app:serve", "testingType": "e2e" }, "configurations": { "qa": { "env": { "API_URL": "https://api.qa.company.com" } }, "dev": { "env": { "API_URL": "http://localhost:3333/api" } } } } } ``` Read more on different ways to use [environment variables for cypress executor](/nx-api/cypress#environment-variables) #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `cypressConfig` | string [**required**] | The path of the Cypress configuration json file. | | | `autoCancelAfterFailures` | string | Specify the number of failures to cancel a run being recorded to the Cypress Cloud or `false` to disable auto-cancellation. | | | `baseUrl` | string | The address (with the port) which your application is running on. | | | `browser` | string | The browser to run tests in. | | | `ciBuildId` | string | A unique identifier for a run to enable grouping or parallelization. | | | `devServerTarget` | string | Dev server target to run tests against. | | | `env` | object | A key-value Pair of environment variables to pass to Cypress runner. | | | `exit` | boolean | Whether or not the Cypress Test Runner will stay open after running tests in a spec file. | `true` | | `group` | string | A named group for recorded runs in the Cypress dashboard. | | | `headed` | boolean | Displays the browser instead of running headlessly. Set this to `true` if your run depends on a Chrome extension being loaded. | `false` | | `headless` | boolean | Hide the browser instead of running headed. | `false` | | `ignoreTestFiles` | string | A String or Array of glob patterns used to ignore test files that would otherwise be shown in your list of tests. Cypress uses minimatch with the options: `{dot: true, matchBase: true}`. We suggest using https://globster.xyz to test what files would match. | | | `key` | string | The key cypress should use to run tests in parallel/record the run (CI only). | | | `parallel` | boolean | Whether or not Cypress should run its tests in parallel (CI only). | `false` | | `port` | string | Pass a specified port value to the devServerTarget, if the value is 'cypress-auto' a free port will automatically be picked for the devServerTarget. | | | `quiet` | boolean | If passed, Cypress output will not be printed to stdout. Only output from the configured Mocha reporter will print. | `false` | | `record` | boolean | Whether or not Cypress should record the results of the tests. | `false` | | `reporter` | string | The reporter used during cypress run. | | | `reporterOptions` | string | The reporter options used. Supported options depend on the reporter. https://docs.cypress.io/guides/tooling/reporters#Reporter-Options | | | `runnerUi` | boolean | Displays the Cypress Runner UI. Useful for when Test Replay is enabled and you would still like the Cypress Runner UI to be displayed for screenshots and video. | | | `skipServe` | boolean | Skip dev-server build. | `false` | | `spec` | string | A comma delimited glob string that is provided to the Cypress runner to specify which spec files to run. i.e. `**examples/**,**actions.spec**`. | | | `tag` | string | A comma delimited list to identify a run with. | | | `testingType` | string | Specify the type of tests to execute. | `"e2e"` | | `watch` | boolean | Recompile and run tests when files change. | `false` | --- ## @nx/cypress Generators The @nx/cypress plugin provides various generators to help you create and configure cypress projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/cypress introduction](/docs/technologies/test-tools/cypress/introduction). ## `configuration` Add a Cypress configuration to an existing project. ### Examples This is a generator to add a cypress e2e configuration to an existing project. ```bash nx g @nx/cypress:configuration --project=my-cool-project --devServerTarget=some-app:serve ``` Running this generator, adds the required files to run cypress tests for a project, Mainly a `cypress.config.ts` file and default files in the `/cypress/` directory. Tests will be located in `/cypress/e2e/*` by default. You can customize the directory used via the `--directory` flag, the value is relative to the project root. For example if you wanted to place the files inside an `e2e` folder ```bash nx g @nx/cypress:configuration --project=my-cool-project --devServerTarget=some-app:serve --directory=e2e ``` Providing a `--devServerTarget` is optional if you provide a `--baseUrl` or the project you're adding the configuration to has a `serve` target already. Otherwise, a `--devServerTarget` is recommend for the `@nx/cypress:cypress` executor to spin up the dev server for you automatically when needed. **Usage:** ```bash nx generate @nx/cypress:configuration [options] ``` **Aliases:** `cypress-e2e-configuration`, `e2e`, `e2e-config` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string [**required**] | The project to add a Cypress configuration to | | | `--baseUrl` | string | The address (with the port) which your application is running on. If you wish to start your application when running the e2e target, then use --devServerTarget instead. | | | `--bundler` | string | The Cypress bundler to use. | `"webpack"` | | `--devServerTarget` | string | A devServerTarget,':[:], that will be used to run tests against. This is usually the app this project will be used in. Pass --baseUrl if you wish to not use a devServerTarget. | | | `--directory` | string | A directory where the project is placed relative from the project root | `"cypress"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--jsx` | boolean | Whether or not this project uses JSX. | `true` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--port` | string | Set the 'port' option on the e2e target. It's recommend to set a different port so you can run tests e2e targets in parallel. Most dev servers support using '0' to automatically find a free port. The value 'cypress-auto' can be used if the underlying dev server does not support automatically finding a free port. | | | `--rootProject` | boolean | Create a application at the root of the workspace | `false` | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | ## `convert-to-inferred` Convert existing Cypress project(s) using `@nx/cypress:cypress` executor to use `@nx/cypress/plugin`. **Usage:** ```bash nx generate @nx/cypress:convert-to-inferred [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/cypress:cypress` executor to use `@nx/cypress/plugin`. | | | `--skipFormat` | boolean | Whether to format files at the end of the migration. | `false` | ## `migrate-to-cypress-11` Migrate Cypress e2e project from v8/v9 to Cypress v11. **Usage:** ```bash nx generate @nx/cypress:migrate-to-cypress-11 [options] ``` ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/cypress: --help ``` --- ## Guides {% index_page_cards path="technologies/test-tools/cypress/guides" /%} --- ## Nx with Cypress Cypress is a test runner built for the modern web. It has a lot of great features: - Time travel - Real-time reloads - Automatic waiting - Spies, stubs, and clocks - Network traffic control - Screenshots and videos ## Requirements The `@nx/cypress` plugin supports the following package versions. | Package | Supported Versions | | --------- | ------------------ | | `cypress` | >= 13 < 16 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setting up @nx/cypress > For Cypress Component Testing, see [the component testing guide](/docs/kb/cypress-component-testing). ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/cypress` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/cypress` by running the following command: ```shell nx add @nx/cypress ``` This will install the correct version of `@nx/cypress`. ### How @nx/cypress infers tasks {% aside type="note" title="Inferred Tasks" %} Nx plugins can infer tasks for your projects based on the configuration of different tools. You can read more about it at the [Inferred Tasks concept page](/docs/concepts/mental-model#inferred-tasks). {% /aside %} The `@nx/cypress` plugin will create a task for any project that has a Cypress configuration file present. Any of the following files will be recognized as a Cypress configuration file: - `cypress.config.js` - `cypress.config.ts` - `cypress.config.mjs` - `cypress.config.cjs` The configuration directory must also contain a `package.json` or `project.json`. ### View inferred tasks To view inferred tasks for a project, open the [project details view](/docs/concepts/mental-model#inferred-tasks) in Nx Console or run `nx show project my-project --web` in the command line. ### @nx/cypress configuration The `@nx/cypress/plugin` is configured in the `plugins` array in `nx.json`. ```json // nx.json { "plugins": [ { "plugin": "@nx/cypress/plugin", "options": { "targetName": "e2e", "ciTargetName": "e2e-ci", "componentTestingTargetName": "component-test", "ciComponentTestingTargetName": "component-test-ci", "openTargetName": "open-cypress" } } ] } ``` The options shown above control the names of the inferred Cypress tasks. The following table shows the default values for each option: | Option | Default | | ------------------------------ | --------------------------------------------- | | `targetName` | `"e2e"` | | `ciTargetName` | `"e2e-ci"` | | `componentTestingTargetName` | `"component-test"` | | `ciComponentTestingTargetName` | `undefined` (task is not inferred by default) | | `openTargetName` | `"open-cypress"` | The e2e and component-test tasks are cached and track Cypress screenshot and video outputs. The `openTargetName` task opens the interactive Cypress application. The `ciTargetName` task uses the [Atomizer](/docs/features/ci-features/split-e2e-tasks). Set `ciComponentTestingTargetName` to atomize component tests as well. Use `include` and `exclude` glob patterns on the plugin entry to scope inference. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/cypress/plugin`. ### Splitting E2E tasks by file To enable e2e task splitting, make sure there is a `ciWebServerCommand` property set in your `cypress.config.ts` file. It will look something like this: ```ts {% meta="{14}" %} // apps/my-project-e2e/cypress.config.ts import { defineConfig } from 'cypress'; import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset'; export default defineConfig({ e2e: { ...nxE2EPreset(__filename, { cypressDir: 'src', bundler: 'vite', webServerCommands: { default: 'nx run my-project:serve', production: 'nx run my-project:preview', }, ciWebServerCommand: 'nx run my-project:serve-static', }), baseUrl: 'http://localhost:4200', }, }); ``` {% aside type="note" title="Using setupNodeEvents function" %} If you use the `setupNodeEvents` function in your Cypress configuration, make sure to invoke the same function that is returned by `nxE2EPreset`. See the recipe on [using `setupNodeEvents` with Cypress preset](/docs/kb/cypress-setup-node-events) for more details. {% /aside %} ### Splitting component testing tasks by file {% aside type="note" title="Available since Nx 21.6.1" %} Splitting component testing tasks by file is available since Nx 21.6.1. {% /aside %} The `@nx/cypress/plugin` can also automatically split your component testing tasks by file. To enable it, set the `ciComponentTestingTargetName` option of the `@nx/cypress/plugin` in your `nx.json` file. It will look something like this: ```json {% meta="{10}" %} // nx.json { "plugins": [ { "plugin": "@nx/cypress/plugin", "options": { "targetName": "e2e", "ciTargetName": "e2e-ci", "componentTestingTargetName": "component-test", "ciComponentTestingTargetName": "component-test-ci", "openTargetName": "open-cypress" } } ] } ``` ## E2E testing By default, when creating a new frontend application, Nx will use Cypress to create the e2e tests project. ```shell nx g @nx/web:app apps/frontend ``` ### Configure Cypress for an existing project To configure Cypress for an existing project, run the following generator: ```shell nx g @nx/cypress:configuration --project=your-app-name ``` Optionally, you can use the `--baseUrl` option if you don't want the Cypress plugin to serve `your-app-name`. ```shell nx g @nx/cypress:configuration --project=your-app-name --baseUrl=http://localhost:4200 ``` Replace `your-app-name` with the app's name as defined in your `project.json` file or the `name` property of your `package.json`. {% aside type="note" title="E2E setup location" %} The `@nx/cypress:configuration` generator is not a project generator. It won't generate a separate project for the E2E tests. It will configure Cypress for the provided project. To set up a separate project, you can generate a separate project with a project generator like `@nx/js:library` first and then run the `@nx/cypress:configuration` generator. {% /aside %} ### Testing applications Run `nx e2e frontend-e2e` to execute e2e tests with Cypress. You can run your e2e test against a production build by using the `production` [configuration](/docs/kb/executors-and-configurations#use-task-configurations) ```shell nx e2e frontend-e2e --configuration=production ``` You can use the `--spec` flag to glob for test files. ```shell # run the tests in the smoke/ directory nx e2e frontend-e2e --spec="**smoke/**" # run the tests in smoke/ directory and with dashboard in the file name nx e2e frontend-e2e --spec="**smoke/**,**dashboard.cy**" ``` By default, Cypress will run in headless mode. You will have the result of all the tests and errors (if any) in your terminal. Screenshots and videos will be accessible in `dist/cypress/apps/frontend/screenshots` and `dist/cypress/apps/frontend/videos`. ### Watching for changes (Headed mode) You can also run Cypress in headed mode and watching for changes. This is a great way to enhance the dev workflow. You can build up test files with the application running and Cypress will re-run those tests as you enhance and add to the suite. {% tabs %} {% tabitem label="Using inferred tasks" %} ```shell nx open-cypress frontend-e2e ``` {% /tabitem %} {% tabitem label="Using the @nx/cypress:cypress executor" %} ```shell nx e2e frontend-e2e --watch ``` {% /tabitem %} {% /tabs %} ### Specifying a custom url to test The `baseUrl` property provides you the ability to test an application hosted on a specific domain. {% tabs %} {% tabitem label="Using inferred tasks" %} ```shell nx e2e frontend-e2e --config="baseUrl=https://frontend.com" ``` {% aside type="note" title="Required options" %} If `baseUrl` is not provided, Cypress will expect to have the `baseUrl` property in its config file. Otherwise, it will error. {% /aside %} {% /tabitem %} {% tabitem label="Using the @nx/cypress:cypress executor" %} ```shell nx e2e frontend-e2e --baseUrl=https://frontend.com ``` {% aside type="note" title="Required options" %} If no `baseUrl` and no `devServerTarget` are provided, Cypress will expect to have the `baseUrl` property in its config file. Otherwise, it will error. {% /aside %} {% /tabitem %} {% /tabs %} ## Using cypress.config.ts If you need to fine tune your Cypress setup, you can do so by modifying `cypress.config.ts` in the project root. For instance, you can add your `projectId` to save all the screenshots and videos into your Cypress dashboard. The complete configuration is documented on [the official website](https://docs.cypress.io/guides/references/configuration.html#Options). For adding more dynamic configurations to your Cypress configuration, you can look into using [setupNodeEvents](https://docs.cypress.io/api/plugins/browser-launch-api#Syntax) configuration option. ## Environment variables If you need to pass a variable to Cypress that you don't want to commit to your repository (i.e. API keys, dynamic values based on configurations, API URLs), you can use [Cypress environment variables](https://docs.cypress.io/guides/guides/environment-variables). There are a handful of ways to pass environment variables to Cypress, but the most common is via the [`cypress.env.json` file](https://docs.cypress.io/guides/guides/environment-variables#Option-1-configuration-file), the `-e` Cypress arg or the `env` option from the `@nx/cypress:cypress` executor in the [project configuration](/docs/reference/project-configuration#task-definitions-targets) or the command line. Create a `cypress.env.json` file in the projects root (i.e. `apps/my-cool-app-e2e/cypress.env.json`). Cypress will automatically pick up this file. This method is helpful for configurations that you don't want to commit. Make sure to add the file to the `.gitignore` and add documentation so people in your repo know what values to populate in their local copy of the `cypress.env.json` file. Setting the `-e` Cypress arg or the `env` option from the `@nx/cypress:cypress` executor in the project configuration is a good way to add values you want to define that you don't mind committing to the repository, such as a base API URL. {% tabs %} {% tabitem label="Using inferred tasks" %} ```json // project.json { ... "targets": { "e2e": { "options": { "args": "--env=API_URL=https://api.my-nx-website.com" } } } } ``` {% /tabitem %} {% tabitem label="Using the @nx/cypress:cypress executor" %} ```json // project.json { ... "targets": { "e2e": { "executor": "@nx/cypress:cypress", "options": { "env": "API_URL=https://api.my-nx-website.com" } } } } ``` {% /tabitem %} {% /tabs %} Finally, you can also pass environment variables via the command line with the `-e` Cypress arg or the `--env` option for the `@nx/cypress:cypress` executor. {% tabs %} {% tabitem label="Using inferred tasks" %} ```shell nx e2e frontend-e2e -e=API_URL=https://api.my-nx-website.com,API_KEY=abc-123 ``` {% /tabitem %} {% tabitem label="Using the @nx/cypress:cypress executor" %} ```shell nx e2e frontend-e2e --env.API_URL="https://api.my-nx-website.com" --env.API_KEY="abc-123" ``` {% /tabitem %} {% /tabs %} {% aside type="caution" title="Command-line args vs configuration options" %} Providing a flag will override any option with the same name set in the project or workspace configuration. {% /aside %} ## Set up CI In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/cypress Migrations For an overview of the plugin and setup instructions, see the [@nx/cypress introduction](/docs/technologies/test-tools/cypress/introduction). The @nx/cypress plugin provides various migrations to help you migrate to newer versions of cypress projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.2.x ### 23.2.0-cypress-15.20.1-package-updates **Version**: 23.2.0-beta.6 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `cypress` | `^15.20.1` | Updated only ## 23.1.x ### `disable-webpack-ct-just-in-time-compile` **Version**: 23.1.0-beta.6 Set `justInTimeCompile: false` in webpack component testing Cypress configs on Cypress 14+, where it defaults to true and can intermittently run 0 tests in CI. #### Requires | Name | Version | |------|---------| `cypress` | `>=14.0.0` | #### Disable `justInTimeCompile` for webpack component testing Cypress 14+ defaults `justInTimeCompile` to `true` for the webpack dev server, compiling each spec on demand. In run mode the runner can load a component test before its spec finishes compiling, so the spec executes 0 tests while the run still exits green - a false pass that hides broken component tests in CI. This migration sets an explicit `justInTimeCompile: false` in the Cypress configs of webpack component testing projects, keeping the choice visible and reversible. Remove the line to opt back into just-in-time compilation. #### Sample Code Changes ##### Before ```ts title="apps/my-app/cypress.config.ts" import { defineConfig } from 'cypress'; import { nxComponentTestingPreset } from '@nx/react/plugins/component-testing'; export default defineConfig({ component: nxComponentTestingPreset(__filename), }); ``` ##### After ```ts title="apps/my-app/cypress.config.ts" import { defineConfig } from 'cypress'; import { nxComponentTestingPreset } from '@nx/react/plugins/component-testing'; export default defineConfig({ component: { ...nxComponentTestingPreset(__filename), // Cypress 14+ defaults justInTimeCompile to true (webpack only), which can // intermittently run 0 tests in CI. Remove this line to opt back in. justInTimeCompile: false, }, }); ``` #### What is not changed `justInTimeCompile` only applies to the webpack dev server, so vite-based component testing is left untouched. This migration skips vite configs (including `@nx/remix`, which uses the vite dev server), configs that already set `justInTimeCompile`, and e2e-only configs. ### 23.0.0-cypress-15.17-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `cypress` | `^15.17.0` | Updated only ### 23.1.0-eslint-plugin-cypress-package-updates **Version**: 23.1.0-beta.6 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `eslint-plugin-cypress` | `^3.5.0` | Updated only ## 23.0.x ### `remove-experimental-prompt-command` **Version**: 23.0.0-beta.10 Removes the `experimentalPromptCommand` flag from `cypress.config.{ts,js,mjs,cjs}`. The flag was removed in Cypress 15.13.0; `cy.prompt` is now in beta without configuration. #### Requires | Name | Version | |------|---------| `cypress` | `>=15.13.0` | #### Remove the `experimentalPromptCommand` Flag from Cypress Config Removes the `experimentalPromptCommand` flag from `cypress.config.{ts,js,mjs,cjs}` files. The flag was removed in [Cypress 15.13.0](https://github.com/cypress-io/cypress/releases/tag/v15.13.0); `cy.prompt` is now in beta without configuration. Leaving the flag in place causes Cypress to error at startup. #### Sample Code Changes Remove `experimentalPromptCommand` from the top level of `defineConfig`. ##### Before ```ts title="apps/myapp-e2e/cypress.config.ts" {5} import { defineConfig } from 'cypress'; export default defineConfig({ e2e: { baseUrl: 'http://localhost:4200' }, experimentalPromptCommand: true, }); ``` ##### After ```ts title="apps/myapp-e2e/cypress.config.ts" import { defineConfig } from 'cypress'; export default defineConfig({ e2e: { baseUrl: 'http://localhost:4200' }, }); ``` The flag is also removed when nested inside `e2e` or `component`. ##### Before ```ts title="apps/myapp-e2e/cypress.config.ts" {5} import { defineConfig } from 'cypress'; export default defineConfig({ e2e: { experimentalPromptCommand: true, baseUrl: 'http://localhost:4200', }, }); ``` ##### After ```ts title="apps/myapp-e2e/cypress.config.ts" import { defineConfig } from 'cypress'; export default defineConfig({ e2e: { baseUrl: 'http://localhost:4200', }, }); ``` ### `rewrite-internal-subpath-imports` **Version**: 23.0.0-beta.19 Rewrites imports from `@nx/cypress/src/*` to either the public `@nx/cypress` entry (for re-exported symbols) or `@nx/cypress/internal` (for everything else). The `./src/*` wildcard was removed from the package's exports map. ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/cypress/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/cypress` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/cypress/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/cypress/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/cypress/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/cypress/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ### 23.0.0-package-updates **Version**: 23.0.0-beta.10 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `cypress` | `^15.14.2` | Updated only ### 23.0.0-vite-dev-server-package-updates **Version**: 23.0.0-beta.10 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@cypress/vite-dev-server` | `^7.3.1` | Updated only ## 22.3.x ### 22.3.2-package-updates **Version**: 22.3.2-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `cypress` | `^15.8.0` | Updated only | `@cypress/webpack-dev-server` | `^5.4.1` | Updated only ## 22.1.x ### `rename-cy-exec-code-property` **Version**: 22.1.0-beta.6 Renames `cy.exec().its('code')` usages to the new `exitCode` property introduced in Cypress v15. #### Requires | Name | Version | |------|---------| `cypress` | `>=15.0.0` | #### Rename `cy.exec().its('code')` to `cy.exec().its('exitCode')` Cypress v15 renamed the result property exposed by `cy.exec()` from `code` to `exitCode`. This migration updates Cypress spec files managed by Nx so that assertions such as `cy.exec(...).its('code')` use the new `exitCode` property. Read more in the [migration guide](https://docs.cypress.io/app/references/migration-guide#cyexec-code-property-renamed). #### Examples ##### Before ```ts title="apps/app-e2e/src/e2e/sample.cy.ts" cy.exec('echo 0').its('code').should('eq', 0); ``` ##### After ```ts title="apps/app-e2e/src/e2e/sample.cy.ts" cy.exec('echo 0').its('exitCode').should('eq', 0); ``` ### `update-selector-playground-api` **Version**: 22.1.0-beta.6 Updates the deprecated `Cypress.SelectorPlayground` API to `Cypress.ElementSelector` and removes the unsupported `onElement` option. #### Requires | Name | Version | |------|---------| `cypress` | `>=15.0.0` | #### Update the Selector Playground API Cypress v15 renamed `Cypress.SelectorPlayground` to `Cypress.ElementSelector` and removed the deprecated `onElement` option when calling `Cypress.ElementSelector.defaults()`. This migration updates existing Cypress support files to use the new API. Read more in the [migration guide](https://docs.cypress.io/app/references/migration-guide#Selector-Playground-API-changes). #### Examples ##### Before ```ts title="apps/web-e2e/src/support/selector.ts" Cypress.SelectorPlayground.defaults({ selectorPriority: ['data-cy'], onElement: (el) => el, }); ``` ##### After ```ts title="apps/web-e2e/src/support/selector.ts" Cypress.ElementSelector.defaults({ selectorPriority: ['data-cy'], }); ``` ### `update-angular-component-testing-support` **Version**: 22.1.0-beta.6 For Angular component testing projects below v18, switches to the fallback `@cypress/angular` harness required by Cypress v15. #### Requires | Name | Version | |------|---------| `cypress` | `>=15.0.0` | #### Use the fallback Angular component testing harness for Cypress v15 Cypress v15 requires Angular component testing projects running Angular versions below 18 to migrate from the built-in `cypress/angular` helper to the `@cypress/angular` harness (v3). This migration updates component testing imports and ensures the correct dependency is installed. Read more in the [migration guide](https://docs.cypress.io/app/references/migration-guide#Angular-17-CT-no-longer-supported). #### Examples ##### Before ```ts title="apps/dashboard/src/app/app.component.cy.ts" import { mount } from 'cypress/angular'; ``` ##### After ```ts title="apps/dashboard/src/app/app.component.cy.ts" import { mount } from '@cypress/angular'; ``` ### 22.1.0-package-updates **Version**: 22.1.0-beta.6 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `cypress` | `^15.6.0` | Updated only | `@cypress/vite-dev-server` | `^7.0.1` | Updated only | `@cypress/webpack-dev-server` | `^5.1.4` | Updated only ## 21.0.x ### `remove-tsconfig-and-copy-files-options-from-cypress-executor` **Version**: 21.0.0-beta.10 Removes the `tsConfig` and `copyFiles` options from the `@nx/cypress:cypress` executor. #### Remove `tsConfig` and `copyFiles` Options from Cypress Executor Removes the previously deprecated and unused `tsConfig` and `copyFiles` options from the `@nx/cypress:cypress` executor configuration in all projects. #### Examples Remove the options from the project configuration: ##### Before ```json title="apps/app1-e2e/project.json" {7-8} { "targets": { "e2e": { "executor": "@nx/cypress:cypress", "options": { "cypressConfig": "apps/app1-e2e/cypress.config.ts", "tsConfig": "apps/app1-e2e/tsconfig.json", "copyFiles": "**/*.spec.ts", "devServerTarget": "app1:serve" } } } } ``` ##### After ```json title="apps/app1-e2e/project.json" { "targets": { "e2e": { "executor": "@nx/cypress:cypress", "options": { "cypressConfig": "apps/app1-e2e/cypress.config.ts", "devServerTarget": "app1:serve" } } } } ``` Remove the options from a target default using the `@nx/cypress:cypress` executor: ##### Before ```json title="nx.json" {7-8} { "targetDefaults": { "e2e": { "cache": true, "executor": "@nx/cypress:cypress", "options": { "tsConfig": "{projectRoot}/tsconfig.json", "copyFiles": "**/*.spec.ts" } } } } ``` ##### After ```json title="nx.json" { "targetDefaults": { "e2e": { "cache": true, "executor": "@nx/cypress:cypress" } } } ``` Remove the options from a target default using the `@nx/cypress:cypress` executor as the key: ##### Before ```json title="nx.json" {6-7} { "targetDefaults": { "@nx/cypress:cypress": { "cache": true, "options": { "tsConfig": "{projectRoot}/tsconfig.json", "copyFiles": "**/*.spec.ts" } } } } ``` ##### After ```json title="nx.json" { "targetDefaults": { "@nx/cypress:cypress": { "cache": true } } } ``` --- ## Detox Get started with [Nx with Detox](/docs/technologies/test-tools/detox/introduction), or browse the topics below. {% index_page_cards path="technologies/test-tools/detox" /%} --- ## @nx/detox Executors The @nx/detox plugin provides various executors to help you create and configure detox projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/detox introduction](/docs/technologies/test-tools/detox/introduction). ### `build` Run detox build options. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `configPath` | string | Specify Detox config file path. If not supplied, detox searches for `.detoxrc[.js]` or `detox` section in `package.json`. | | | `detoxConfiguration` | string | Select a device configuration from your defined configurations, if not supplied, and there's only one configuration, detox will default to it. | | ### `test` Run detox test options. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `detoxConfiguration` | string [**required**] | Select a device configuration from your defined configurations, if not supplied, and there's only one configuration, detox will default to it. | | | `appLaunchArgs` | number | Custom arguments to pass (through) onto the app every time it is launched. | | | `artifactsLocation` | string | Artifacts (logs, screenshots, etc) root directory. | | | `buildTarget` | string | Target which builds the application. | | | `captureViewHierarchy` | string | [iOS Only] Capture `*.uihierarchy` snapshots on view action errors and `device.captureViewHierarchy()` calls. | | | `cleanup` | boolean | Shutdown simulator when test is over, useful for CI scripts, to make sure detox exists cleanly with no residue | | | `color` | boolean | Colors in log output | | | `configPath` | string | Specify Detox config file path. If not supplied, detox searches for `.detoxrc[.js]` or `detox` section in package.json. | | | `debugSynchronization` | boolean | Customize how long an action/expectation can take to complete before Detox starts querying the app why it is busy. By default, the app status will be printed if the action takes more than 10s to complete. | | | `deviceBootArgs` | string | Custom arguments to pass (through) onto the device (emulator/simulator) binary when booted. | | | `deviceLaunchArgs` | string | A list of passthrough-arguments to use when (if) devices (Android emulator / iOS simulator) are launched by Detox. | | | `deviceName` | string | Override the device name specified in a configuration. Useful for running a single build configuration on multiple devices. | | | `forceAdbInstall` | boolean | Due to problems with the adb install command on Android, Detox resorts to a different scheme for install APK's. Setting true will disable that and force usage of `adb install`, instead. | | | `gpu` | boolean | [Android Only] Launch Emulator with the specific `-gpu [gpu mode]` parameter. | | | `headless` | boolean | Android Only] Launch Emulator in headless mode. Useful when running on CI. | | | `inspectBrk` | boolean | Uses node's `--inspect-brk` flag to let users debug the jest/mocha test runner | | | `jestReportSpecs` | boolean | [Jest Only] Whether to output logs per each running spec, in real-time. By default, disabled with multiple workers. | | | `keepLockFile` | boolean | Keep the device lock file when running Detox tests. | | | `loglevel` | string | Log level: `fatal`, `error`, `warn`, `info`, `verbose`, `trace`. | | | `recordLogs` | string | Save logs during each test to artifacts directory. Pass `failing` to save logs of failing tests only. | | | `recordPerformance` | string | [iOS Only] Save Detox Instruments performance recordings of each test to artifacts directory. | | | `recordTimeline` | string | [Jest Only] Record tests and events timeline, for visual display on the `chrome://tracing` tool. | | | `recordVideos` | string | Save screen recordings of each test to artifacts directory. Pass `failing` to save recordings of failing tests only. | | | `retries` | number | [Jest Circus Only] Re-spawn the test runner for individual failing suite files until they pass, or `` times at least. | | | `reuse` | boolean | Reuse existing installed app (do not delete + reinstall) for a faster run. | `false` | | `runnerConfig` | string | Test runner config file, defaults to `e2e/mocha.opts` for mocha and `e2e/config.json` for Jest. | | | `takeScreenshots` | string | Save screenshots before and after each test to artifacts directory. Pass `failing` to save screenshots of failing tests only. | | | `useCustomLogger` | boolean | Use Detox' custom console-logging implementation, for logging Detox (non-device) logs. Disabling will fallback to Node.js / test-runner's implementation (e.g. Jest / Mocha). | | | `workers` | number | Specifies number of workers the test runner should spawn, requires a test runner with parallel execution support (Detox CLI currently supports Jest). | | --- ## @nx/detox Generators The @nx/detox plugin provides various generators to help you create and configure detox projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/detox introduction](/docs/technologies/test-tools/detox/introduction). ## `application` Create Detox Configuration for the workspace. **Usage:** ```bash nx generate @nx/detox:application [options] ``` **Aliases:** `app` **Arguments:** ```bash nx generate @nx/detox:application [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--appProject` | string [**required**] | Name of the frontend project to be tested. | | | `--framework` | string [**required**] | App framework to test | | | `--appDisplayName` | string | Display name of the app to be tested if different from appProject | | | `--appName` | string | Name of the app to be tested if different from appProject | | | `--e2eName` | string | Name of the E2E Project. | | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `convert-to-inferred` Convert existing Detox project(s) using `@nx/detox:*` executors to use `@nx/detox/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target. **Usage:** ```bash nx generate @nx/detox:convert-to-inferred [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/detox:*` executors to use `@nx/detox/plugin`. | | | `--skipFormat` | boolean | Whether to format files at the end of the migration. | `false` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/detox: --help ``` --- ## Nx with Detox Detox is gray box end-to-end testing and automation library for mobile apps. It has a lot of great features: - Cross Platform - Runs on Devices - Automatically Synchronized - Test Runner Independent - Debuggable ## Requirements The `@nx/detox` plugin supports the following package versions. | Package | Supported Versions | | ------- | ------------------ | | `detox` | ^20.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setting up detox ### Setup environment #### Install applesimutils (Mac only) [applesimutils](https://github.com/wix/AppleSimulatorUtils) is a collection of utils for Apple simulators. ```shell brew tap wix/brew brew install applesimutils ``` #### Install Jest globally ```shell npm install -g jest ``` ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/detox` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/detox` by running the following command: ```shell nx add @nx/detox ``` This will install the correct version of `@nx/detox`. ### How @nx/detox infers tasks The `@nx/detox/plugin` plugin creates tasks for projects with one of these Detox configuration files: - `.detoxrc.js` - `.detoxrc.json` - `detox.config.js` - `detox.config.json` It creates cached `build` and `test` tasks and a continuous `start` task. ### View inferred tasks To view inferred tasks for a project, open the [project details view](/docs/concepts/mental-model#inferred-tasks) in Nx Console or run `nx show project my-project --web` in the command line. ### @nx/detox configuration The `@nx/detox/plugin` is configured in the `plugins` array in `nx.json`. ```json // nx.json { "plugins": [ { "plugin": "@nx/detox/plugin", "options": { "buildTargetName": "build", "startTargetName": "start", "testTargetName": "test", "buildDepsTargetName": "build-deps", "watchDepsTargetName": "watch-deps" } } ] } ``` | Option | Type | Default | Description | | --------------------- | ------ | ------- | ------------------------------------------ | | `buildTargetName` | string | `build` | Name of the cached `detox build` task. | | `startTargetName` | string | `start` | Name of the continuous `detox start` task. | | `testTargetName` | string | `test` | Name of the cached `detox test` task. | | `buildDepsTargetName` | string | none | Name of an optional dependency build task. | | `watchDepsTargetName` | string | none | Name of an optional dependency watch task. | Use `include` and `exclude` glob patterns on the plugin entry to scope inference. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/detox/plugin`. ## Using detox ### Testing applications - Run `nx test-ios frontend-e2e` to build the iOS app and execute e2e tests with Detox for iOS (Mac only) - Run `nx test-android frontend-e2e` to build the Android app and execute e2e tests with Detox for Android You can run below commands: - `nx build-ios frontend-e2e`: build the iOS app (Mac only) - `nx build-android frontend-e2e`: build the Android app ### Testing against prod build You can run your e2e test against a production build: - `nx test-ios frontend-e2e --prod`: to build the iOS app and execute e2e tests with Detox for iOS with Release configuration (Mac only) - `nx test-android frontend-e2e --prod`: to build the Android app and execute e2e tests with Detox for Android with release build type - `nx build-ios frontend-e2e --prod`: build the iOS app using Release configuration (Mac only) - `nx build-android frontend-e2e --prod`: build the Android app using release build type ## Configuration ### Using .detoxrc.json If you need to fine tune your Detox setup, you can do so by modifying `.detoxrc.json` in the e2e project. #### Change testing simulator/emulator For iOS, in terminal, run `xcrun simctl list devices available` to view a list of simulators on your Mac. To open your active simulator, `run open -a simulator`. In `frontend-e2e/.detoxrc.json`, you could change the simulator under `devices.simulator.device`. For Android, in terminal, run `emulator -list-avds` to view a list of emulators installed. To open your emulator, run `emulator -avd `. In `frontend-e2e/.detoxrc.json`, you could change the simulator under `devices.emulator.device`. In addition, to override the device name specified in a configuration, you could use `--device-name` option: `nx test-ios --device-name "iPhone 11"`. The `device-name` property provides you the ability to test an application run on specific device. ```shell nx test-ios frontend-e2e --device-name "iPhone 11" nx test-android frontend-e2e --device-name "Pixel_4a_API_30" ``` ## Set up CI In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/detox Migrations For an overview of the plugin and setup instructions, see the [@nx/detox introduction](/docs/technologies/test-tools/detox/introduction). The @nx/detox plugin provides various migrations to help you migrate to newer versions of detox projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.0.x ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/detox/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/detox` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/detox/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/detox/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/detox/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/detox/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ## 22.2.x ### `update-22-0-0-remove-config-plugins-detox-for-expo-54` **Version**: 22.2.0-beta.2 Remove @config-plugins/detox for Expo 54+ projects (package discontinued) #### Requires | Name | Version | |------|---------| `expo` | `>= 54.0.0` | ## 22.0.x ### 22.0.0-package-updates **Version**: 22.0.0-beta.5 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `detox` | `~20.43.0` | Updated only | `@testing-library/jest-dom` | `~6.9.1` | Updated only ### 22.0.0-config-plugins-detox-package-updates **Version**: 22.0.0-beta.5 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@config-plugins/detox` | `~11.0.0` | Updated only --- ## Jest Get started with [Nx with Jest](/docs/technologies/test-tools/jest/introduction), or browse the topics below. {% index_page_cards path="technologies/test-tools/jest" /%} --- ## @nx/jest Executors The @nx/jest plugin provides various executors to help you create and configure jest projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/jest introduction](/docs/technologies/test-tools/jest/introduction). ### `jest` Jest target options for Build Facade. Jest can be configured in many ways, but primarily you'll need to at least have the jestConfig options ```json "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "libs/my-lib/jest.config.ts" } } ``` It is also helpful to have `passWithNoTests: true` set so your project doesn't fail testing while tests are still being added. ```json "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "libs/my-lib/jest.config.ts", "passWithNoTests": true } } ``` #### Snapshots Update snapshots running with `--update-snapshot` or `-u` for short. ```bash nx test my-project -u ``` Other times you might not want to allow updating snapshots such as in CI. Adding a _ci_ configuration is helpful for adding this behavior. ```json "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "libs/my-lib/jest.config.ts", "passWithNoTests": true }, "configurations": { "ci": { "ci": true } } } ``` ```bash nx affected --target=test ``` Learn more [about _affected_](/ci/features/affected) #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `jestConfig` | string [**required**] | The path of the Jest configuration. (https://jestjs.io/docs/en/configuration) | | | `bail` | string | Exit the test suite immediately after `n` number of failing tests. (https://jestjs.io/docs/cli#--bail) | | | `changedSince` | string | Runs tests related to the changes since the provided branch or commit hash. If the current branch has diverged from the given branch, then only changes made locally will be tested. (https://jestjs.io/docs/cli#--changedsince) | | | `ci` | boolean | Whether to run Jest in continuous integration (CI) mode. This option is on by default in most popular CI environments. It will prevent snapshots from being written unless explicitly requested. (https://jestjs.io/docs/cli#--ci) | | | `clearCache` | boolean | Deletes the Jest cache directory and then exits without running tests. Will delete Jest's default cache directory. _Note: clearing the cache will reduce performance_. | | | `codeCoverage` | boolean | Indicates that test coverage information should be collected and reported in the output. (https://jestjs.io/docs/cli#--coverageboolean) | | | `color` | boolean | Forces test results output color highlighting (even if `stdout` is not a TTY). Set to false if you would like to have no colors. (https://jestjs.io/docs/cli#--colors) | | | `colors` | boolean | Forces test results output highlighting even if stdout is not a TTY. (https://jestjs.io/docs/cli#--colors) | | | `config` | string | The path to a Jest config file specifying how to find and execute tests. If no `rootDir` is set in the config, the directory containing the config file is assumed to be the `rootDir` for the project. This can also be a JSON-encoded value which Jest will use as configuration. | | | `coverageDirectory` | string | The directory where Jest should output its coverage files. | | | `coverageReporters` | array | A list of reporter names that Jest uses when writing coverage reports. Any istanbul reporter. | | | `detectLeaks` | boolean | **EXPERIMENTAL**: Detect memory leaks in tests. After executing a test, it will try to garbage collect the global object used, and fail if it was leaked | | | `detectOpenHandles` | boolean | Attempt to collect and print open handles preventing Jest from exiting cleanly (https://jestjs.io/docs/cli#--detectopenhandles) | | | `findRelatedTests` | string | Find and run the tests that cover a comma separated list of source files that were passed in as arguments. (https://jestjs.io/docs/cli#--findrelatedtests-spaceseparatedlistofsourcefiles) | | | `forceExit` | boolean | Force Jest to exit after all tests have completed running. This is useful when resources set up by test code cannot be adequately cleaned up.This feature is an escape-hatch. If Jest doesn't exit at the end of a test run, it means external resources are still being held on to or timers are still pending in your code. It is advised to tear down external resources after each test to make sure Jest can shut down cleanly. You can use --detectOpenHandles to help track it down. | | | `json` | boolean | Prints the test results in `JSON`. This mode will send all other test output and user messages to `stderr`. (https://jestjs.io/docs/cli#--json) | | | `logHeapUsage` | boolean | Logs the heap usage after every test. Useful to debug memory leaks. Use together with --runInBand and --expose-gc in node. | | | `maxWorkers` | string | Specifies the maximum number of workers the worker-pool will spawn for running tests. This defaults to the number of the cores available on your machine. Useful for CI. (its usually best not to override this default) (https://jestjs.io/docs/cli#--maxworkersnumstring) | | | `onlyChanged` | boolean | Attempts to identify which tests to run based on which files have changed in the current repository. Only works if you're running tests in a `git` or `hg` repository at the moment. (https://jestjs.io/docs/cli#--onlychanged) | | | `outputFile` | string | Write test results to a file when the `--json` option is also specified. (https://jestjs.io/docs/cli#--outputfilefilename) | | | `passWithNoTests` | boolean | Will not fail if no tests are found (for example while using `--testPathPatterns`.) (https://jestjs.io/docs/cli#--passwithnotests) | | | `randomize` | boolean | Shuffle the order of the tests within a file. The shuffling is based on the seed. This option is only supported using the default jest-circus test runner. | | | `reporters` | array | Run tests with specified reporters. Reporter options are not available via CLI. Example with multiple reporters: `jest --reporters="default" --reporters="jest-junit"`. (https://jestjs.io/docs/cli#--reporters) | | | `runInBand` | boolean | Run all tests serially in the current process (rather than creating a worker pool of child processes that run tests). This is sometimes useful for debugging, but such use cases are pretty rare. Useful for CI. (https://jestjs.io/docs/cli#--runinband) | | | `showConfig` | boolean | Print your Jest config and then exits. (https://jestjs.io/docs/en/cli#--showconfig) | | | `silent` | boolean | Prevent tests from printing messages through the console. (https://jestjs.io/docs/cli#--silent) | | | `testFile` | string | The name of the file to test. | From command line | | `testLocationInResults` | boolean | Adds a location field to test results. Used to report location of a test in a reporter. { "column": 4, "line": 5 } (https://jestjs.io/docs/cli#--testlocationinresults) | | | `testNamePattern` | string | Run only tests with a name that matches the regex pattern. (https://jestjs.io/docs/cli#--testnamepatternregex) | | | `testPathIgnorePatterns` | array | An array of regexp pattern strings that is matched against all tests paths before executing the test. Only run those tests with a path that does not match with the provided regexp expressions. (https://jestjs.io/docs/cli#--testpathignorepatternsregexarray) | | | `testPathPatterns` | array | An array of regexp pattern strings that is matched against all tests paths before executing the test. (https://jestjs.io/docs/cli#--testpathpatternsregex) | `[]` | | `testResultsProcessor` | string | Node module that implements a custom results processor. (https://jestjs.io/docs/en/configuration#testresultsprocessor-string) | | | `testTimeout` | number | Default timeout of a test in milliseconds. Default value: `5000`. (https://jestjs.io/docs/cli#--testtimeoutnumber) | | | `tsConfig` | string | The path to the TypeScript configuration file, relative to the workspace root. Note this is only metadata used by Angular migrations. This executor does not use it. | | | `updateSnapshot` | boolean | Use this flag to re-record snapshots. Can be used together with a test suite pattern or with `--testNamePattern` to re-record snapshot for test matching the pattern. (https://jestjs.io/docs/cli#--updatesnapshot) | | | `useStderr` | boolean | Divert all output to stderr. | | | `verbose` | boolean | Display individual test results with the test suite hierarchy. (https://jestjs.io/docs/cli#--verbose) | | | `watch` | boolean | Watch files for changes and rerun tests related to changed files. If you want to re-run all tests when a file has changed, use the `--watchAll` option. (https://jestjs.io/docs/cli#--watch) | | | `watchAll` | boolean | Watch files for changes and rerun all tests when something changes. If you want to re-run only the tests that depend on the changed files, use the `--watch` option. (https://jestjs.io/docs/cli#--watchall) | | --- ## @nx/jest Generators The @nx/jest plugin provides various generators to help you create and configure jest projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/jest introduction](/docs/technologies/test-tools/jest/introduction). ## `convert-to-inferred` Convert existing Jest project(s) using `@nx/jest:jest` executor to use `@nx/jest/plugin`. **Usage:** ```bash nx generate @nx/jest:convert-to-inferred [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/jest:jest` executor to use `@nx/jest/plugin`. If not provided, all projects using the `@nx/jest:jest` executor will be converted. | | | `--skipFormat` | boolean | Whether to format files. | `false` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/jest: --help ``` --- ## Nx with Jest [Jest](https://jestjs.io) is a JavaScript testing framework. The `@nx/jest` plugin adds [inferred Jest targets](#configuration), a [Jest configuration generator](#add-jest-to-a-project), and CI-ready [test splitting](#unit-and-e2e-configurations). You can use Jest with Nx without the plugin and still get [task caching](/docs/features/cache-task-results), [task orchestration](/docs/features/run-tasks), and the [project graph](/docs/features/explore-graph). ## Requirements The `@nx/jest` plugin supports the following package versions. | Package | Supported Versions | | ------- | -------------------- | | `jest` | ^29.0.0 \|\| ^30.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setup ### Add to an existing workspace {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/jest` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). Using `nx add` helps ensure the correct version is installed and configured. {% /aside %} ```shell nx add @nx/jest ``` Verify the inferred test task in the project details view: ```shell nx show project my-app ``` ### Add Jest to a project Generate Jest configuration for an existing project: ```shell nx g @nx/jest:configuration --project=my-app ``` Replace `my-app` with your project name. The generator accepts framework-specific options. Pass the right flags for your project type: {% tabs syncKey="framework" %} {% tabitem label="React" %} ```shell nx g @nx/jest:configuration --project=my-react-lib --supportTsx ``` Enables the JSX/TSX transform so Jest can process React components. {% /tabitem %} {% tabitem label="Angular" %} ```shell nx g @nx/jest:configuration --project=my-angular-lib --setupFile=angular ``` Configures `jest-preset-angular` and keeps Angular snapshot serializers enabled. {% /tabitem %} {% tabitem label="Node / TypeScript" %} ```shell nx g @nx/jest:configuration --project=my-node-lib --testEnvironment=node ``` Uses the `node` test environment instead of the default `jsdom`. {% /tabitem %} {% /tabs %} See the full [configuration generator reference](/docs/technologies/test-tools/jest/generators#configuration) for all options. {% aside type="tip" title="Framework generators handle this automatically" %} When generating a new project with a framework generator, you can specify Jest as the test runner and Nx passes the correct options automatically: ```shell nx g @nx/react:lib libs/my-react-lib --unitTestRunner=jest nx g @nx/node:app apps/my-api --unitTestRunner=jest ``` {% /aside %} ## Local development Run tests: ```shell nx test my-app ``` Watch for changes: ```shell nx test my-app --watch ``` Run a specific file (positional argument or `--testFile`): ```shell nx test my-app ./path/to/spec/file/user-profile.spec.ts nx test my-app --testFile user-profile.spec.ts ``` Collect coverage: ```shell nx test my-app --coverage ``` Snapshot example: ```typescript describe('userProfile', () => { it('matches the saved snapshot', () => { expect(renderUserProfile()).toMatchSnapshot(); }); }); ``` Update snapshots with `-u`/`--updateSnapshot` and check snapshot files into source control. ## Configuration ### Task inference The `@nx/jest` plugin infers tasks for any project with a Jest configuration file: - `jest.config.js` - `jest.config.ts` - `jest.config.mjs` - `jest.config.mts` - `jest.config.cjs` - `jest.config.cts` The configuration directory must also contain a `project.json` or a workspace-package `package.json`. Root Jest configurations that only aggregate projects with `getJestProjectsAsync()` are not registered as projects. ### Plugin options Configure the plugin in `nx.json`: ```json // nx.json { "plugins": [ { "plugin": "@nx/jest/plugin", "options": { "targetName": "test" } } ] } ``` | Option | Type | Default | Description | | -------------------- | ------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `targetName` | string | `test` | Name of the test task. | | `ciTargetName` | string | none | Creates a CI-only task for atomized test runs. | | `ciGroupName` | string | none | Custom group name for atomized tasks in Nx Cloud/UI. | | `disableJestRuntime` | boolean | `true` | When `true`, Nx skips creating the Jest runtime and computes inputs/outputs itself. Set to `false` to enable the Jest runtime. | | `useJestResolver` | boolean | `true` when runtime is enabled | Whether to use Jest's resolver for resolving config file references as task inputs. Follows symlinks and honors custom `moduleDirectories`/`modulePaths`. Faster path-based classification is used when `false`. | `disableJestRuntime` defaults to `true` because the plugin treats it as enabled only when `options.disableJestRuntime !== false`. This reduces computation time for inferred tasks; set it to `false` if you need Jest runtime inference. The test task is cached, with inputs and outputs based on your Jest configuration. Set `ciTargetName` to enable the [Atomizer](/docs/features/ci-features/split-e2e-tasks). ### Unit and e2e configurations Use two plugin entries to separate unit and E2E Jest tasks, and enable CI splitting for E2E: ```json // nx.json { "plugins": [ { "plugin": "@nx/jest/plugin", "exclude": ["e2e/**/*"], "options": { "targetName": "test" } }, { "plugin": "@nx/jest/plugin", "include": ["e2e/**/*"], "options": { "targetName": "e2e", "ciTargetName": "e2e-ci" } } ] } ``` Here `ciTargetName` atomizes only the E2E tasks. Use `ciGroupName` to set a custom group label. ### View inferred tasks Open the project details view in Nx Console or run: ```shell nx show project my-app ``` A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter for these tasks must use the exact identifier `@nx/jest/plugin`. ## Set up CI In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). ## What's next {% cardgrid %} {% linkcard title="Jest Configuration" description="Explore all Jest configuration options." href="https://jestjs.io/docs/configuration" /%} {% linkcard title="Snapshot Testing" description="Learn how Jest snapshot testing works and when to use it." href="https://jestjs.io/docs/snapshot-testing" /%} {% linkcard title="Split E2E Tasks" description="Distribute E2E tests across CI for better caching and retries." href="/docs/features/ci-features/split-e2e-tasks" /%} {% linkcard title="Feature-Based Testing" description="Organize tests by feature for better caching and targeted CI." href="/docs/kb/feature-based-testing" /%} {% linkcard title="Jest Generators Reference" description="Full API reference for @nx/jest generators." href="/docs/technologies/test-tools/jest/generators" /%} {% linkcard title="Jest Executors Reference" description="Full API reference for @nx/jest executors." href="/docs/technologies/test-tools/jest/executors" /%} {% /cardgrid %} --- ## @nx/jest Migrations For an overview of the plugin and setup instructions, see the [@nx/jest introduction](/docs/technologies/test-tools/jest/introduction). The @nx/jest plugin provides various migrations to help you migrate to newer versions of jest projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.1.x ### `set-ts-jest-isolated-modules` **Version**: 23.1.0-beta.4 Set `isolatedModules: true` in `tsconfig.spec.json` for ts-jest projects on TypeScript < 6 (ts-jest 29.2+ forces `moduleResolution: node10` on the CommonJS path, which ignores package `exports` maps and breaks resolution of exports-only workspace libraries), then verify the workspace still typechecks and remedy any failures the change surfaced. #### Requires | Name | Version | |------|---------| `ts-jest` | `>=29.2.0` | #### Set isolatedModules for ts-jest below TypeScript 6 The jest 30 path of `@nx/jest` bumps ts-jest to 29.4.x. On the CommonJS jest path, ts-jest 29.2+ uses `moduleResolution: node10` when `bundler` is invalid alongside the forced `module: commonjs`, which is the case below TypeScript 6. `node10` does not read package `exports` maps, so a workspace library that exposes types only through `exports` fails to resolve during the ts-jest type check: ```text error TS2307: Cannot find module '@my-org/some-lib' or its corresponding type declarations. ``` The migration sets `isolatedModules: true` in each affected `tsconfig.spec.json`, so ts-jest transpiles each file independently and skips the cross-file type resolution that was failing. This matches the `isolatedModules: true` that fresh ts-solution workspaces already set in `tsconfig.base.json`. Type checking stays on each project's dedicated typecheck target. The migration runs only below TypeScript 6, in ts-solution workspaces that do not already enable `isolatedModules`. TypeScript 6 and above resolves `exports` under `bundler` with `commonjs`, so those workspaces are left unchanged. #### Sample code changes ##### Before ```json title="tsconfig.spec.json" { "extends": "../../tsconfig.base.json", "compilerOptions": { "types": ["jest", "node"] } } ``` ##### After ```json title="tsconfig.spec.json" {4} { "extends": "../../tsconfig.base.json", "compilerOptions": { "isolatedModules": true, "types": ["jest", "node"] } } ``` #### Verifying the workspace After the migration runs, verify the workspace still typechecks and fix anything `isolatedModules` surfaced: ```bash nx run-many -t typecheck ``` `isolatedModules` can fail typecheck (TS1205 - re-exporting a type needs `export type`; TS2748 - const enum access) or break a project's tests at runtime even when typecheck passes. The cause is a package that re-exports a value through both `module.exports` and an ESM `export` (for example napi bindings exposing a `const enum`); per-file transpilation cannot preserve it, and consumers of that const enum break too. To fix a broken project, remove `isolatedModules` from its `tsconfig.spec.json`; if that brings back the `TS2307: Cannot find module` error this migration was added to prevent, that project needs `isolatedModules`, so fix the source instead. ### 23.1.0-ts-jest-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `ts-jest` | `^29.4.7` | Updated only ### 23.1.0-jest-preset-angular-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `jest-preset-angular` | `~17.0.0` | Updated only ## 23.0.x ### `update-snapshot-guide-link` **Version**: 23.0.0-beta.10 Update the Jest snapshot guide link in `.snap` files from the legacy `https://goo.gl/fbAQLP` URL to `https://jestjs.io/docs/snapshot-testing`, which Jest v30 now requires. #### Requires | Name | Version | |------|---------| `jest` | `>=30.0.0` | #### Update Jest Snapshot Guide Link Updates the snapshot guide link at the top of every `.snap` file from the legacy `https://goo.gl/fbAQLP` to `https://jestjs.io/docs/snapshot-testing`. Jest v30 errors out at test setup time if it sees the old link, so existing snapshot files need to be rewritten before tests can run. Read more at the [Jest v30 migration notes](https://jestjs.io/docs/upgrading-to-jest30). #### Examples ##### Before ```text title="apps/myapp/src/__snapshots__/example.spec.ts.snap" // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`renders correctly 1`] = `"hello"`; ``` ##### After ```text title="apps/myapp/src/__snapshots__/example.spec.ts.snap" // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing exports[`renders correctly 1`] = `"hello"`; ``` ### `rewrite-jest-internal-subpath-imports` **Version**: 23.0.0-beta.16 Rewrites `@nx/jest/src/*` subpath imports now that the `./src/*` subpath is no longer exposed by `@nx/jest`'s exports map. Named imports/exports of public symbols are routed to `@nx/jest` and the rest to the new `@nx/jest/internal` entry; `require`, dynamic `import` and `jest.mock` calls reference the whole module and are routed to `@nx/jest/internal`. ### `rewrite-jest-project-generator` **Version**: 23.0.0-beta.16 Replaces the removed `jestProjectGenerator` export from `@nx/jest` with its replacement `configurationGenerator`. ### `migrate-jest-executor-setup-file` **Version**: 23.0.0-beta.22 Migrate the deprecated `setupFile` option of the `@nx/jest:jest` executor: push the file path into `setupFilesAfterEnv` in the project's Jest config and remove the option from `project.json` and `nx.json` target defaults. #### Migrate `setupFile` Option to `setupFilesAfterEnv` Migrates the previously deprecated `setupFile` option of the `@nx/jest:jest` executor. The setup file path is appended to the `setupFilesAfterEnv` array in the project's Jest configuration (using `/...` form), and the deprecated option is removed from `project.json` and `nx.json` target defaults. If the setup file cannot be migrated automatically (e.g. the Jest configuration cannot be parsed because it exports a factory function or assigns `setupFilesAfterEnv` to a non-array value, it sets `rootDir` to a non-literal value, or the target shares a Jest configuration with another target using a different setup file), the deprecated option is still removed and a warning is logged listing the affected targets so the setup file path can be moved manually. #### Examples Push the setup file into the project's Jest configuration and remove the option from `project.json`: ##### Before ```json title="apps/myapp/project.json" {7} { "targets": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "apps/myapp/jest.config.ts", "setupFile": "apps/myapp/src/test-setup.ts" } } } } ``` ```ts title="apps/myapp/jest.config.ts" export default { displayName: 'myapp', }; ``` ##### After ```json title="apps/myapp/project.json" { "targets": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "apps/myapp/jest.config.ts" } } } } ``` ```ts title="apps/myapp/jest.config.ts" export default { displayName: 'myapp', setupFilesAfterEnv: ['/src/test-setup.ts'], }; ``` Append to an existing `setupFilesAfterEnv` array: ##### Before ```json title="apps/myapp/project.json" {7} { "targets": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "apps/myapp/jest.config.ts", "setupFile": "apps/myapp/src/test-setup.ts" } } } } ``` ```ts title="apps/myapp/jest.config.ts" export default { displayName: 'myapp', setupFilesAfterEnv: ['/src/existing-setup.ts'], }; ``` ##### After ```json title="apps/myapp/project.json" { "targets": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "apps/myapp/jest.config.ts" } } } } ``` ```ts title="apps/myapp/jest.config.ts" export default { displayName: 'myapp', setupFilesAfterEnv: [ '/src/existing-setup.ts', '/src/test-setup.ts', ], }; ``` Remove the option from a target default using the `@nx/jest:jest` executor: ##### Before ```json title="nx.json" {7} { "targetDefaults": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "{projectRoot}/jest.config.ts", "setupFile": "{projectRoot}/src/test-setup.ts" } } } } ``` ##### After ```json title="nx.json" { "targetDefaults": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "{projectRoot}/jest.config.ts" } } } } ``` Per-project paths don't make sense as workspace defaults, so the option is removed from `nx.json`. Base targets that inherited it don't lose their setup file: the inherited path is expanded and added to `setupFilesAfterEnv` in each project's Jest config, the same as if the target had declared the option itself. A warning is logged noting the removal from `nx.json`. Remove the option from a target default entry matching on the `@nx/jest:jest` executor: ##### Before ```json title="nx.json" {6} { "targetDefaults": { "@nx/jest:jest": { "options": { "jestConfig": "{projectRoot}/jest.config.ts", "setupFile": "{projectRoot}/src/test-setup.ts" } } } } ``` ##### After ```json title="nx.json" { "targetDefaults": { "@nx/jest:jest": { "options": { "jestConfig": "{projectRoot}/jest.config.ts" } } } } ``` ### `migrate-jest-configuration-skip-setup-file` **Version**: 23.0.0-beta.22 Migrate the deprecated `skipSetupFile` option of the `@nx/jest:configuration` generator stored as a default in `nx.json` or per-project `project.json` to `setupFile: 'none'` (when `true`) or remove it (when `false`). #### Migrate `skipSetupFile` Generator Default to `setupFile` Migrates the previously deprecated `skipSetupFile` option of the `@nx/jest:configuration` generator. When set as a default in `nx.json` `generators` or per-project `project.json` `generators`, it is rewritten as follows: - `skipSetupFile: true` becomes `setupFile: 'none'` (preserving the original behavior of skipping the setup file). Existing `setupFile` values are left untouched. - `skipSetupFile: false` is dropped (it was a no-op). Both flat (`@nx/jest:configuration`) and nested (`@nx/jest` -> `configuration`) forms are handled. #### Examples Rewrite a `nx.json` generator default: ##### Before ```json title="nx.json" {4} { "generators": { "@nx/jest:configuration": { "skipSetupFile": true } } } ``` ##### After ```json title="nx.json" { "generators": { "@nx/jest:configuration": { "setupFile": "none" } } } ``` Drop the option when set to `false`: ##### Before ```json title="nx.json" {4} { "generators": { "@nx/jest:configuration": { "skipSetupFile": false, "testEnvironment": "jsdom" } } } ``` ##### After ```json title="nx.json" { "generators": { "@nx/jest:configuration": { "testEnvironment": "jsdom" } } } ``` Rewrite a per-project generator default: ##### Before ```json title="apps/myapp/project.json" {4} { "generators": { "@nx/jest:configuration": { "skipSetupFile": true } } } ``` ##### After ```json title="apps/myapp/project.json" { "generators": { "@nx/jest:configuration": { "setupFile": "none" } } } ``` The nested form (`@nx/jest` -> `configuration`) is handled the same way. ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/jest/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/jest` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/jest/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/jest/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/jest/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/jest/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ### 23.0.0-pin-jest-30-3-for-rn-compat-package-updates **Version**: 23.0.0-beta.9 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `jest` | `~30.3.0` | Updated only | `babel-jest` | `~30.3.0` | Updated only | `@types/jest` | `~30.0.0` | Updated only ## 22.3.x ### `replace-removed-matcher-aliases-v22-3` **Version**: 22.3.2-beta.0 Replace removed matcher aliases in Jest v30 with their corresponding matcher #### Requires | Name | Version | |------|---------| `jest` | `>=30.0.0` | #### Replace Removed Matcher Aliases Replaces removed Jest matcher aliases in test files with their corresponding matchers to align with Jest v30 changes. Read more at the [Jest v30 migration notes](https://jestjs.io/docs/upgrading-to-jest30#jest-expect--matchers). #### Examples ##### Before ```typescript title="apps/myapp/src/app.spec.ts" describe('test', () => { it('should pass', async () => { expect(mockFn).toBeCalled(); expect(mockFn).toBeCalledTimes(1); expect(mockFn).toBeCalledWith(arg); expect(mockFn).lastCalledWith(arg); expect(mockFn).nthCalledWith(1, arg); expect(mockFn).toReturn(); expect(mockFn).toReturnTimes(1); expect(mockFn).toReturnWith(value); expect(mockFn).lastReturnedWith(value); expect(mockFn).nthReturnedWith(1, value); expect(() => someFn()).toThrowError(); expect(() => someFn()).not.toThrowError(); await expect(someAsyncFn()).rejects.toThrowError(); await expect(someAsyncFn()).resolves.not.toThrowError(); }); }); ``` ##### After ```typescript title="apps/myapp/src/app.spec.ts" describe('test', () => { it('should pass', async () => { expect(mockFn).toHaveBeenCalled(); expect(mockFn).toHaveBeenCalledTimes(1); expect(mockFn).toHaveBeenCalledWith(arg); expect(mockFn).toHaveBeenLastCalledWith(arg); expect(mockFn).toHaveBeenNthCalledWith(1, arg); expect(mockFn).toHaveReturned(); expect(mockFn).toHaveReturnedTimes(1); expect(mockFn).toHaveReturnedWith(value); expect(mockFn).toHaveLastReturnedWith(value); expect(mockFn).toHaveNthReturnedWith(1, value); expect(() => someFn()).toThrow(); expect(() => someFn()).not.toThrow(); await expect(someAsyncFn()).rejects.toThrow(); await expect(someAsyncFn()).resolves.not.toThrow(); }); }); ``` ### 22.3.0-package-updates **Version**: 22.3.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `jest` | `^30.0.0` | Updated only | `@types/jest` | `^30.0.0` | Updated only | `expect` | `^30.0.0` | Updated only | `@jest/globals` | `^30.0.0` | Updated only | `jest-jasmine2` | `^30.0.0` | Updated only | `jest-environment-jsdom` | `^30.0.0` | Updated only | `jest-util` | `^30.0.0` | Updated only | `babel-jest` | `^30.0.0` | Updated only | `@swc/jest` | `~0.2.38` | Updated only ### 22.3.0-jest-preset-angular-package-updates **Version**: 22.3.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `jest-preset-angular` | `~16.0.0` | Updated only ## 22.2.x ### `convert-jest-config-to-cjs` **Version**: 22.2.0-beta.2 Convert jest.config.ts files from ESM to CJS syntax (export default -> module.exports, import -> require) for projects using CommonJS resolution to ensure correct loading under Node.js type-stripping. #### Convert Jest Config to CJS Converts `jest.config.ts` files to `jest.config.cts`. This is needed because Node.js type-stripping in newer versions (22+, 24+) can cause issues with ESM syntax in `.ts` files when the project is configured for CommonJS. This migration only runs if `@nx/jest/plugin` is registered in `nx.json`. #### Examples ##### Before ```typescript title="jest.config.ts" import { foo } from 'bar'; import baz from 'qux'; export default { displayName: 'myapp', preset: foo, transform: baz, }; ``` ##### After ```typescript title="jest.config.cts" const { foo } = require('bar'); const baz = require('qux').default ?? require('qux'); module.exports = { displayName: 'myapp', preset: foo, transform: baz, }; ``` ## 21.3.x ### `rename-test-path-pattern` **Version**: 21.3.0-beta.3 Rename the CLI option `testPathPattern` to `testPathPatterns`. #### Rename `testPathPattern` to `testPathPatterns` Renames the `testPathPattern` option to `testPathPatterns` in the `@nx/jest:jest` executor configuration to align with Jest v30 CLI changes. Read more at the [Jest v30 migration notes](https://jestjs.io/docs/upgrading-to-jest30#--testpathpattern-was-renamed-to---testpathpatterns). #### Examples Rename the option in project configuration: ##### Before ```json title="apps/myapp/project.json" {7} { "targets": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "apps/myapp/jest.config.ts", "testPathPattern": "some-regex" } } } } ``` ##### After ```json title="apps/myapp/project.json" {7} { "targets": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "apps/myapp/jest.config.ts", "testPathPatterns": "some-regex" } } } } ``` Rename the option in project configuration with configurations: ##### Before ```json title="apps/myapp/project.json" {7,10,11} { "targets": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "apps/myapp/jest.config.ts", "testPathPattern": "some-regex" }, "configurations": { "development": { "testPathPattern": "regex-dev" }, "production": { "testPathPattern": "regex-prod" } } } } } ``` ##### After ```json title="apps/myapp/project.json" {7,10,11} { "targets": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "apps/myapp/jest.config.ts", "testPathPatterns": "some-regex" }, "configurations": { "development": { "testPathPatterns": "regex-dev" }, "production": { "testPathPatterns": "regex-prod" } } } } } ``` Rename the option in a target default using the `@nx/jest:jest` executor: ##### Before ```json title="nx.json" {7} { "targetDefaults": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "{projectRoot}/jest.config.ts", "testPathPattern": "some-regex" } } } } ``` ##### After ```json title="nx.json" {7} { "targetDefaults": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "{projectRoot}/jest.config.ts", "testPathPatterns": "some-regex" } } } } ``` Rename the option in a target default using the `@nx/jest:jest` executor as the key: ##### Before ```json title="nx.json" {6} { "targetDefaults": { "@nx/jest:jest": { "options": { "jestConfig": "{projectRoot}/jest.config.ts", "testPathPattern": "some-regex" } } } } ``` ##### After ```json title="nx.json" {6} { "targetDefaults": { "@nx/jest:jest": { "options": { "jestConfig": "{projectRoot}/jest.config.ts", "testPathPatterns": "some-regex" } } } } ``` ### `replace-removed-matcher-aliases` **Version**: 21.3.0-beta.3 Replace removed matcher aliases in Jest v30 with their corresponding matcher #### Requires | Name | Version | |------|---------| `jest` | `>=30.0.0` | #### Replace Removed Matcher Aliases Replaces removed Jest matcher aliases in test files with their corresponding matchers to align with Jest v30 changes. Read more at the [Jest v30 migration notes](https://jestjs.io/docs/upgrading-to-jest30#jest-expect--matchers). #### Examples ##### Before ```typescript title="apps/myapp/src/app.spec.ts" describe('test', () => { it('should pass', async () => { expect(mockFn).toBeCalled(); expect(mockFn).toBeCalledTimes(1); expect(mockFn).toBeCalledWith(arg); expect(mockFn).lastCalledWith(arg); expect(mockFn).nthCalledWith(1, arg); expect(mockFn).toReturn(); expect(mockFn).toReturnTimes(1); expect(mockFn).toReturnWith(value); expect(mockFn).lastReturnedWith(value); expect(mockFn).nthReturnedWith(1, value); expect(() => someFn()).toThrowError(); expect(() => someFn()).not.toThrowError(); await expect(someAsyncFn()).rejects.toThrowError(); await expect(someAsyncFn()).resolves.not.toThrowError(); }); }); ``` ##### After ```typescript title="apps/myapp/src/app.spec.ts" describe('test', () => { it('should pass', async () => { expect(mockFn).toHaveBeenCalled(); expect(mockFn).toHaveBeenCalledTimes(1); expect(mockFn).toHaveBeenCalledWith(arg); expect(mockFn).toHaveBeenLastCalledWith(arg); expect(mockFn).toHaveBeenNthCalledWith(1, arg); expect(mockFn).toHaveReturned(); expect(mockFn).toHaveReturnedTimes(1); expect(mockFn).toHaveReturnedWith(value); expect(mockFn).toHaveLastReturnedWith(value); expect(mockFn).toHaveNthReturnedWith(1, value); expect(() => someFn()).toThrow(); expect(() => someFn()).not.toThrow(); await expect(someAsyncFn()).rejects.toThrow(); await expect(someAsyncFn()).resolves.not.toThrow(); }); }); ``` ### 21.3.0-package-updates **Version**: 21.3.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `jest` | `~30.0.0` | Updated only | `@types/jest` | `~30.0.0` | Updated only | `expect` | `~30.0.0` | Updated only | `@jest/globals` | `~30.0.0` | Updated only | `jest-jasmine2` | `~30.0.0` | Updated only | `jest-environment-jsdom` | `~30.0.0` | Updated only | `babel-jest` | `~30.0.0` | Updated only | `@swc/jest` | `~0.2.38` | Updated only ### 21.3.3-package-updates **Version**: 21.3.3-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `ts-jest` | `~29.4.0` | Updated only ### 21.3.3-jest-util-package-updates **Version**: 21.3.3-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `jest-util` | `~30.0.0` | Updated only ## 21.0.x ### `replace-getJestProjects-with-getJestProjectsAsync-v21` **Version**: 21.0.0-beta.9 Replace usage of `getJestProjects` with `getJestProjectsAsync`. #### Replace Usage of `getJestProjects` with `getJestProjectsAsync` Replaces the usage of the removed `getJestProjects` function with the `getJestProjectsAsync` function. #### Sample Code Changes ##### Before ```ts title="jest.config.ts" import { getJestProjects } from '@nx/jest'; export default { projects: getJestProjects(), }; ``` ##### After ```ts title="jest.config.ts" import { getJestProjectsAsync } from '@nx/jest'; export default async () => ({ projects: await getJestProjectsAsync(), }); ``` ### `remove-tsconfig-option-from-jest-executor` **Version**: 21.0.0-beta.10 Remove the previously deprecated and unused `tsConfig` option from the `@nx/jest:jest` executor. #### Remove `tsConfig` Option from Jest Executor Removes the previously deprecated and unused `tsConfig` option from the `@nx/jest:jest` executor configuration in all projects. #### Examples Remove the option from the project configuration: ##### Before ```json title="apps/myapp/project.json" {7} { "targets": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "apps/myapp/jest.config.ts", "tsConfig": "apps/myapp/tsconfig.spec.json" } } } } ``` ##### After ```json title="apps/myapp/project.json" { "targets": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "apps/myapp/jest.config.ts" } } } } ``` Remove the option from a target default using the `@nx/jest:jest` executor: ##### Before ```json title="nx.json" {7} { "targetDefaults": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "{projectRoot}/jest.config.ts", "tsConfig": "{projectRoot}/tsconfig.spec.json" } } } } ``` ##### After ```json title="nx.json" { "targetDefaults": { "test": { "executor": "@nx/jest:jest", "options": { "jestConfig": "{projectRoot}/jest.config.ts" } } } } ``` Remove the option from a target default using the `@nx/jest:jest` executor as the key: ##### Before ```json title="nx.json" {6} { "targetDefaults": { "@nx/jest:jest": { "options": { "jestConfig": "{projectRoot}/jest.config.ts", "tsConfig": "{projectRoot}/tsconfig.spec.json" } } } } ``` ##### After ```json title="nx.json" { "targetDefaults": { "@nx/jest:jest": { "options": { "jestConfig": "{projectRoot}/jest.config.ts" } } } } ``` --- ## Playwright Get started with [Nx with Playwright](/docs/technologies/test-tools/playwright/introduction), or browse the topics below. {% index_page_cards path="technologies/test-tools/playwright" /%} --- ## @nx/playwright Executors The @nx/playwright plugin provides various executors to help you create and configure playwright projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/playwright introduction](/docs/technologies/test-tools/playwright/introduction). ### `playwright` Run Playwright tests. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `browser` | string | Browser to use for tests, one of 'all', 'chromium', 'firefox' or 'webkit'. If a playwright config is provided/discovered then the browserName value is expected from the configured 'projects' | | | `cacheDir` | string | Directory for Playwright internal cache (browser binaries, etc.). Sets the PWTEST_CACHE_DIR environment variable. | | | `config` | string | Configuration file, or a test directory with optional | | | `debug` | boolean | Run tests with Playwright Inspector. Shortcut for 'PWDEBUG=1' environment variable and '--timeout=0',--max-failures=1 --headed --workers=1' options | | | `forbidOnly` | boolean | Fail if test.only is called | | | `fullyParallel` | boolean | Run all tests in parallel | | | `globalTimeout` | number | Maximum time this test suite can run in milliseconds | | | `grep` | string | Only run tests matching this regular expression | | | `grepInvert` | string | Only run tests that do not match this regular expression | | | `headed` | boolean | Run tests in headed browsers | | | `ignoreSnapshots` | boolean | Ignore screenshot and snapshot expectations | | | `lastFailed` | boolean | Run only the tests that failed in the last run | | | `list` | boolean | Collect all the tests and report them, but do not run | | | `maxFailures` | string | Stop after the first N failures | | | `noDeps` | boolean | Do not run project dependencies | | | `output` | string | Folder for output artifacts | | | `passWithNoTests` | boolean | Makes test run succeed even if no tests were found | `true` | | `project` | array | Only run tests from the specified list of projects | | | `quiet` | boolean | Suppress stdio | | | `repeatEach` | number | Run each test N times | | | `reporter` | string | Common Reporter values to use, comma-separated, 'list', 'line', 'dot', 'json', 'junit', 'null', 'github', 'html', 'blob'. To configure reporter options, use the playwright configuration. | | | `retries` | number | Maximum retry count for flaky tests, zero for no retries | | | `shard` | string | Shard tests and execute only the selected shard, specify in the form 'current/all', 1-based, for example '3/5' | | | `skipInstall` | boolean | Skip running playwright install before running playwright tests. This is to ensure that playwright browsers are installed before running tests. | `false` | | `testFiles` | array | Test files to run | | | `timeout` | number | Specify test timeout threshold in milliseconds, zero for unlimited | | | `trace` | string | Force tracing mode, can be 'on', 'off', 'on-first-retry', 'on-all-retries', 'retain-on-failure' | | | `ui` | boolean | Run tests in interactive UI mode | | | `uiHost` | string | Host to serve UI on; specifying this option opens UI in a browser tab | | | `uiPort` | number | Port to serve UI on, 0 for any free port; specifying this option opens UI in a browser tab | | | `updateSnapshots` | boolean | Update snapshots with actual results. Snapshots will be created if missing. | | | `workers` | string | Number of concurrent workers or percentage of logical CPU cores, use 1 to run in a single worker | | --- ## @nx/playwright Generators The @nx/playwright plugin provides various generators to help you create and configure playwright projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/playwright introduction](/docs/technologies/test-tools/playwright/introduction). ## `configuration` Add a Playwright configuration. **Usage:** ```bash nx generate @nx/playwright:configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string [**required**] | The project to add a Playwright configuration to. | | | `--directory` | string | A directory where the project is placed relative from the project root. | `"e2e"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--rootProject` | boolean | Create a application at the root of the workspace | `false` | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipInstall` | boolean | Skip running `playwright install`. This is to ensure that playwright browsers are installed. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--webServerAddress` | string | The address of the web server. | | | `--webServerCommand` | string | The command to start the web server. | | ## `convert-to-inferred` Convert existing Playwright project(s) using `@nx/playwright:playwright` executor to use `@nx/playwright/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target. **Usage:** ```bash nx generate @nx/playwright:convert-to-inferred [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/playwright:playwright` executor to use `@nx/playwright/plugin`. | | | `--skipFormat` | boolean | Whether to format files at the end of the migration. | `false` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/playwright: --help ``` --- ## Guides {% index_page_cards path="technologies/test-tools/playwright/guides" /%} --- ## Nx with Playwright Playwright is a modern web test runner. With included features such as: - Cross browser support, including mobile browsers - Multi tab, origin, and user support - Automatic waiting - Test generation - Screenshots and videos ## Requirements The `@nx/playwright` plugin supports the following package versions. | Package | Supported Versions | | ------------------ | ------------------ | | `@playwright/test` | ^1.36.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setting up @nx/playwright ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/playwright` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/playwright` by running the following command: ```shell nx add @nx/playwright ``` This will install the correct version of `@nx/playwright`. ### How @nx/playwright infers tasks The `@nx/playwright` plugin will create a task for any project that has a Playwright configuration file present. Any of the following files will be recognized as a Playwright configuration file: - `playwright.config.js` - `playwright.config.ts` - `playwright.config.mjs` - `playwright.config.mts` - `playwright.config.cjs` - `playwright.config.cts` The configuration directory must also contain a `package.json` or `project.json`. ### View inferred tasks To view inferred tasks for a project, open the [project details view](/docs/concepts/mental-model#inferred-tasks) in Nx Console or run `nx show project my-project --web` in the command line. ### @nx/playwright configuration The `@nx/playwright/plugin` is configured in the `plugins` array in `nx.json`. ```json frame="none" // nx.json { "plugins": [ { "plugin": "@nx/playwright/plugin", "options": { "ciTargetName": "e2e-ci", "targetName": "e2e" } } ] } ``` The `targetName` and `ciTargetName` options control the name of the inferred Playwright tasks. The default names are `e2e` and `e2e-ci`. When your Playwright config declares a `webServer` that sets `reuseExistingServer` to `true` and whose `command` runs an Nx task, Nx runs that task as a dependency of the Playwright tasks. The command must be `nx run :` or `nx `, optionally prefixed by a package manager runner such as `npx` or `pnpm exec`, with no extra arguments. The Playwright docs suggest setting `reuseExistingServer` to `!process.env.CI`. Set it to `true` instead, since Nx already runs the web server as a task dependency. With `false`, Nx skips that task and Playwright starts its own server inside each Playwright task. If that `webServer` also sets a `port` or `url`, Nx infers an extra task, named `--wait-for-webserver` (`e2e--wait-for-webserver` by default), that waits for the server to be ready. The atomized CI tasks share that task when it would wait for the same servers, depend on the same serve tasks, and load the same env files. Otherwise they get their own `--wait-for-webserver` task. Playwright then finds the server already running and reuses it. The `webServerTimeout` option sets how long, in milliseconds, that task waits before failing. Set the `waitForWebServer` option to `false` to opt out of that task. For the `webServer` shapes Nx leaves to Playwright and for how Nx caches and re-infers the server address, see [Playwright web server readiness](/docs/kb/playwright-web-server-readiness). ### Splitting E2E tests The `targetName` task runs the complete Playwright suite and caches the configured test and reporter outputs. The `ciTargetName` task uses the [Atomizer](/docs/features/ci-features/split-e2e-tasks). Both options accept task names as strings. To run the non-atomized suite in CI, invoke the `targetName` task (the default is `e2e`) instead of the `ciTargetName` task. Use `include` and `exclude` glob patterns on the plugin entry to scope inference. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/playwright/plugin`. ## E2E testing By default, when creating a new frontend application, Nx will prompt for which e2e test runner to use. Select `playwright` or pass in the arg `--e2eTestRunner=playwright` ```shell nx g @nx/web:app apps/frontend --e2eTestRunner=playwright ``` ### Add Playwright e2e to an existing project To generate an E2E project for an existing project, run the following generator ```shell nx g @nx/playwright:configuration --project=your-app-name ``` The generator prompts for the [web server option](https://playwright.dev/docs/test-webserver) to add to the Playwright config. Pass `--webServerCommand` and `--webServerAddress` to skip the prompts. ```shell nx g @nx/playwright:configuration --project=your-app-name --webServerCommand="npx nx serve your-project-name" --webServerAddress="http://localhost:4200" ``` ### Testing applications Run `nx e2e ` to execute e2e tests with Playwright {% aside type="note" title="Selecting Specific Specs" %} You can use the `--grep/-g` flag to filter tests using regular expressions. You can use the `--grepInvert/-gv` flag to filter out tests that match the regular expression. ```bash frame="none" # run tests that match the regular expression nx e2e frontend-e2e --grep="feat-a" # run tests that don't match the regular expression nx e2e frontend-e2e --grepInvert="feat-a" ``` {% /aside %} By default, Playwright will run in headless mode. You will have the result of all the tests and errors (if any) in your terminal. Test output such as reports, screenshots, and videos will be accessible in `dist/.playwright/apps//`. This can be configured with the `outputDir` configuration options. {% aside type="note" title="Output Caching" %} If changing the output directory or report output, make sure to update the [target outputs](/docs/concepts/how-caching-works#what-is-cached) so the artifacts are correctly cached {% /aside %} ### Watching for changes With, `nx e2e frontend-e2e --ui` Playwright will start in headed mode where you can see your application being tested. From, there you can toggle on the watch icon which will rerun the tests when the spec file updates. ```shell nx e2e --ui ``` You can also use `--headed` flag to run Playwright where the browser can be seen without using the [Playwright UI](https://playwright.dev/docs/test-ui-mode) ### Specifying a project/target browser The default generated Playwright configuration will contain a `projects` property that contains a list of browsers to run the tests against. It should look similar to this: ```ts frame="none" export default defineConfig({ ..., projects: [ { name: "chromium", use: { ...devices["Desktop Chrome"] }, }, { name: "firefox", use: { ...devices["Desktop Firefox"] }, }, { name: "webkit", use: { ...devices["Desktop Safari"] }, } ] }); ``` By default, Playwright will run tests against all browsers in the `projects` list. You can specify a specific browser to run the tests against by passing the `--project` flag to the `nx e2e` command. ```shell nx e2e frontend-e2e -- --project=firefox ``` {% aside type="note" title="Argument Forwarding" %} As Nx also has a `--project` argument, you need to use `--` to forward the argument to the Playwright configuration. {% /aside %} ### Specifying a base url The `baseURL` property within the Playwright configuration can control where the tests visit by default. ```ts frame="none" import { defineConfig } from '@playwright/test'; export default defineConfig({ // Rest of your config... // Run your local dev server before starting the tests webServer: { command: 'npx nx serve ', url: 'http://localhost:4200', reuseExistingServer: true, }, use: { baseURL: 'http://localhost:4200', // url playwright visits with `await page.goto('/')`; }, }); ``` In order to set different `baseURL` values for different environments you can pass them via [environment variables and Nx configurations](/docs/reference/environment-variables#loading-environment-variables) or optionally via setting them per the environment they are needed in such as `CI` ```ts frame="none" import { defineConfig } from '@playwright/test'; const baseUrl = process.env.BASE_URL ?? (process.env.CI ? 'https://some-staging-url.example.com' : 'http://localhost:4200'); export default defineConfig({ // Rest of your config... use: { baseURL: baseUrl, // url playwright visits with `await page.goto('/')`; }, }); ``` Leave `webServer` out when the tests run against a deployed environment. There's no local server for Playwright to start, and pointing `webServer.url` at a URL that already answers makes Playwright fail unless `reuseExistingServer` is `true`. By default Nx, provides a `nxE2EPreset` with predefined configuration for Playwright. ```ts frame="none" import { defineConfig } from '@playwright/test'; import { nxE2EPreset } from '@nx/playwright/preset'; import { workspaceRoot } from '@nx/devkit'; // For CI, you may want to set BASE_URL to the deployed application. const baseURL = process.env['BASE_URL'] || 'http://localhost:4200'; /** * Read environment variables from file. * https://github.com/motdotla/dotenv */ // require('dotenv').config(); /** * See https://playwright.dev/docs/test-configuration. */ export default defineConfig({ ...nxE2EPreset(__filename, { testDir: './e2e' }), /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { baseURL, /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', }, /* Run your local dev server before starting the tests */ webServer: { command: 'npx nx serve ', url: baseURL, reuseExistingServer: true, cwd: workspaceRoot, }, }); ``` This preset sets up the `outputDir` and [HTML reporter](https://playwright.dev/docs/test-reporters#html-reporter) to output in `dist/.playwright/` and sets up chromium, firefox, webkit browsers to be used a browser targets. If you want to use mobile and/or branded browsers you can pass those options into the preset function ```ts frame="none" export default defineConfig({ ...nxE2EPreset(__filename, { testDir: './e2e', includeMobileBrowsers: true, // includes mobile Chrome and Safari includeBrandedBrowsers: true, // includes Google Chrome and Microsoft Edge }), // other settings }); ``` If you want to override any settings within the `nxE2EPreset`, You can define them after the preset like so ```ts frame="none" const config = nxE2EPreset(__filename, { testDir: './e2e', includeMobileBrowsers: true, // includes mobile Chrome and Safari includeBrandedBrowsers: true, // includes Google Chrome and Microsoft Edge }); export default defineConfig({ ...config retries: 3, reporters: [...config.reporters, /* other reporter settings */], }); ``` See the [Playwright configuration docs](https://playwright.dev/docs/test-configuration) for more options for Playwright. ## Set up CI In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/playwright Migrations For an overview of the plugin and setup instructions, see the [@nx/playwright introduction](/docs/technologies/test-tools/playwright/introduction). The @nx/playwright plugin provides various migrations to help you migrate to newer versions of playwright projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.0.x ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/playwright/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/playwright` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/playwright/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/playwright/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/playwright/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/playwright/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. --- ## Storybook Get started with [Nx with Storybook](/docs/technologies/test-tools/storybook/introduction), or browse the topics below. {% index_page_cards path="technologies/test-tools/storybook" /%} --- ## @nx/storybook Executors The @nx/storybook plugin provides various executors to help you create and configure storybook projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/storybook introduction](/docs/technologies/test-tools/storybook/introduction). ### `build` Build storybook in production mode. `project.json`: ```json //... "ui": { "targets": { //... "build-storybook": { "executor": "@nx/storybook:build", "outputs": ["{options.outputDir}"], "options": { "outputDir": "dist/storybook/ui", "configDir": "libs/ui/.storybook" }, "configurations": { "ci": { "quiet": true } } } } ``` ```bash nx run ui:build-storybook ``` ### Examples #### For non-Angular projects ###### Working in docsMode You can work in docs mode, building a documentation-only site, by setting the `docsMode` option to `true` and using the `@storybook/addon-docs` addon. Read more on the [Storybook documentation page for `addon-docs`](https://storybook.js.org/addons/@storybook/addon-docs). ```json "storybook": { "executor": "@nx/storybook:build", "options": { "port": 4400, "configDir": "libs/ui/.storybook", "docsMode": true }, "configurations": { "ci": { "quiet": true } } } ``` #### For Angular projects ###### Default configuration This is the default configuration for Angular projects using Storybook. You can see that it uses the native `@storybook/angular:build-storybook` executor. You can read more about the configuration options at the relevant [Storybook documentation page](https://storybook.js.org/docs/angular/get-started/install). ```json "build-storybook": { "executor": "@storybook/angular:build-storybook", "outputs": ["{options.outputDir}"], "options": { "outputDir": "dist/storybook/ngapp", "configDir": "apps/ngapp/.storybook", "browserTarget": "ngapp:build", "compodoc": false }, "configurations": { "ci": { "quiet": true } } } ``` ###### Changing the browserTarget You can set the `browserTarget` to use `build-storybook` as the builder. This is most useful in the cases where your project does not have a `build` target. Read more about the `browserTarget` in the [Set up Storybook for Angular Projects](/recipes/storybook/overview-angular) recipe. ```json "build-storybook": { "executor": "@storybook/angular:build-storybook", "outputs": ["{options.outputDir}"], "options": { "outputDir": "dist/storybook/ngapp", "configDir": "apps/ngapp/.storybook", "browserTarget": "ngapp:build-storybook", "compodoc": false }, "configurations": { "ci": { "quiet": true } } } ``` ###### Adding styles You can add paths to stylesheets to be included in the Storybook build by using the `styles` array. You can also add `stylePreprocessorOptions`, much like you would do in the Angular builder. You can read more in our guide about [styles and preprocessor options for Storybook](/recipes/storybook/angular-configuring-styles). ```json "build-storybook": { "executor": "@storybook/angular:build-storybook", "outputs": ["{options.outputDir}"], "options": { "outputDir": "dist/storybook/ngapp", "configDir": "apps/ngapp/.storybook", "browserTarget": "ngapp:build-storybook", "compodoc": false, "styles": ["some-styles.css"], "stylePreprocessorOptions": { "includePaths": ["some-style-paths"] } }, "configurations": { "ci": { "quiet": true } } } ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `configDir` | string [**required**] | Directory where to load Storybook configurations from. | | | `debugWebpack` | boolean | Display final webpack configurations for debugging purposes. | | | `disableTelemetry` | boolean | Disables Storybook's telemetry. | | | `docs` | boolean | Starts Storybook in documentation mode. Learn more about it : https://storybook.js.org/docs/react/writing-docs/build-documentation#preview-storybooks-documentation. | | | `docsMode` | boolean | Build a documentation-only site using addon-docs. | `false` | | `loglevel` | string | Controls level of logging during build. Can be one of: [silly, verbose, info (default), warn, error, silent]. | | | `outputDir` | string | Directory where to store built files. | | | `quiet` | boolean | Suppress verbose build output. | | | `stylePreprocessorOptions` | object | Options to pass to style preprocessors. | | | `styles` | array | Global styles to be included in the build. | | | `webpackStatsJson` | boolean | string | Write Webpack Stats JSON to disk. | `false` | ### `storybook` Serve up Storybook in development mode. `project.json`: ```json //... "ui": { "targets": { //... "storybook": { "executor": "@nx/storybook:storybook", "options": { "port": 4400, "configDir": "libs/ui/.storybook" }, "configurations": { "ci": { "quiet": true } } }, } } ``` ```bash nx run ui:storybook ``` ### Examples #### For non-Angular projects ###### Working in docsMode You can work in docs mode, building a documentation-only site, by setting the `docsMode` option to `true` and using the `@storybook/addon-docs` addon. Read more on the [Storybook documentation page for `addon-docs`](https://storybook.js.org/addons/@storybook/addon-docs). ```json "storybook": { "executor": "@nx/storybook:storybook", "options": { "port": 4400, "configDir": "libs/ui/.storybook", "docsMode": true }, "configurations": { "ci": { "quiet": true } } } ``` #### For Angular projects ###### Default configuration This is the default configuration for Angular projects using Storybook. You can see that it uses the native `@storybook/angular:start-storybook` executor. You can read more about the configuration options at the relevant [Storybook documentation page](https://storybook.js.org/docs/angular/get-started/install). ```json "storybook": { "executor": "@storybook/angular:start-storybook", "options": { "port": 4400, "configDir": "libs/ui/.storybook", "browserTarget": "ui:build", "compodoc": false }, "configurations": { "ci": { "quiet": true } } }, ``` ###### Changing the browserTarget You can set the `browserTarget` to use `build-storybook` as the builder. This is most useful in the cases where your project does not have a `build` target. Read more about the `browserTarget` in the [Set up Storybook for Angular Projects](/recipes/storybook/overview-angular) recipe. ```json "storybook": { "executor": "@storybook/angular:start-storybook", "options": { "port": 4400, "configDir": "libs/ui/.storybook", "browserTarget": "ui:build-storybook", "compodoc": false }, "configurations": { "ci": { "quiet": true } } }, ``` ###### Adding styles You can add paths to stylesheets to be included in the Storybook build by using the `styles` array. You can also add `stylePreprocessorOptions`, much like you would do in the Angular builder. You can read more in our guide about [styles and preprocessor options for Storybook](/recipes/storybook/angular-configuring-styles). ```json "storybook": { "executor": "@storybook/angular:start-storybook", "options": { "port": 4400, "configDir": "libs/ui/.storybook", "browserTarget": "ui:build", "compodoc": false, "styles": ["some-styles.css"], "stylePreprocessorOptions": { "includePaths": ["some-style-paths"] } }, "configurations": { "ci": { "quiet": true } } }, ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `configDir` | string [**required**] | Directory where to load Storybook configurations from. | | | `ci` | boolean | CI mode (skip interactive prompts, don't open browser). | `false` | | `debugWebpack` | boolean | Display final webpack configurations for debugging purposes. | | | `disableTelemetry` | boolean | Disables Storybook's telemetry. | | | `docs` | boolean | Starts Storybook in documentation mode. Learn more about it : https://storybook.js.org/docs/react/writing-docs/build-documentation#preview-storybooks-documentation. | | | `docsMode` | boolean | Starts Storybook in documentation mode. Learn more about it : https://storybook.js.org/docs/react/writing-docs/build-documentation#preview-storybooks-documentation. | `false` | | `host` | string | Host to listen on. | | | `https` | boolean | Serve Storybook over HTTPS. Note: You must provide your own certificate information. | `false` | | `loglevel` | string | Controls level of logging during build. Can be one of: [silly, verbose, info (default), warn, error, silent]. | | | `noOpen` | boolean | Do not open Storybook automatically in the browser. | | | `open` | boolean | Open browser window automatically. | | | `port` | number | Port to listen on. | `9009` | | `previewUrl` | string | Preview URL. | | | `quiet` | boolean | Suppress verbose build output. | | | `smokeTest` | boolean | Exit after successful start. | | | `sslCa` | string | Provide an SSL certificate authority. (Optional with --https, required if using a self-signed certificate). | | | `sslCert` | string | Provide an SSL certificate. (Required with --https). | | | `sslKey` | string | Provide an SSL key. (Required with --https). | | | `uiFramework` | string | Storybook framework npm package. | | | `webpackStatsJson` | boolean | string | Write Webpack Stats JSON to disk. | `false` | --- ## @nx/storybook Generators The @nx/storybook plugin provides various generators to help you create and configure storybook projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/storybook introduction](/docs/technologies/test-tools/storybook/introduction). ## `configuration` Add Storybook configuration to a UI library or an application. This is a framework-agnostic generator for setting up Storybook configuration for a project. ```bash nx g @nx/storybook:configuration ``` :::tip[Nx uses Storybook 10] Nx will configure your project to use Storybook v10. If you are not on Storybook 10 yet, please migrate. Please follow our [Storybook 10 migration generator](/docs/technologies/test-tools/storybook/generators#migrate-10) guide. ::: If you are using Angular, React, Next.js, Vue or React Native in your project, it's best to use the framework specific Storybook configuration generator: - [React Storybook Configuration Generator](/nx-api/react/generators/storybook-configuration) (React and Next.js projects) - [Angular Storybook Configuration Generator](/nx-api/angular/generators/storybook-configuration) - [React Native Storybook Configuration Generator](/nx-api/react-native/generators/storybook-configuration) - [Vue Storybook Configuration Generator](/nx-api/vue/generators/storybook-configuration) If you are not using one of the framework-specific generators mentioned above, when running this generator you will be prompted to provide the following: - The `name` of the project you want to generate the configuration for. - The `uiFramework` you want to use. Supported values are: - `@storybook/angular` - `@storybook/html-webpack5` - `@storybook/nextjs` - `@storybook/preact-webpack5` - `@storybook/react-webpack5` - `@storybook/react-vite` - `@storybook/server-webpack5` - `@storybook/svelte-webpack5` - `@storybook/svelte-vite` - `@storybook/sveltekit` - `@storybook/vue-webpack5` - `@storybook/vue-vite` - `@storybook/vue3-webpack5` - `@storybook/vue3-vite` - `@storybook/web-components-webpack5` - `@storybook/web-components-vite` - Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, all the necessary dependencies will be installed. Also, a `test-storybook` target will be generated in your project's `project.json`, with a command to invoke the [Storybook `test-runner`](https://storybook.js.org/docs/angular/writing-tests/test-runner). You can read more about this in the [Nx Storybook interaction tests documentation page](/recipes/storybook/storybook-interaction-tests#setup-storybook-interaction-tests). You must provide a `name` and a `uiFramework` for the generator to work. You can read more about how this generator works, in the [Storybook package overview page](/nx-api/storybook#generating-storybook-configuration). ### Examples #### Generate Storybook configuration using JavaScript ```bash nx g @nx/storybook:configuration ui --uiFramework=@storybook/web-components-vite --tsConfiguration=false ``` By default, our generator generates TypeScript Storybook configuration files. You can choose to use JavaScript for the Storybook configuration files of your project (the files inside the `.storybook` directory, eg. `.storybook/main.js`). **Usage:** ```bash nx generate @nx/storybook:configuration [options] ``` **Arguments:** ```bash nx generate @nx/storybook:configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--uiFramework` | string [**required**] | Storybook UI Framework to use. | | | `--configureStaticServe` | boolean | Add a static-storybook to serve the static storybook built files. | `false` | | `--interactionTests` | boolean | Set up Storybook interaction tests. | `true` | | `--js` | boolean | Generate JavaScript story files rather than TypeScript story files. | `false` | | `--linter` | string | The tool to use for running lint checks. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--tsConfiguration` | boolean | Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js. | `true` | ## `convert-to-inferred` Convert existing Storybook project(s) using `@nx/storybook:*` executors to use `@nx/storybook/plugin`. Defaults to migrating all projects. Pass '--project' to migrate only one target. **Usage:** ```bash nx generate @nx/storybook:convert-to-inferred [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/storybook:*` executors to use `@nx/storybook/plugin`. | | | `--skipFormat` | boolean | Whether to format files at the end of the migration. | `false` | ## `migrate-10` Migrate Storybook to version 10. Storybook 10 is a major release that brings new features and improvements. You can read more about it in the [Storybook 10 release article](https://storybook.js.org/blog/storybook-10). Apart from the new features, it also brings some breaking changes—notably, Storybook 10 requires configuration files to use ESM syntax instead of CommonJS. You can migrate your existing Nx workspace with Storybook configuration to use Storybook version 10. Nx offers the `@nx/storybook:migrate-10` generator to help you with this migration. ### How to use it Just call: ```bash npx nx g @nx/storybook:migrate-10 ``` :::danger[Commit your changes] It is advised that you start with a clean git history before running this generator, since it is going to be making changes to your workspace. ::: This generator calls the Storybook CLI upgrade command (`storybook@latest upgrade`) to update your Storybook packages and configuration. The logs will explain what is happening in every step. #### Accept the automigration prompts The Storybook CLI will prompt you to run some code generators and modifiers. You can say `yes` to these prompts to let Storybook automatically update your configuration files. #### Check the result Once the generator finishes and the Storybook CLI automigration scripts have run, verify that your `.storybook/main.ts` files use ESM syntax: ```ts title="apps/my-app/.storybook/main.ts" import type { StorybookConfig } from '@storybook/react-vite'; const config: StorybookConfig = { stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx))'], addons: ['@storybook/addon-essentials'], framework: { name: '@storybook/react-vite', options: {}, }, }; export default config; ``` #### Make sure that all works by running Storybook You can now use Storybook 10! 🎉 ```bash npx nx storybook PROJECT_NAME ``` and ```bash npx nx build-storybook PROJECT_NAME ``` ### Run the generator by automatically accepting the Storybook CLI prompts You can run the generator with the `--autoAcceptAllPrompts` flag, which will automatically accept all the Storybook CLI prompts. This is useful if you want to run the generator in a CI environment, or if you want to run the generator in a script. ```bash npx nx g @nx/storybook:migrate-10 --autoAcceptAllPrompts ``` ### AI-assisted migration for ESM conversion Storybook 10 requires configuration files to use ESM syntax. If you have CommonJS configuration files, Nx will generate an instructions file at `tools/ai-migrations/MIGRATE_STORYBOOK_10.md` when running `nx migrate`. This file contains detailed instructions that an AI agent (Claude, ChatGPT, GitHub Copilot, etc.) can use to convert your CJS configs to ESM. See the [Upgrading Storybook guide](/docs/technologies/test-tools/storybook/guides/upgrading-storybook) for more details on AI-assisted migrations. ### Report any issues and bugs Please report any issues and bugs you find [on the Nx GitHub page](https://github.com/nrwl/nx/issues/new/choose) or on the [Storybook GitHub page](https://github.com/storybookjs/storybook/issues/new/choose). **Usage:** ```bash nx generate @nx/storybook:migrate-10 [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--autoAcceptAllPrompts` | boolean | Say yes to all the prompts from the Storybook CLI migration scripts. | `false` | | `--configDir` | array | Directory(ies) where to load Storybook configurations from. Use this if you want to customize the Storybook projects you'd like to migrate. | `[]` | | `--skipAiInstructions` | boolean | Skip writing the AI migration instructions file to `tools/ai-migrations/`. | `false` | ## `migrate-8` Migrate Storybook to version 8. Storybook 8 is a major release that brings a lot of new features and improvements. You can read more about it in the [Storybook 8.0.0 release article](https://storybook.js.org/blog/storybook-8/). Apart from the new features and improvements it introduces, it also brings some breaking changes. You can read more about them in the [Storybook 8 migration docs](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#from-version-7x-to-800) and the [Storybook 8.0.0 migration guide](https://storybook.js.org/docs/react/migration-guide). You can now migrate your existing Nx workspace with Storybook configuration to use Storybook version 8. To help you, Nx offers the `@nx/storybook:migrate-8` generator. This generator will help you migrate your existing Storybook setup to version 8. ### How to use it Just call: ```bash npx nx g @nx/storybook:migrate-8 ``` :::danger[Commit your changes] It is advised that you start with a clean git history before running this generator, since it is going to be making lots of changes to your workspace. ::: You can run this generator using the above command, without passing any options. This will start the migration process for all your projects that have Storybook configured. The logs will explain what is happening in every step, and the logs are mixed Nx and Storybook CLI logs. During the process you will be prompted by the Storybook CLI to accept the automigration scripts. You can read more about that in the next section. When the generator finishes, you will see a summary of the changes that were made to your workspace, and it will also create a new file, called `storybook-migration-summary.md` at the root of your project, which will contain a list of all the changes that were made to your workspace. #### Accept the automigration prompts The Storybook CLI (running through our generator) will prompt you to run some code generators and modifiers. You can say `yes` to these prompts, which are usually the following (there may be more or less, depending on your setup, and depending on the latest versions of the Storybook CLI - this code is NOT managed by Nx, but by Storybook): - `mainjsFramework`: It will try to add the `framework` field in your project's `.storybook/main.js|ts` file. - `eslintPlugin`: installs the `eslint-plugin-storybook` - `newFrameworks`: removes unused dependencies (eg. `@storybook/builder-webpack5`, `@storybook/manager-webpack5`, `@storybook/builder-vite`) - `autodocsTrue`: adds `autodocs: true` to your project's `.storybook/main.js|ts` file #### Check the result Once the generator finishes, and the Storybook CLI automigration scripts have run, you should check the result. Examples of migrated `.storybook/main.js|ts` files would look like this: ##### Full example for Angular projects Here is an example of a project-level `.storybook/main.js|ts` file for an Angular project that has been migrated to Storybook version 8: ```ts title="apps/my-angular-app/.storybook/main.js" const config = { stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx)'], addons: ['@storybook/addon-essentials'], framework: { name: '@storybook/angular', options: {}, }, }; export default config; ``` ##### Full example for React projects with Vite Here is an example of a project-level `.storybook/main.js|ts` file for a React project using Vite that has been migrated to Storybook version 8: ```ts title="apps/my-react-app/.storybook/main.js" const config = { stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx)'], addons: ['@storybook/addon-essentials'], framework: { name: '@storybook/react-vite', options: { builder: { viteConfigPath: 'apps/rv1/vite.config.ts', }, }, }, }; export default config; ``` #### Make sure that all works by running Storybook You can now use Storybook 8! 🎉 ```bash npx nx build-storybook PROJECT_NAME ``` and ```bash npx nx storybook PROJECT_NAME ``` ### Run the generator by automatically accepting the Storybook CLI prompts You can run the generator with the `--autoAcceptAllPrompts` flag, which will automatically accept all the Storybook CLI prompts. This is useful if you want to run the generator in a CI environment, or if you want to run the generator in a script. Or if you are sure that you want to accept all the prompts! ```bash npx nx g @nx/storybook:migrate-8 --autoAcceptAllPrompts ``` The Storybook CLI may still ask you about some things, but mostly it should just run the whole migration suite uninterrupted. ### Run the migration manually Nx gives you the ability to run all the migration steps one by one, manually, but still with the help of our migrator. To help you out with the commands that you need to run, Nx will print out the instructions if you run the generator with the `--onlyShowListOfCommands` flag, like this: ```bash npx nx g @nx/storybook:migrate-8 --onlyShowListOfCommands ``` Essentially, the way to run the migration manually is the following: 1. Call the Nx generator to show you the list of commands: `npx nx g @nx/storybook:migrate-8 --onlyShowListOfCommands` 2. Call the Storybook upgrade script: `npx storybook@latest upgrade` 3. Call the Storybook automigrate scripts for each one of the projects using Storybook (the `@nx/storybook:migrate-8` will give you the list of all the commands) ### Report any issues and bugs Please report any issues and bugs you find [on the Nx GitHub page](https://github.com/nrwl/nx/issues/new/choose) or on the [Storybook GitHub page](https://github.com/storybookjs/storybook/issues/new/choose). **Usage:** ```bash nx generate @nx/storybook:migrate-8 [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--autoAcceptAllPrompts` | boolean | Say yes to all the prompts from the Storybook CLI migration scripts. | `false` | | `--noUpgrade` | boolean | Skip upgrading Storybook packages. Only use this option if you are already on version 8, and you do not want the latest beta. | `false` | | `--onlyShowListOfCommands` | boolean | Only show the steps that you need to follow in order to migrate. This does NOT make any changes to your code. | `false` | ## `migrate-9` Migrate Storybook to version 9. Storybook 9 is a major release that brings a lot of new features and improvements. You can read more about it in the [Storybook 9.0.0 release article](https://storybook.js.org/blog/storybook-9). Apart from the new features and improvements it introduces, it also brings some breaking changes. You can read more about them in the [Storybook 9 migration docs](https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#from-version-8x-to-900) and the [Storybook 9.0.0 migration guide](https://storybook.js.org/docs/react/migration-guide). You can now migrate your existing Nx workspace with Storybook configuration to use Storybook version 9. To help you, Nx offers the `@nx/storybook:migrate-9` generator. This generator will help you migrate your existing Storybook setup to version 9. ### How to use it Just call: ```bash npx nx g @nx/storybook:migrate-9 ``` :::danger[Commit your changes] It is advised that you start with a clean git history before running this generator, since it is going to be making lots of changes to your workspace. ::: You can run this generator using the above command, without passing any options. This will start the migration process for all your projects that have Storybook configured. The logs will explain what is happening in every step, and the logs are mixed Nx and Storybook CLI logs. During the process you will be prompted by the Storybook CLI to accept the automigration scripts. You can read more about that in the next section. When the generator finishes, you will see a summary of the changes that were made to your workspace, and it will also create a new file, called `storybook-migration-summary.md` at the root of your project, which will contain a list of all the changes that were made to your workspace. #### Accept the automigration prompts The Storybook CLI (running through our generator) will prompt you to run some code generators and modifiers. You can say `yes` to these prompts, which are usually the following (there may be more or less, depending on your setup, and depending on the latest versions of the Storybook CLI - this code is NOT managed by Nx, but by Storybook): - `mainjsFramework`: It will try to add the `framework` field in your project's `.storybook/main.js|ts` file. - `eslintPlugin`: installs the `eslint-plugin-storybook` - `newFrameworks`: removes unused dependencies (eg. `@storybook/builder-webpack5`, `@storybook/manager-webpack5`, `@storybook/builder-vite`) - `autodocsTrue`: adds `autodocs: true` to your project's `.storybook/main.js|ts` file #### Check the result Once the generator finishes, and the Storybook CLI automigration scripts have run, you should check the result. Examples of migrated `.storybook/main.js|ts` files would look like this: ##### Full example for Angular projects Here is an example of a project-level `.storybook/main.js|ts` file for an Angular project that has been migrated to Storybook version 9: ```ts title="apps/my-angular-app/.storybook/main.js" const config = { stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx)'], addons: ['@storybook/addon-essentials'], framework: { name: '@storybook/angular', options: {}, }, }; export default config; ``` ##### Full example for React projects with Vite Here is an example of a project-level `.storybook/main.js|ts` file for a React project using Vite that has been migrated to Storybook version 9: ```ts title="apps/my-react-app/.storybook/main.js" const config = { stories: ['../src/app/**/*.@(mdx|stories.@(js|jsx|ts|tsx)'], addons: ['@storybook/addon-essentials'], framework: { name: '@storybook/react-vite', options: { builder: { viteConfigPath: 'apps/rv1/vite.config.ts', }, }, }, }; export default config; ``` #### Make sure that all works by running Storybook You can now use Storybook 9! 🎉 ```bash npx nx build-storybook PROJECT_NAME ``` and ```bash npx nx storybook PROJECT_NAME ``` ### Run the generator by automatically accepting the Storybook CLI prompts You can run the generator with the `--autoAcceptAllPrompts` flag, which will automatically accept all the Storybook CLI prompts. This is useful if you want to run the generator in a CI environment, or if you want to run the generator in a script. Or if you are sure that you want to accept all the prompts! ```bash npx nx g @nx/storybook:migrate-9 --autoAcceptAllPrompts ``` The Storybook CLI may still ask you about some things, but mostly it should just run the whole migration suite uninterrupted. ### Run the migration manually Nx gives you the ability to run all the migration steps one by one, manually, but still with the help of our migrator. To help you out with the commands that you need to run, Nx will print out the instructions if you run the generator with the `--onlyShowListOfCommands` flag, like this: ```bash npx nx g @nx/storybook:migrate-9 --onlyShowListOfCommands ``` Essentially, the way to run the migration manually is the following: 1. Call the Nx generator to show you the list of commands: `npx nx g @nx/storybook:migrate-9 --onlyShowListOfCommands` 2. Call the Storybook upgrade script: `npx storybook@latest upgrade` 3. Call the Storybook automigrate scripts for each one of the projects using Storybook (the `@nx/storybook:migrate-9` will give you the list of all the commands) ### Report any issues and bugs Please report any issues and bugs you find [on the Nx GitHub page](https://github.com/nrwl/nx/issues/new/choose) or on the [Storybook GitHub page](https://github.com/storybookjs/storybook/issues/new/choose). **Usage:** ```bash nx generate @nx/storybook:migrate-9 [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--autoAcceptAllPrompts` | boolean | Say yes to all the prompts from the Storybook CLI migration scripts. | `false` | | `--noUpgrade` | boolean | Skip upgrading Storybook packages. Only use this option if you are already on version 9, and you do not want to install the packages again. | `false` | | `--onlyShowListOfCommands` | boolean | Only show the steps that you need to follow in order to migrate. This does NOT make any changes to your code. | `false` | | `--versionTag` | string | The version of Storybook to use. Use 'latest' to use the latest stable version, or 'next' to use the latest beta. | `"latest"` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/storybook: --help ``` --- ## Guides {% index_page_cards path="technologies/test-tools/storybook/guides" /%} --- ## Nx with Storybook [Storybook](https://storybook.js.org) is a development environment for UI components. Browse a component library, view the different states of each component, and interactively develop and test components. Briefly walk you through using Storybook within an Nx workspace. ## Requirements The `@nx/storybook` plugin supports the following package versions. | Package | Supported Versions | | ----------- | ------------------ | | `storybook` | >=8.0.0 <11.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setting up Storybook ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/storybook` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/storybook` by running the following command: ```shell nx add @nx/storybook ``` This will install the correct version of `@nx/storybook`. ### How @nx/storybook infers tasks The `@nx/storybook` plugin will create a task for any project that has a Storybook configuration file present. Any of the following files will be recognized as a Storybook configuration file: - `.storybook/main.js` - `.storybook/main.ts` - `.storybook/main.cjs` - `.storybook/main.cts` - `.storybook/main.mjs` - `.storybook/main.mts` The project directory that contains `.storybook` must also contain a `package.json` or `project.json`. ### View inferred tasks To view inferred tasks for a project, open the [project details view](/docs/concepts/mental-model#inferred-tasks) in Nx Console or run `nx show project my-project --web` in the command line. ### @nx/storybook configuration The `@nx/storybook/plugin` is configured in the `plugins` array in `nx.json`. ```json frame="none" // nx.json { "plugins": [ { "plugin": "@nx/storybook/plugin", "options": { "buildStorybookTargetName": "build-storybook", "serveStorybookTargetName": "storybook", "testStorybookTargetName": "test-storybook", "staticStorybookTargetName": "static-storybook", "buildDepsTargetName": "build-deps", "watchDepsTargetName": "watch-deps" } } ] } ``` | Option | Default | Inferred behavior | | --------------------------- | ------------------ | --------------------------------------------------- | | `buildStorybookTargetName` | `build-storybook` | Builds and caches the static Storybook output. | | `serveStorybookTargetName` | `storybook` | Runs the Storybook development server continuously. | | `testStorybookTargetName` | `test-storybook` | Runs the stories as tests when a runner is set up. | | `staticStorybookTargetName` | `static-storybook` | Serves the built Storybook output continuously. | | `buildDepsTargetName` | `build-deps` | Builds project dependencies. | | `watchDepsTargetName` | `watch-deps` | Watches project dependencies. | Use `include` and `exclude` glob patterns on the plugin entry to scope inference. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/storybook/plugin`. ## Using Storybook ### Generating Storybook configuration You can generate Storybook configuration for an individual project with this command: ```shell nx g @nx/storybook:configuration project-name ``` or {% tabs %} {% tabitem label="Angular" %} ```shell nx g @nx/angular:storybook-configuration my-angular-project ``` {% /tabitem %} {% tabitem label="React" %} ```shell nx g @nx/react:storybook-configuration my-react-project ``` {% /tabitem %} {% tabitem label="Vue" %} ```shell nx g @nx/vue:storybook-configuration my-vue-project ``` {% /tabitem %} {% /tabs %} These framework-specific generators will also **generate stories** and interaction tests for you. If you are NOT using a framework-specific generator (for [Angular](/docs/technologies/angular/generators#storybook-configuration), [React](/docs/technologies/react/generators#storybook-configuration), [Vue](/docs/technologies/vue/generators#storybook-configuration)), in the field `uiFramework` you must choose one of the following Storybook frameworks: - `@storybook/angular` - `@storybook/nextjs` - `@storybook/react-webpack5` - `@storybook/react-vite` - `@storybook/server-webpack5` - `@storybook/svelte-vite` - `@storybook/sveltekit` - `@storybook/vue-vite` - `@storybook/vue3-vite` - `@storybook/web-components-vite` Choosing one of these frameworks will have the following effects on your workspace: 1. Nx will install all the required Storybook packages that go with it. 2. Nx will generate a project-level `.storybook` folder (located under `libs/your-project/.storybook` or `apps/your-project/.storybook`) containing the essential configuration files for Storybook. 3. Nx will create new `targets` in your project's `project.json`, called `storybook`, `test-storybook` and `build-storybook`, containing all the necessary configuration to serve, test and build Storybook. Make sure to **use the framework-specific generators** if your project is using Angular, React, Next.js, Vue, Nuxt, or React Native: [`@nx/angular:storybook-configuration`](/docs/technologies/angular/generators#storybook-configuration), [`@nx/react:storybook-configuration`](/docs/technologies/react/generators#storybook-configuration), [`@nx/vue:storybook-configuration`](/docs/technologies/vue/generators#storybook-configuration) as shown above. ### Running Storybook Serve Storybook using this command: ```shell nx run project-name:storybook ``` or ```shell nx storybook project-name ``` ### Building Storybook Build Storybook using this command: ```shell nx run project-name:build-storybook ``` or ```shell nx build-storybook project-name ``` ### Testing Storybook Run all the interaction tests in your stories using this command: ```shell nx run project-name:test-storybook ``` or ```shell nx test-storybook project-name ``` On the test runner path, this target runs `@storybook/test-runner` against a Storybook that's already serving. Start `nx storybook project-name` first, or point the runner at a published URL. Projects that register the Vitest addon run their stories through Vitest instead, with no serving Storybook. For the Vitest addon setup, see [Setting up Storybook interaction tests](/docs/kb/storybook-interaction-tests). ### Anatomy of the Storybook setup When running the Nx Storybook generator, it configures the Nx workspace to run Storybook and creates a project-specific Storybook configuration. The project-specific Storybook configuration is pretty much similar to what you would have for a non-Nx setup of Storybook. There's a `.storybook` folder within the project root folder. ```text frame="none" / ├── .storybook/ │ ├── main.ts │ └── preview.ts ├── src/ ├── README.md ├── tsconfig.json ├── tsconfig.storybook.json └── etc... ``` ### Using addons To register a [Storybook addon](https://storybook.js.org/addons/) for all Storybook instances in your workspace: 1. In your project's `.storybook/main.ts` file, in the `addons` array of the `module.exports` object, add the new addon: ```typescript frame="none" // /.storybook/main.ts import type { StorybookConfig } from '@storybook/react-vite'; const config: StorybookConfig = { ... addons: ['some-storybook-addon', ...], ... }; export default config; ``` 2. If a decorator is required, in each project's `/.storybook/preview.ts`, you can export an array called `decorators`. ```typescript frame="none" // /.storybook/preview.ts import someDecorator from 'some-storybook-addon'; export const decorators = [someDecorator]; ``` ### Setting up documentation To set up documentation, you can use [Storybook Autodocs](https://storybook.js.org/docs/react/writing-docs/autodocs). For Angular, [you can use `compodoc`](/docs/kb/angular-storybook-compodoc) to infer `argTypes`. You can read more about `argTypes` in the [official Storybook `argTypes` documentation](https://storybook.js.org/docs/angular/api/argtypes#automatic-argtype-inference). You can read more about how to best set up documentation using Storybook for your project in the [official Storybook documentation](https://storybook.js.org/docs/react/writing-docs/introduction). ## More documentation You can find dedicated information for React and Angular: - [Set up Storybook for Angular Projects](/docs/kb/overview-angular) - [Set up Storybook for React Projects](/docs/kb/storybook-for-react) - [Set up Storybook for Vue Projects](/docs/kb/overview-vue) You can find all Storybook-related Nx documentation in the [Storybook guides section](/docs/technologies/test-tools/storybook/guides). For more on using Storybook, see the [official Storybook documentation](https://storybook.js.org/docs/basics/introduction/). ### Migration scenarios Here's more information on common migration scenarios for Storybook with Nx. For Storybook specific migrations that are not automatically handled by Nx please refer to the [official Storybook page](https://storybook.js.org/) - [Upgrading Storybook guide](/docs/kb/upgrading-storybook) - [Storybook 10 migration generator](/docs/technologies/test-tools/storybook/generators#migrate-10) ## Set up CI In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/storybook Migrations For an overview of the plugin and setup instructions, see the [@nx/storybook introduction](/docs/technologies/test-tools/storybook/introduction). The @nx/storybook plugin provides various migrations to help you migrate to newer versions of storybook projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.2.x ### 23.2.0-test-runner-package-updates **Version**: 23.2.0-beta.8 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@storybook/test-runner` | `^0.24.0` | Updated only ## 23.1.x ### 23.1.0-package-updates **Version**: 23.1.0-rc.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@storybook/angular` | `^10.5.0` | Updated only | `@storybook/react` | `^10.5.0` | Updated only | `storybook` | `^10.5.0` | Added if not installed | `@storybook/addon-onboarding` | `^10.5.0` | Updated only | `@storybook/addon-themes` | `^10.5.0` | Updated only | `@storybook/builder-webpack5` | `^10.5.0` | Updated only | `@storybook/core-webpack` | `^10.5.0` | Updated only | `@storybook/html` | `^10.5.0` | Updated only | `@storybook/html-vite` | `^10.5.0` | Updated only | `@storybook/nextjs` | `^10.5.0` | Updated only | `@storybook/preact` | `^10.5.0` | Updated only | `@storybook/preact-vite` | `^10.5.0` | Updated only | `@storybook/react-vite` | `^10.5.0` | Updated only | `@storybook/react-webpack5` | `^10.5.0` | Updated only | `@storybook/server` | `^10.5.0` | Updated only | `@storybook/server-webpack5` | `^10.5.0` | Updated only | `@storybook/svelte` | `^10.5.0` | Updated only | `@storybook/svelte-vite` | `^10.5.0` | Updated only | `@storybook/sveltekit` | `^10.5.0` | Updated only | `@storybook/vue3` | `^10.5.0` | Updated only | `@storybook/vue3-vite` | `^10.5.0` | Updated only | `@storybook/web-components` | `^10.5.0` | Updated only | `@storybook/web-components-vite` | `^10.5.0` | Updated only ## 23.0.x ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/storybook/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/storybook` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/storybook/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/storybook/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/storybook/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/storybook/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ## 22.2.x ### 22.2.0-package-updates **Version**: 22.2.0-beta.1 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@storybook/angular` | `^10.1.0` | Updated only | `@storybook/react` | `^10.1.0` | Updated only | `storybook` | `^10.1.0` | Added if not installed | `@storybook/addon-onboarding` | `^10.1.0` | Updated only | `@storybook/addon-themes` | `^10.1.0` | Updated only | `@storybook/builder-webpack5` | `^10.1.0` | Updated only | `@storybook/core-webpack` | `^10.1.0` | Updated only | `@storybook/html` | `^10.1.0` | Updated only | `@storybook/html-vite` | `^10.1.0` | Updated only | `@storybook/nextjs` | `^10.1.0` | Updated only | `@storybook/preact` | `^10.1.0` | Updated only | `@storybook/preact-vite` | `^10.1.0` | Updated only | `@storybook/react-vite` | `^10.1.0` | Updated only | `@storybook/react-webpack5` | `^10.1.0` | Updated only | `@storybook/server` | `^10.1.0` | Updated only | `@storybook/server-webpack5` | `^10.1.0` | Updated only | `@storybook/svelte` | `^10.1.0` | Updated only | `@storybook/svelte-vite` | `^10.1.0` | Updated only | `@storybook/sveltekit` | `^10.1.0` | Updated only | `@storybook/vue3` | `^10.1.0` | Updated only | `@storybook/vue3-vite` | `^10.1.0` | Updated only | `@storybook/web-components` | `^10.1.0` | Updated only | `@storybook/web-components-vite` | `^10.1.0` | Updated only ## 22.1.x ### `update-22-1-0-migrate-storybook-v10` **Version**: 22.1.0-beta.8 Update workspace to use Storybook v10 #### Requires | Name | Version | |------|---------| `storybook` | `>=10.0.0` | ### 21.1.0-package-updates **Version**: 22.1.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@storybook/angular` | `^9.0.5` | Updated only | `@storybook/react` | `^9.0.5` | Updated only | `storybook` | `^9.0.5` | Added if not installed | `@storybook/addon-onboarding` | `^9.0.5` | Updated only | `@storybook/addon-themes` | `^9.0.5` | Updated only | `@storybook/builder-webpack5` | `^9.0.5` | Updated only | `@storybook/core-webpack` | `^9.0.5` | Updated only | `@storybook/html` | `^9.0.5` | Updated only | `@storybook/html-vite` | `^9.0.5` | Updated only | `@storybook/nextjs` | `^9.0.5` | Updated only | `@storybook/preact` | `^9.0.5` | Updated only | `@storybook/preact-vite` | `^9.0.5` | Updated only | `@storybook/react-vite` | `^9.0.5` | Updated only | `@storybook/react-webpack5` | `^9.0.5` | Updated only | `@storybook/server` | `^9.0.5` | Updated only | `@storybook/server-webpack5` | `^9.0.5` | Updated only | `@storybook/svelte` | `^9.0.5` | Updated only | `@storybook/svelte-vite` | `^9.0.5` | Updated only | `@storybook/sveltekit` | `^9.0.5` | Updated only | `@storybook/vue3` | `^9.0.5` | Updated only | `@storybook/vue3-vite` | `^9.0.5` | Updated only | `@storybook/web-components` | `^9.0.5` | Updated only | `@storybook/web-components-vite` | `^9.0.5` | Updated only ### 22.1.0-package-updates **Version**: 22.1.0-beta.8 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@storybook/angular` | `^10.0.0` | Updated only | `@storybook/react` | `^10.0.0` | Updated only | `storybook` | `^10.0.0` | Added if not installed | `@storybook/addon-onboarding` | `^10.0.0` | Updated only | `@storybook/addon-themes` | `^10.0.0` | Updated only | `@storybook/builder-webpack5` | `^10.0.0` | Updated only | `@storybook/core-webpack` | `^10.0.0` | Updated only | `@storybook/html` | `^10.0.0` | Updated only | `@storybook/html-vite` | `^10.0.0` | Updated only | `@storybook/nextjs` | `^10.0.0` | Updated only | `@storybook/preact` | `^10.0.0` | Updated only | `@storybook/preact-vite` | `^10.0.0` | Updated only | `@storybook/react-vite` | `^10.0.0` | Updated only | `@storybook/react-webpack5` | `^10.0.0` | Updated only | `@storybook/server` | `^10.0.0` | Updated only | `@storybook/server-webpack5` | `^10.0.0` | Updated only | `@storybook/svelte` | `^10.0.0` | Updated only | `@storybook/svelte-vite` | `^10.0.0` | Updated only | `@storybook/sveltekit` | `^10.0.0` | Updated only | `@storybook/vue3` | `^10.0.0` | Updated only | `@storybook/vue3-vite` | `^10.0.0` | Updated only | `@storybook/web-components` | `^10.0.0` | Updated only | `@storybook/web-components-vite` | `^10.0.0` | Updated only ## 21.2.x ### `update-21-2-0-migrate-storybook-v9` **Version**: 21.2.0-beta.3 Update workspace to use Storybook v9 #### Requires | Name | Version | |------|---------| `storybook` | `>=9.0.0 <10.0.0` | ### `update-21-2-0-remove-addon-dependencies` **Version**: 21.2.0-beta.3 Remove deprecated Storybook addon dependencies #### Requires | Name | Version | |------|---------| `storybook` | `>=9.0.0 <10.0.0` | --- ## Vitest Get started with [Nx with Vitest](/docs/technologies/test-tools/vitest/introduction), or browse the topics below. {% index_page_cards path="technologies/test-tools/vitest" /%} --- ## @nx/vitest Executors The @nx/vitest plugin provides various executors to help you create and configure vitest projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/vitest introduction](/docs/technologies/test-tools/vitest/introduction). ### `test` Test using Vitest. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `configFile` | string | The path to the local vitest config, relative to the workspace root. | | | `mode` | string | Vite mode for loading configuration. | | | `reportsDirectory` | string | Directory to write coverage report to. | | | `runMode` | string | Vitest execution mode. | `"test"` | | `testFiles` | array | | | | `watch` | boolean | Watch files for changes and rerun tests related to changed files. | | --- ## @nx/vitest Generators The @nx/vitest plugin provides various generators to help you create and configure vitest projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/vitest introduction](/docs/technologies/test-tools/vitest/introduction). ## `configuration` Generate a Vitest setup for a project. **Usage:** ```bash nx generate @nx/vitest:configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string [**required**] | The name of the project to test. | | | `--compiler` | string | The compiler to use | `"babel"` | | `--coverageProvider` | string | Coverage provider to use. | `"v8"` | | `--inSourceTests` | boolean | Do not generate separate spec files and set up in-source testing. | `false` | | `--passWithNoTests` | boolean | Exit with a zero status when the project has no test files. | `false` | | `--runtimeTsconfigFileName` | string | The name of the project's tsconfig file that includes the runtime source files. If not provided, it will default to `tsconfig.lib.json` for libraries and `tsconfig.app.json` for applications. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--skipViteConfig` | boolean | Skip generating a vite config file. | `false` | | `--testEnvironment` | string | The vitest environment to use. See https://vitest.dev/config/#environment. | | | `--testTarget` | string | The test target of the project to be transformed to use the @nx/vitest:test executor. | | | `--uiFramework` | string | UI framework to use with vitest. | | | `--zoneless` | boolean | Whether the Angular project is zoneless. When not provided, it is auto-detected from the project configuration. | | ## `convert-to-inferred` Convert existing vitest project(s) using `@nx/vitest:test` executors to use the `@nx/vitest` inferred plugin. Defaults to migrating all projects. Pass '--project' to migrate only one target. **Usage:** ```bash nx generate @nx/vitest:convert-to-inferred [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string | The project to convert from using the `@nx/vitest:test` executor to use the `@nx/vitest` inferred plugin. | | | `--skipFormat` | boolean | Whether to format files at the end of the migration. | `false` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/vitest: --help ``` --- ## Guides {% index_page_cards path="technologies/test-tools/vitest/guides" /%} --- ## Nx with Vitest [Vitest](https://vitest.dev) is a fast test runner built on Vite. The `@nx/vitest` plugin adds [inferred Vitest targets](#configuration), a [project configuration generator](#add-vitest-to-a-project), and CI-friendly [test splitting](#ci-considerations). You can use Vitest with Nx without the plugin and still get [task caching](/docs/features/cache-task-results), [task orchestration](/docs/features/run-tasks), and the [project graph](/docs/features/explore-graph). ## Requirements The `@nx/vitest` plugin supports the following package versions. | Package | Supported Versions | | -------- | ------------------ | | `vitest` | ^3.0.0 \|\| ^4.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setup ### Add to an existing workspace {% aside type="note" title="Keep Nx Package Versions In Sync" %} Install the `@nx/vitest` version that matches the version of `nx` in your workspace. If the version numbers get out of sync, you can encounter difficult to debug errors. Use the [version sync recipe](/docs/kb/keep-nx-versions-in-sync) to fix mismatches. Using `nx add` helps ensure the correct version is installed and configured. {% /aside %} ```shell nx add @nx/vitest ``` ### Verify inferred tasks Nx infers Vitest tasks from your Vitest or Vite config files. To confirm which targets were created, open the project details view in Nx Console or run: ```shell nx show project my-app ``` ### Add Vitest to a project Use the configuration generator to set up a project with Vitest: ```shell nx g @nx/vitest:configuration --project=my-app ``` The generator accepts framework-specific options. Pass the right flags for your project type: {% tabs syncKey="framework" %} {% tabitem label="React" %} ```shell nx g @nx/vitest:configuration --project=my-react-lib --uiFramework=react ``` Sets up Vitest with React JSX support and `jsdom` test environment. {% /tabitem %} {% tabitem label="Vue" %} ```shell nx g @nx/vitest:configuration --project=my-vue-lib --uiFramework=vue ``` Sets up Vitest with Vue SFC support and `jsdom` test environment. {% /tabitem %} {% tabitem label="Angular" %} ```shell nx g @nx/vitest:configuration --project=my-angular-lib --uiFramework=angular ``` Configures Vitest for Angular with the appropriate test setup. {% /tabitem %} {% tabitem label="Node / TypeScript" %} ```shell nx g @nx/vitest:configuration --project=my-node-lib --testEnvironment=node ``` Uses the `node` test environment instead of the default `jsdom`. {% /tabitem %} {% /tabs %} See the full [configuration generator reference](/docs/technologies/test-tools/vitest/generators#configuration) for all options. {% aside type="tip" title="Framework generators handle this automatically" %} When generating a new project with a framework generator, you can specify Vitest as the test runner and Nx passes the correct options automatically: ```shell nx g @nx/react:lib libs/my-react-lib --unitTestRunner=vitest nx g @nx/vue:lib libs/my-vue-lib --unitTestRunner=vitest ``` {% /aside %} ## Local development Run Vitest through Nx so caching and the project graph work together. ```shell nx test my-app ``` ```shell nx test my-app --watch ``` ```shell nx test my-app -- MyComponent.spec.ts ``` ```shell nx test my-app --ui ``` ```shell nx test my-app --coverage ``` {% aside type="note" title="Replace the project name" %} Replace `my-app` with the name of your project and the test file pattern with the file you want to run. {% /aside %} ## Configuration ### How tasks are inferred The `@nx/vitest` plugin creates a target for any project that has a Vitest or Vite config file with test settings. The plugin looks for: - `vitest.config.js` - `vitest.config.ts` - `vitest.config.mjs` - `vitest.config.mts` - `vitest.config.cjs` - `vitest.config.cts` - `vite.config.js` (with `test` configuration) - `vite.config.ts` (with `test` configuration) - `vite.config.mjs` (with `test` configuration) - `vite.config.mts` (with `test` configuration) - `vite.config.cjs` (with `test` configuration) - `vite.config.cts` (with `test` configuration) The configuration directory must also contain a `package.json` or `project.json`. A root Vitest workspace configuration that only orchestrates child projects is not registered as its own project. To view inferred tasks, open the [project details view](/docs/concepts/mental-model#inferred-tasks) in Nx Console or run `nx show project my-app`. ### Plugin options Configure the plugin in `nx.json`: ```json // nx.json { "plugins": [ { "plugin": "@nx/vitest", "options": { "testTargetName": "test", "ciTargetName": "test-ci", "ciGroupName": "Unit Tests (CI)", "testMode": "watch" } } ] } ``` | Option | Type | Default | Description | | ------------------- | ------------------ | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `testTargetName` | string | `test` | Name of the test task. | | `ciTargetName` | string | none | Creates a CI-only task used for atomized runs. | | `ciGroupName` | string | derived from `ciTargetName` | Display name for the atomized CI group. | | `testMode` | `watch` or `run` | `watch` | Determines whether Nx runs `vitest` (watch) or `vitest run` (single run). | | `discoverTestFiles` | `glob` or `vitest` | `glob` | How atomized test files are discovered. `glob` mirrors Vitest's resolution without booting Vitest per project, which is faster during graph creation. Set to `vitest` to always enumerate through Vitest. | When `discoverTestFiles` is `glob`, configs that a glob cannot reproduce faithfully still boot Vitest automatically: `test.projects` or `test.workspace` (inline or an auto-loaded `vitest.workspace.*` or `vitest.projects.*` file), plugins with a `configureVitest` hook, `test.changed` or `test.related`, and enabled browser `instances` that set their own `include`, `exclude`, `includeSource`, or `dir`. Patterns the glob reads differently than Vitest trigger the same fallback: an absolute path, a trailing `/`, or an `!(...)` extglob, each optionally negated, in `include`, `exclude`, `includeSource`, or `typecheck`'s include or exclude. The `glob` path reads the Nx workspace file index, so it never enumerates specs ignored by `.gitignore` or `.nxignore`, even ones Vitest itself would run. Set `discoverTestFiles` to `vitest` if you need those specs atomized. There is no single "disable" flag for inference. Use `include` and `exclude` to scope which projects each plugin instance applies to. The test task is cached, with outputs based on your Vitest coverage configuration. Custom reporter output files are not inferred. Set `ciTargetName` to enable the [Atomizer](/docs/features/ci-features/split-e2e-tasks). When coverage is enabled, each atomized task writes its report to a directory under the configured `reportsDirectory` that mirrors the spec file's path. A project reporting to `coverage/apps/my-app` gets `coverage/apps/my-app/src/a.spec.ts/coverage-final.json` for the `src/a.spec.ts` task, so a tool configured with a fixed report path needs a glob instead. Nx inserts the project root before the spec path for a `reportsDirectory` outside the project, unless the directory already ends with it. `../../reports` for `apps/my-app` gives `reports/apps/my-app/src/a.spec.ts`. Nx caches only outputs inside the workspace, so atomized tasks and their CI parent run uncached when `reportsDirectory` resolves outside it. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter for these tasks must use the exact identifier `@nx/vitest`. ### Configure unit and e2e separately If you use Vitest for unit and e2e tests, configure the plugin twice with different include and exclude patterns. ```json // nx.json { "plugins": [ { "plugin": "@nx/vitest", "exclude": ["e2e/**/*"], "options": { "testTargetName": "test", "testMode": "watch" } }, { "plugin": "@nx/vitest", "include": ["e2e/**/*"], "options": { "testTargetName": "e2e", "ciTargetName": "e2e-ci", "ciGroupName": "E2E Tests (CI)", "testMode": "run" } } ] } ``` ## Set up CI In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). ## What's next {% cardgrid %} {% linkcard title="Vitest Config Reference" description="Learn about Vitest configuration options and defaults." href="https://vitest.dev/config/" /%} {% linkcard title="Migrate from @nx/vite" description="Move Vitest tasks from the Vite plugin to @nx/vitest." href="/docs/kb/migrating-from-nx-vite" /%} {% linkcard title="Split E2E Tasks" description="Distribute E2E tests by file with atomized tasks." href="/docs/features/ci-features/split-e2e-tasks" /%} {% linkcard title="Feature-Based Testing" description="Organize tests by feature for better caching and targeted CI." href="/docs/kb/feature-based-testing" /%} {% linkcard title="Vitest Generators Reference" description="Reference for @nx/vitest generators." href="/docs/technologies/test-tools/vitest/generators" /%} {% linkcard title="Vitest Executors Reference" description="Reference for @nx/vitest executors." href="/docs/technologies/test-tools/vitest/executors" /%} {% /cardgrid %} --- ## @nx/vitest Migrations For an overview of the plugin and setup instructions, see the [@nx/vitest introduction](/docs/technologies/test-tools/vitest/introduction). The @nx/vitest plugin provides various migrations to help you migrate to newer versions of vitest projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.2.x ### `update-23-2-0-use-import-meta-dirname` **Version**: 23.2.0-beta.6 Replace `__dirname` with `import.meta.dirname` in `vite.config.mts`/`vitest.config.mts` files so they work with Vite's `configLoader: 'native'`. #### Replace `__dirname` with `import.meta.dirname` in Vite config files Vite 8 warns when a config uses features that its `configLoader: 'native'` mode cannot support, and `__dirname` is one of them: ``` (!) Your Vite config uses features that are unsupported by `configLoader: 'native'`, which is planned to become the default in a future major version of Vite: - `__dirname` (packages/utils/vitest.config.mts:4:9). Use `import.meta.dirname` instead ``` This migration rewrites `__dirname` to `import.meta.dirname` in every `vite.config.mts`, `vite.config.mjs`, `vitest.config.mts`, and `vitest.config.mjs` file in your workspace. #### Sample Code Changes ##### Before ```ts export default defineConfig(() => ({ root: __dirname, })); ``` ##### After ```ts export default defineConfig(() => ({ root: import.meta.dirname, })); ``` #### What is not rewritten `.ts` and `.js` configs are left alone, since those extensions can still be loaded as CommonJS, where `import.meta` is a syntax error. Configs that declare their own `__dirname` (usually `const __dirname = path.dirname(fileURLToPath(import.meta.url))`) are also left alone - that idiom already works under the native config loader. No migration renames an existing config to `.mts`, because other tooling may reference it by path. So a workspace whose config is `.ts` keeps the companion warning about ESM syntax in a CommonJS-loaded file. Rename it yourself, or set `"type": "module"` in the closest `package.json`, to clear that one. Newly generated configs use `.mts` and are unaffected. One generated case does put `import.meta.dirname` in a `.ts` config: `@nx/nuxt` falls back to `.ts` on workspaces still using eslintrc, because the legacy `@nuxt/eslint-config` cannot parse `.mts`. Vite's default config loader bundles that file, so it works today, but the file is not loadable under `configLoader: 'native'` - its own `import` statements already make it so. ## 23.1.x ### 23.1.0-analog-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@analogjs/vite-plugin-angular` | `~2.6.0` | Updated only | `@analogjs/vitest-angular` | `~2.6.0` | Updated only ## 23.0.x ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/vitest` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/vitest` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/vitestst` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/vitestst'; ``` ##### After ```ts import { createNodes } from '@nx/vitestst'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/vitestst` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ### 23.0.1-happy-dom-package-updates **Version**: 23.0.1 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `happy-dom` | `^20.10.4` | Updated only ## 22.6.x ### `update-22-6-0-prefix-reports-directory` **Version**: 22.6.0-beta.11 Prefix reportsDirectory with {projectRoot} to maintain correct resolution after workspace-root-relative behavior change. #### Prefix `reportsDirectory` with `{projectRoot}` The `reportsDirectory` option for `@nx/vitest:test` (and `@nx/vite:test`) is now resolved relative to the workspace root instead of the project root. This migration prepends `{projectRoot}/` to existing `reportsDirectory` values so the resolved path remains the same. #### Sample Code Changes ##### Before ```json title="project.json" { "targets": { "test": { "executor": "@nx/vitest:test", "options": { "reportsDirectory": "coverage/libs/my-lib" } } } } ``` ##### After ```json title="project.json" {6} { "targets": { "test": { "executor": "@nx/vitest:test", "options": { "reportsDirectory": "{projectRoot}/coverage/libs/my-lib" } } } } ``` ## 22.3.x ### `update-22-3-2` **Version**: 22.3.2-beta.0 Create AI Instructions to help migrate users workspaces past breaking changes for Vitest 4. #### Requires | Name | Version | |------|---------| `@angular/build` | `>=21.0.0` | `vitest` | `>=4.0.0` | ### 22.3.2-package-updates **Version**: 22.3.2-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `vitest` | `^4.0.8` | Updated only | `@vitest/coverage-v8` | `^4.0.8` | Updated only | `@vitest/coverage-istanbul` | `^4.0.8` | Updated only | `@vitest/ui` | `^4.0.8` | Updated only | `jsdom` | `^27.1.0` | Updated only ### 22.3.2-analog-package-updates **Version**: 22.3.2-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@analogjs/vite-plugin-angular` | `~2.2.0` | Updated only | `@analogjs/vitest-angular` | `~2.2.0` | Updated only ## 22.2.x ### 22.2.0-analog-package-updates **Version**: 22.2.0-beta.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@analogjs/vite-plugin-angular` | `~2.1.2` | Updated only | `@analogjs/vitest-angular` | `~2.1.2` | Updated only ## 22.1.x ### `update-22-1-0` **Version**: 22.1.0-beta.8 Create AI Instructions to help migrate users workspaces past breaking changes for Vitest 4. #### Requires | Name | Version | |------|---------| `vitest` | `>=4.0.0` | ### 22.1.0-package-updates **Version**: 22.1.0-beta.8 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `vitest` | `^4.0.0` | Updated only | `@vitest/coverage-v8` | `^4.0.0` | Updated only | `@vitest/coverage-istanbul` | `^4.0.0` | Updated only | `@vitest/ui` | `^4.0.0` | Updated only --- ## TypeScript Get started with [Nx with TypeScript](/docs/technologies/typescript/introduction), or browse the topics below. {% index_page_cards path="technologies/typescript" /%} --- ## @nx/js Executors The @nx/js plugin provides various executors to help you create and configure js projects within your Nx workspace. Below is a complete reference for all available executors and their options. For an overview of the plugin and setup instructions, see the [@nx/js introduction](/docs/technologies/typescript/introduction). ### `copy-workspace-modules` Copies Workspace Modules into the output directory after a build to prepare it for use with Docker or alternatives. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `buildTarget` | string [**required**] | The build target that produces the output directory to transform. | `"build"` | | `outputPath` | string | The output path to transform. Usually inferred from the outputs of the buildTarget. | | ### `node` Execute Nodejs applications. The `@nx/js:node` executor runs the output of a build target. For example, an application uses esbuild ([`@nx/esbuild:esbuild`](/nx-api/esbuild/executors/esbuild)) to output the bundle to `dist/my-app` folder, which can then be executed by `@nx/js:node`. `project.json`: ```json "my-app": { "targets": { "serve": { "executor": "@nx/js:node", "options": { "buildTarget": "my-app:build" } }, "build": { "executor": "@nx/esbuild:esbuild", "options": { "main": "my-app/src/main.ts", "output": ["dist/my-app"], //... } }, } } ``` ```bash npx nx serve my-app ``` ### Examples ###### Pass extra Node CLI arguments Using `runtimeArgs`, you can pass arguments to the underlying `node` command. For example, if you want to set [`--no-warnings`](https://nodejs.org/api/cli.html#--no-warnings) to silence all Node warnings, then add the following to the `project.json` file. ```json "my-app": { "targets": { "serve": { "executor": "@nx/js:node", "options": { "runtimeArgs": ["--no-warnings"], //... }, }, } } ``` ###### Run all task dependencies If your application build depends on other tasks, and you want those tasks to also be executed, then set the `runBuildTargetDependencies` to `true`. For example, a library may have a task to generate GraphQL schemas, which is consume by the application. In this case, you want to run the generate task before building and running the application. This option is also useful when the build consumes a library from its output, not its source. For example, if an executor that supports `buildLibsFromSource` option has it set to `false` (e.g. [`@nx/webpack:webpack`](/nx-api/webpack/executors/webpack)). Note that this option will increase the build time, so use it only when necessary. ```json "my-app": { "targets": { "serve": { "executor": "@nx/js:node", "options": { "runBuildTargetDependencies": true, //... }, }, } } ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `buildTarget` | string [**required**] | The target to run to build you the app. | | | `args` | array | Extra args when starting the app. | `[]` | | `buildTargetOptions` | object | Additional options to pass into the build target. | `{}` | | `debounce` | number | Delay in milliseconds to wait before restarting. Useful to batch multiple file changes events together. Set to zero (0) to disable. | `500` | | `host` | string | The host to inspect the process on. | `"localhost"` | | `inspect` | string | Ensures the app is starting with debugging. | `"inspect"` | | `port` | number | The port to inspect the process on. Setting port to 0 will assign random free ports to all forked processes. | `9229` | | `runBuildTargetDependencies` | boolean | Whether to run dependencies before running the build. Set this to true if the project does not build libraries from source (e.g. 'buildLibsFromSource: false'). | `false` | | `runtimeArgs` | array | Extra args passed to the node process. | `[]` | | `waitUntilTargets` | array | The targets to run before starting the node app. Listed in the form :. The main target will run once all listed targets have output something to the console. | `[]` | | `watch` | boolean | Enable re-building when files change. | `true` | ### `prune-lockfile` Creates a pruned lockfile based on the project dependencies and places it into the output directory. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `buildTarget` | string [**required**] | The build target that produces the output directory to place the pruned lockfile. | `"build"` | | `outputPath` | string | The output path to place the pruned lockfile. Usually inferred from the outputs of the buildTarget. | | ### `swc` Builds using SWC. ### Examples ###### Custom swcrc `@nx/js:swc` can compile your code with a custom `.swcrc` ```json title="libs/ts-lib/project.json" { "build": { "executor": "@nx/js:swc", "options": { "outputPath": "dist/libs/ts-lib", "main": "libs/ts-lib/src/index.ts", "tsConfig": "libs/ts-lib/tsconfig.lib.json", "assets": ["libs/ts-lib/*.md"], "swcrc": "libs/ts-lib/.dev.swcrc" }, "configurations": { "production": { "swcrc": "libs/ts-lib/.prod.swcrc" } } } } ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `main` | string [**required**] | The name of the main entry-point file. | | | `outputPath` | string [**required**] | The output path of the generated files. | | | `tsConfig` | string [**required**] | The path to the Typescript configuration file. | | | `additionalEntryPoints` | array | Additional entry-points to add to exports field in the package.json file. | | | `assets` | array | List of static assets. | `[]` | | `clean` | boolean | Remove previous output before build. | `true` | | `generateExportsField` | boolean | Update the output package.json file's 'exports' field. This field is used by Node and bundles. | `false` | | `generateLockfile` | boolean | Generate a lockfile (e.g. package-lock.json) that matches the workspace lockfile to ensure package versions match. | `false` | | `includeIgnoredAssetFiles` | boolean | Include files that are ignored by .gitignore and .nxignore when copying assets. WARNING: Ignored files are not automatically considered when calculating the task hash. To ensure Nx tracks these files for caching, add them to your target's inputs using 'dependentTasksOutputs' or 'runtime' configuration. | `false` | | `skipTypeCheck` | boolean | Whether to skip TypeScript type checking. | `false` | | `stripLeadingPaths` | boolean | Remove leading directory from output (e.g. src). See: https://swc.rs/docs/usage/cli#--strip-leading-paths | `false` | | `swcExclude` | array | List of SWC Glob/Regex to be excluded from compilation (https://swc.rs/docs/configuration/compilation#exclude). | `["./src/**/.*.spec.ts$","./**/.*.spec.ts$","./src/**/jest-setup.ts$","./**/jest-setup.ts$","./**/.*.js$"]` | | `swcrc` | string | The path to the SWC configuration file. Default: .swcrc | | | `watch` | boolean | Enable re-building when files change. | `false` | ### `tsc` Builds using TypeScript. ### Examples ###### Using TypeScript Transformer Plugins `@nx/js:tsc` can run the [TypeScript Transformers](https://github.com/madou/typescript-transformer-handbook) by using the `transformers` option. ```json title="libs/ts-lib/project.json" { "build": { "executor": "@nx/js:tsc", "options": { "outputPath": "dist/libs/ts-lib", "main": "libs/ts-lib/src/index.ts", "tsConfig": "libs/ts-lib/tsconfig.lib.json", "assets": ["libs/ts-lib/*.md"], "transformers": [ "@nestjs/swagger/plugin", { "name": "@automapper/classes/transformer-plugin", "options": {} } ] } } } ``` ###### Batch mode execution :::tip[Available since Nx 16.6.0] The `@nx/js:tsc` batch implementation was introduced in Nx **16.6.0**. ::: The `@nx/js:tsc` executor supports running multiple tasks in a single process. When running in batch mode, the executor uses the [TypeScript APIs for incremental builds](https://www.typescriptlang.org/docs/handbook/project-references.html#build-mode-for-typescript). This results in a much faster build time when compared to the default implementation (the bigger the task graph to run, the more the performance improvements). :::danger[Experimental feature] Executing tasks in batch mode is an experimental feature. ::: :::tip[Requirements] Building a project with the `@nx/js:tsc` executor in batch mode requires all dependent projects (excluding implicit dependencies) to be buildable and built using the `@nx/js:tsc` executor. ::: To run your builds using the batch implementation, pass in `--batch` flag: ```shell nx build ts-lib --batch ``` For optimal performance, you could set the `clean` option to `false`. Otherwise, the executor cleans the output folder before running the build, which results in the loss of the [`.tsbuildinfo` file](https://www.typescriptlang.org/tsconfig/#tsBuildInfoFile) and, consequently, the loss of important optimizations performed by TypeScript. This is not a requirement. Even if the `clean` option is not set to `false` there are other important optimizations that are performed by the batch implementation. ```json title="libs/ts-lib/project.json" { "build": { "executor": "@nx/js:tsc", "options": { "outputPath": "dist/libs/ts-lib", "main": "libs/ts-lib/src/index.ts", "tsConfig": "libs/ts-lib/tsconfig.lib.json", "assets": ["libs/ts-lib/*.md"], "clean": false } } } ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `main` | string [**required**] | The name of the main entry-point file. | | | `outputPath` | string [**required**] | The output path of the generated files. | | | `tsConfig` | string [**required**] | The path to the Typescript configuration file. | | | `additionalEntryPoints` | array | Additional entry-points to add to exports field in the package.json file. Ignored when `generatePackageJson` is set to `false`. | | | `assets` | array | List of static assets. | `[]` | | `clean` | boolean | Remove previous output before build. | `true` | | `generateExportsField` | boolean | Update the output package.json file's 'exports' field. This field is used by Node and bundlers. Ignored when `generatePackageJson` is set to `false`. | `false` | | `generateLockfile` | boolean | Generate a lockfile (e.g. package-lock.json) that matches the workspace lockfile to ensure package versions match. Ignored when `generatePackageJson` is set to `false`. | `false` | | `generatePackageJson` | boolean | Generate package.json file in the output folder. | `true` | | `includeIgnoredAssetFiles` | boolean | Include files that are ignored by .gitignore and .nxignore when copying assets. WARNING: Ignored files are not automatically considered when calculating the task hash. To ensure Nx tracks these files for caching, add them to your target's inputs using 'dependentTasksOutputs' or 'runtime' configuration. | `false` | | `outputFileName` | string | The path to the main file relative to the outputPath | | | `rootDir` | string | Sets the rootDir for TypeScript compilation. When not defined, it uses the root of project. | | | `transformers` | array | List of TypeScript Transformer Plugins. | `[]` | | `watch` | boolean | Enable re-building when files change. | `false` | ### `verdaccio` Start a local registry with Verdaccio. #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `port` | number [**required**] | Port of local registry that Verdaccio should listen to | | | `clear` | boolean | Clear local registry storage before starting Verdaccio | `true` | | `config` | string | Path to the custom Verdaccio config file | | | `listenAddress` | string | Listen address that Verdaccio should listen to | `"localhost"` | | `location` | string | Location option for npm config | `"user"` | | `scopes` | array | Scopes to be added to the Verdaccio config | | | `storage` | string | Path to the custom storage directory for Verdaccio | | --- ## @nx/js Generators The @nx/js plugin provides various generators to help you create and configure js projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/js introduction](/docs/technologies/typescript/introduction). ## `convert-to-swc` Convert a TSC library to SWC. **Usage:** ```bash nx generate @nx/js:convert-to-swc [options] ``` **Aliases:** `swc` **Arguments:** ```bash nx generate @nx/js:convert-to-swc [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--targets` | array | List of targets to convert. | `["build"]` | ## `library` Create a TypeScript Library. The `@nx/js:lib` generator will generate a library for you, and it will configure it according to the options you provide. ```bash npx nx g @nx/js:lib libs/mylib ``` By default, the library that is generated when you use this executor without passing any options, like the example above, will be a buildable library, using the `@nx/js:tsc` executor as a builder. You may configure the tools you want to use to build your library, or bundle it too, by passing the `--bundler` flag. The `--bundler` flag controls the compiler and/or the bundler that will be used to build your library. If you choose `tsc` or `swc`, the result will be a buildable library using either `tsc` or `swc` as the compiler. If you choose `rollup` or `vite`, the result will be a buildable library using `rollup` or `vite` as the bundler. In the case of `rollup`, it will default to the `tsc` compiler. If you choose `esbuild`, you may use the [`esbuildOptions` property](https://esbuild.github.io/api/) in your `project.json` under the `build` target options to specify whether you wish to bundle your library or not. ### Examples ###### Buildable with default compiler (tsc) Generate a buildable library using the `@nx/js:tsc` executor. This uses `tsc` as the compiler. ```bash npx nx g @nx/js:lib libs/mylib ``` ###### Buildable with SWC compiler Generate a buildable library using [SWC](https://swc.rs) as the compiler. This will use the `@nx/js:swc` executor. ```bash npx nx g @nx/js:lib libs/mylib --bundler=swc ``` ###### Buildable with tsc Generate a buildable library using tsc as the compiler. This will use the `@nx/js:tsc` executor. ```bash npx nx g @nx/js:lib libs/mylib --bundler=tsc ``` ###### Buildable, with Rollup as a bundler Generate a buildable library using [Rollup](https://rollupjs.org) as the bundler. This will use the `@nx/rollup:rollup` executor. It will also use [SWC](https://swc.rs) as the compiler. ```bash npx nx g @nx/js:lib libs/mylib --bundler=rollup ``` If you do not want to use `swc` as the compiler, and want to use the default `babel` compiler, you can do so in your `project.json` under the `build` target options, using the [`compiler` property](/nx-api/rollup/executors/rollup#compiler): ```jsonc title="libs/mylib/project.json" "build": { "executor": "@nx/rollup:rollup", "options": { //... "compiler": "babel" } } ``` ###### Buildable, with Vite as a bundler Generate a buildable library using [Vite](https://vite.dev/) as the bundler. This will use the `@nx/vite:build` executor. ```bash npx nx g @nx/js:lib libs/mylib --bundler=vite ``` ###### Using ESBuild Generate a buildable library using [ESBuild](https://esbuild.github.io/) as the bundler. This will use the `@nx/esbuild:esbuild` executor. ```bash npx nx g @nx/js:lib libs/mylib --bundler=esbuild ``` If you want to specify whether you want to bundle your library or not, you can do so in your `project.json` under the `build` target options, using the [`esbuildOptions` property](https://esbuild.github.io/api/): ```jsonc title="libs/mylib/project.json" "build": { "executor": "@nx/esbuild:esbuild", "options": { //... "esbuildOptions": { "bundle": true } } } ``` ###### Minimal publishing target Generate a **publishable** library with a minimal publishing target. The result will be a buildable library using the `@nx/js:tsc` executor, using `tsc` as the compiler. You can change the compiler or the bundler by passing the `--bundler` flag. ```bash npx nx g lib libs/mylib --publishable ``` ###### In a nested directory Generate a library named `mylib` and put it under a directory named `nested` (`libs/nested/mylib`). ```shell npx nx g lib libs/nested/mylib ``` ###### Non-buildable library Generate a non-buildable library. ```bash npx nx g @nx/js:lib libs/mylib --bundler=none ``` **Usage:** ```bash nx generate @nx/js:library [options] ``` **Aliases:** `lib` **Arguments:** ```bash nx generate @nx/js:library [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--buildable` | boolean | Generate a buildable library. | `true` | | `--bundler` | string | The bundler to use. Choosing 'none' means this library is not buildable. | `"tsc"` | | `--compiler` | string | The compiler used by the build and test targets | | | `--config` | string | Determines whether the project's executors should be configured in `workspace.json`, `project.json` or as npm scripts. | `"project"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--formatter` | string | The tool to use for code formatting. | | | `--importPath` | string | The library name used to import it, like @myorg/my-awesome-lib. Required for publishable library. | | | `--includeBabelRc` | boolean | Include a .babelrc configuration to compile TypeScript files | | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--minimal` | boolean | Generate a library with a minimal setup. No README.md generated. | `false` | | `--name` | string | Library name. | | | `--publishable` | boolean | Configure the library ready for use with `nx release` (https://nx.dev/core-features/manage-releases). | `false` | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--skipTsConfig` | boolean | Do not update tsconfig.json for development experience. | `false` | | `--skipTypeCheck` | boolean | Whether to skip TypeScript type checking for SWC compiler. | `false` | | `--strict` | boolean | Whether to enable tsconfig strict mode or not. | `true` | | `--tags` | string | Add tags to the library (used for linting). | | | `--testEnvironment` | string | The test environment to use if unitTestRunner is set to jest or vitest. | `"node"` | | `--unitTestRunner` | string | Test runner to use for unit tests. | | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `setup-build` Sets up build target for a project. **Usage:** ```bash nx generate @nx/js:setup-build [options] ``` **Arguments:** ```bash nx generate @nx/js:setup-build [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--bundler` | string [**required**] | The bundler to use to build the project. | `"tsc"` | | `--buildTarget` | string | The build target to add. | `"build"` | | `--main` | string | The path to the main entry file, relative to workspace root. Defaults to /src/index.ts or /src/main.ts. | | | `--tsConfig` | string | The path to the tsConfig file, relative to workspace root. Defaults to /tsconfig.lib.json or /tsconfig.app.json depending on project type. | | ## `setup-prettier` Setup Prettier as the formatting tool. **Usage:** ```bash nx generate @nx/js:setup-prettier [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | ## `setup-verdaccio` Setup Verdaccio local-registry. **Usage:** ```bash nx generate @nx/js:setup-verdaccio [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--skipFormat` | boolean | Skip formatting files. | `false` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/js: --help ``` --- ## Guides {% index_page_cards path="technologies/typescript/guides" /%} --- ## Nx with TypeScript A TypeScript/JavaScript (TS/JS) monorepo keeps many packages in one repository, sharing types and code directly instead of through published packages. Nx runs and maintains that monorepo at scale: it detects projects from your package manager workspaces, keeps TypeScript project references in sync automatically, and runs typechecking and builds with caching and affected detection. The `@nx/js` plugin provides [generators for creating TypeScript and JavaScript projects](#generate-and-manage-typescript-libraries), and its `@nx/js/typescript` plugin infers [`typecheck` and `build` tasks](#how-the-typescript-plugin-infers-tasks) from your tsconfig files. You don't need the plugin to use TypeScript with Nx, any project already benefits from [caching](/docs/features/cache-task-results), [task orchestration](/docs/features/run-tasks), and the [project graph](/docs/features/explore-graph). The plugin can help simplify setups and maintenance of TypeScript projects at scale. ## How Nx maintains a TypeScript monorepo Keeping all the tools in a large TypeScript monorepo correctly configured and working together is a difficult task, and every added tool is a new chance for conflicts. Nx approaches this from two directions: it configures itself to match the existing configuration of your other tools, and it enhances certain tools to work better in a monorepo. ### Project detection with package manager workspaces If your repository uses package manager workspaces, Nx uses those settings to find all the [projects](/docs/reference/project-configuration) in your repository. You don't define projects for your package manager and again for Nx: the `workspaces` configuration is enough for Nx to build the project graph. ```json // package.json { "workspaces": ["apps/*", "packages/*"] } ``` {% graph height="200px" title="Project View" %} ```json { "composite": false, "projects": [ { "name": "product-state", "type": "lib", "data": { "root": "packages/cart/product-state", "tags": ["scope:cart", "type:state"] } }, { "name": "ui-buttons", "type": "lib", "data": { "root": "packages/ui/buttons", "tags": ["scope:shared", "type:ui"] } }, { "name": "cart", "type": "app", "data": { "root": "apps/cart", "tags": ["type:app", "scope:cart"] } } ], "dependencies": { "product-state": [], "ui-buttons": [], "cart": [ { "source": "cart", "target": "product-state", "type": "static" }, { "source": "cart", "target": "ui-buttons", "type": "static" } ] }, "workspaceLayout": { "appsDir": "apps", "libsDir": "libs" }, "affectedProjectIds": [], "focus": null, "groupByFolder": false, "exclude": [], "enableTooltips": false } ``` {% /graph %} ### Tasks inferred from your tooling configuration Nx [plugins](/docs/concepts/nx-plugins) for tools like TypeScript, Vite, Playwright, and Jest [infer task configuration](/docs/concepts/mental-model#inferred-tasks) from your existing tooling config files, keeping those files as the single source of truth. Because a config file like `vite.config.ts` exists, Nx knows the project can run a `build` task with Vite, and it reads the config to set the correct [cache](/docs/features/cache-task-results) outputs. The TypeScript-specific inference is covered in [how the TypeScript plugin infers tasks](#how-the-typescript-plugin-infers-tasks). ### TypeScript project references kept in sync TypeScript [Project References](https://www.typescriptlang.org/docs/handbook/project-references.html) let the compiler build and typecheck each project independently, reusing intermediate `*.tsbuildinfo` files instead of re-typechecking every dependency. This provides [significant performance improvements](/docs/kb/typescript-project-linking#typescript-project-references-performance-benefits), particularly in a large monorepo. The downside is that each project's references have to be defined by hand in the appropriate `tsconfig.*.json` file, which is tedious to set up and hard to maintain as the repository changes. Nx uses a [sync generator](/docs/concepts/sync-generators) to update the references automatically from the project graph it already knows about: ```jsonc // apps/cart/tsconfig.json { "extends": "../../tsconfig.base.json", "files": [], // intentionally empty "references": [ // UPDATED BY NX SYNC // All project dependencies { "path": "../../packages/product-state", }, { "path": "../../packages/ui/buttons", }, // This project's other tsconfig.*.json files { "path": "./tsconfig.lib.json", }, { "path": "./tsconfig.spec.json", }, ], } ``` If someone adds another dependency to the `cart` app and runs the `build` task, Nx detects that the project references are out of sync and asks whether to update them: ```plaintext {% title="nx build cart" frame="terminal" %} NX The workspace is out of sync [@nx/js:typescript-sync]: Some TypeScript configuration files are missing project references to the projects they depend on or contain outdated project references. This will result in an error in CI. ? Would you like to sync the identified changes to get your workspace up to date? … ❯ Yes, sync the changes and run the tasks No, run the tasks without syncing the changes ``` ## Requirements Nx supports the latest version of TypeScript. TypeScript itself only officially supports its latest release under the [Modern Lifecycle Policy](https://learn.microsoft.com/en-us/lifecycle/policies/modern), but Nx maintains a wider range to give you time to upgrade. Support for an older TypeScript version may be dropped in an Nx major release, and support for a newer version may be added in an Nx minor release. | Nx Version | TypeScript Version | | -------------- | ------------------ | | 23.x (current) | >= 5.8.0 < 7.1.0 | | 22.x | >= 5.4.2 < 5.10.0 | | 21.x | >= 5.4.2 < 5.10.0 | | 20.x | ~5.4.2 | TypeScript 7.0 does not yet provide a programmatic API. To use its CLI while tools that consume the API continue to use TypeScript 6.0, see [Use TypeScript 7.0 alongside TypeScript 6.0](/docs/kb/typescript-7). ## Setting up @nx/js plugin ### Add to an existing Nx workspace {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/js` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). Using `nx add` helps ensure the correct version is installed and configured. {% /aside %} ```shell nx add @nx/js ``` Verify the plugin is setup: {% steps %} 1. Check for the `@nx/js` listed as a plugin: ```shell nx report ``` 2. Ensure inferred tasks are working: ```shell nx show projects --with-target=typecheck ``` 3. Inspect a specific project configuration for the `typecheck`/`build` task, depending on your project configuration: ```shell nx show project ``` {% /steps %} If your TypeScript projects aren't showing up with the right targets, then [view read how `@nx/js` infers projects](#how-the-typescript-plugin-infers-tasks) ### Create a new TypeScript monorepo {% tabs syncKey="package-manager" %} {% tabitem label="npm" %} ```shell npx create-nx-workspace my-org --template nrwl/typescript-template ``` {% /tabitem %} {% tabitem label="yarn" %} ```shell yarn create nx-workspace my-org --template nrwl/typescript-template ``` {% /tabitem %} {% tabitem label="pnpm" %} ```shell pnpm create nx-workspace my-org --template nrwl/typescript-template ``` {% /tabitem %} {% /tabs %} This creates a monorepo configured with [TypeScript Project References](https://www.typescriptlang.org/docs/handbook/project-references.html) and your package manager's [workspaces](https://docs.npmjs.com/cli/using-npm/workspaces) feature. [Nx automatically maintains the project references](#typescript-project-references-kept-in-sync) as you add and remove projects. {% aside type="tip" title="Tutorial" %} For a guided walkthrough, follow the [Learn Nx Tutorial](/docs/getting-started/tutorials/crafting-your-workspace). {% /aside %} The `--template nrwl/typescript-template` uses the modern setup with workspaces and project references as of Nx 20. You can [migrate an existing workspace from path aliases ](/docs/kb/switch-to-workspaces-project-references) to the modern setup. If you want to keep using the older style of setups with `compilerOptions.paths`, use `create-nx-workspace --preset=apps`. ### Add build support to an existing project If you have a TypeScript project that doesn't have a build target, you can use the [`setup-build` generator](/docs/technologies/typescript/generators#setup-build) to add one: ```shell nx g @nx/js:setup-build my-lib ``` You'll be prompted to choose a bundler. This configures the project for compilation without recreating it from scratch. ## Generate and manage TypeScript libraries ### Create a library Quickly scaffold out a new typescript library. You'll be prompted for common options like test runner and linter. See all the [library generator options](/docs/technologies/typescript/generators#library) for more customization. ```shell nx g @nx/js:lib libs/my-lib ``` ### Create a buildable or publishable library To create a library that can be compiled and published to npm, specify a bundler: ```shell # Using TSC (default compiler) nx g @nx/js:lib libs/my-lib --bundler=tsc # Using SWC (faster compilation) nx g @nx/js:lib libs/my-lib --bundler=swc # Using Rollup (to output in multiple formats) nx g @nx/js:lib libs/my-lib --bundler=rollup ``` If you plan to publish your library to npm, then you can pass in the `--publishable` flag to automatically setup [Nx Release](/docs/kb/release-npm-packages) to manage the releasing process. ```shell nx g @nx/js:lib libs/my-lib --bundler=tsc --publishable ``` By default, `@nx/js` uses [TSC](https://www.typescriptlang.org/docs/handbook/2/basic-types.html#tsc-the-typescript-compiler) to compile libraries. You can convert to use [SWC](https://swc.rs) for faster compilation at any time with the `convert-to-swc` generator: ```shell nx g @nx/js:convert-to-swc my-lib ``` SWC dependencies are installed automatically the first time the generator is ran. ### Build TypeScript libraries ```shell nx build my-lib ``` The output location is controlled by `outDir` in your build tsconfig (defaults to `tsconfig.lib.json`): ```json // packages/my-lib/tsconfig.lib.json { "compilerOptions": { "outDir": "./dist", "rootDir": "./src" } } ``` The plugin reads `outDir` automatically and sets the correct [task outputs](/docs/concepts/how-caching-works) for caching. If you're manually defining the `build` targets, make sure to keep the [target configuration outputs](/docs/kb/configure-outputs) in sync with where typescript is outputting the built artifacts. This ensures Nx properly restores the cache for your build tasks. ### Run typechecking ```shell nx typecheck my-lib ``` The `typecheck` task is automatically inferred for any project with a `tsconfig.json`. If you're not using inferred plugins, then you can manually define a `typecheck` target in your [projects configuration](/docs/reference/project-configuration#project-level-configuration-files). ## Configure `@nx/js` TypeScript inference By default, when you add the `@nx/js` the plugin will be configured to use project and target inference. Otherwise you can manually add the [plugin to your `nx.json#plugins` to enable it](#plugin-options) ### How the TypeScript plugin infers tasks The `@nx/js/typescript` plugin adds tasks based on the files it finds in your project: The `@nx/js/typescript` plugin will add a `typecheck` task to any project that has a `tsconfig.json`. The plugin also adds a `build` task for projects that meet both of the following conditions: 1. Have a runtime tsconfig file (defaults to `tsconfig.lib.json`) 2. Have a `package.json` with entry points that reference compiled output (not source files) For example, this project **will** get a `build` task because its exports point to `dist/`: ```json // packages/my-lib/package.json { "name": "@my-org/my-lib", "exports": { "./package.json": "./package.json", ".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" } } } ``` This project **will not** get a `build` task because its exports point directly to source: ```json // packages/my-lib/package.json { "name": "@my-org/my-lib", "exports": { "./package.json": "./package.json", ".": "./src/index.ts" } } ``` This distinction is important in monorepos because not all libraries need to be compiled. Libraries consumed only within the monorepo can point directly to source for faster development, while libraries published to npm need a build step. See [TypeScript Project Linking](/docs/kb/typescript-project-linking) for more on how projects reference each other. ### Plugin options Configure `@nx/js/typescript` in the `plugins` array in `nx.json`: ```json // nx.json { "plugins": [ { "plugin": "@nx/js/typescript", "options": { "compiler": "tsc", "typecheck": { "targetName": "typecheck", "configName": "tsconfig.json" }, "build": { "targetName": "build", "configName": "tsconfig.lib.json", "buildDepsName": "build-deps", "watchDepsName": "watch-deps", "skipBuildCheck": false }, "verboseOutput": false } } ] } ``` | Option | Description | Default | | ---------------------- | ----------------------------------------------------------------------------- | ------------------- | | `compiler` | Compiler used by the typecheck and build tasks: `tsc` or `tsgo`. | `tsc` | | `typecheck` | Enables typecheck inference; set to `false` to disable it. | enabled | | `typecheck.targetName` | Name of the cached typecheck task. | `typecheck` | | `typecheck.configName` | The tsconfig file used for typechecking. | `tsconfig.json` | | `build` | Enables build inference; set to `false` or omit it to disable build tasks. | disabled | | `build.targetName` | Name of the cached build task. | `build` | | `build.configName` | The tsconfig file used for builds. | `tsconfig.lib.json` | | `build.buildDepsName` | Name of the task for building all dependencies. | `build-deps` | | `build.watchDepsName` | Name of the task for watching and rebuilding dependencies. | `watch-deps` | | `build.skipBuildCheck` | Allows build inference without package exports that point to compiled output. | `false` | | `verboseOutput` | Adds TypeScript's `--verbose` flag to the underlying commands. | `false` | The `typecheck` and `build` tasks are cached, with inputs and outputs based on your TypeScript configuration, and depend on the corresponding tasks in dependency projects. ### Exclude or include specific projects You can control which projects the plugin applies to at three levels: **Plugin-level:** Use `include`/`exclude` glob patterns to scope the plugin to specific projects: ```json {% meta="{5-6}" %} // nx.json { "plugins": [ { "plugin": "@nx/js/typescript", "include": ["packages/**/*"], "exclude": ["packages/legacy-app/**/*"], "options": { ... } } ] } ``` **Project-level:** To opt a specific project out of the `typecheck` task, set `nx.addTypecheckTarget` to `false` in that project's `tsconfig.json`: ```json {% meta="{6-8}" %} // packages/my-lib/tsconfig.json { "extends": "../../tsconfig.base.json", "files": [], "include": [], "references": [{ "path": "./tsconfig.lib.json" }], "nx": { "addTypecheckTarget": false } } ``` ### View inferred tasks To see what tasks Nx inferred for a project: ```shell nx show project ``` Or open the [project details view in Nx Console](/docs/kb/console-project-details#_top). A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter for these tasks must use the exact identifier `@nx/js/typescript`. ## Set up CI for your TypeScript monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). ## What's next {% cardgrid %} {% linkcard title="Learn Nx Tutorial" description="Build a TypeScript monorepo step-by-step with Nx." href="/docs/getting-started/tutorials/crafting-your-workspace" /%} {% linkcard title="Switch to Workspaces & Project References" description="Migrate from path aliases to the modern monorepo setup." href="/docs/kb/switch-to-workspaces-project-references" /%} {% linkcard title="Compile to Multiple Formats" description="Build libraries to both ESM and CommonJS with Rollup." href="/docs/kb/compile-multiple-formats" /%} {% linkcard title="Generators Reference" description="Full API reference for @nx/js generators." href="/docs/technologies/typescript/generators" /%} {% linkcard title="Executors Reference" description="Full API reference for @nx/js executors." href="/docs/technologies/typescript/executors" /%} {% linkcard title="Migrations Reference" description="Full reference for @nx/js migrations." href="/docs/technologies/typescript/migrations" /%} {% /cardgrid %} --- ## @nx/js Migrations For an overview of the plugin and setup instructions, see the [@nx/js introduction](/docs/technologies/typescript/introduction). The @nx/js plugin provides various migrations to help you migrate to newer versions of js projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.2.x ### `update-23-2-0-add-pnpm-prune-lockfile-cache-config` **Version**: 23.2.0-beta.11 Add the pnpm-workspace.yaml, patches, and local_path_modules paths to the outputs of @nx/js:prune-lockfile targets in pnpm workspaces, and the workspace root pnpm-workspace.yaml, package.json, and a pnpm-major runtime probe to their inputs, so a cache replay restores the pnpm install settings, patch files, and vendored local-path dependencies the executor now emits, and a change to the root pnpm install settings or a pnpm major switch does not replay a stale one. #### Update the cache configuration of `@nx/js:prune-lockfile` targets for the pnpm install artifacts In a pnpm workspace, the `@nx/js:prune-lockfile` executor emits more than the pruned `package.json` and `pnpm-lock.yaml`: a settings-only `pnpm-workspace.yaml`, the `.patch` files of a `pnpm patch` workspace under `patches/`, and non-workspace local-path dependencies (`file:` tarballs and directories, `link:` targets) under `local_path_modules/`. On pnpm 11 and above that workspace file is where the build-script approvals and `supportedArchitectures` live; on pnpm 10 and below they go into the emitted `package.json` and the file ships carrying only an empty `packages` list. Targets generated before this change declare only the manifest and the lockfile in `outputs`, so a cache replay in a clean checkout restores just those two files. The missing `patches/` and `local_path_modules/` break the deploy install loudly. On pnpm 11 and above a missing `pnpm-workspace.yaml` is worse than loud: it silently drops the build-script approvals, and the deployed app's native dependencies never run their build scripts. Those artifacts are built from pnpm settings the workspace root declares. The build approvals and `supportedArchitectures` among them are recorded nowhere in the lockfile, so nothing else in the task hash moves when they change, and the same targets declare no `inputs` at all. Revoking a build approval would replay the previous run's artifact from cache and ship an output that still grants it. The three artifact paths are appended to whichever `outputs` array declares the `pnpm-lock.yaml` entry, reusing that entry's own path prefix. That covers a target's own `outputs` and the `targetDefaults` entries applying to prune-lockfile targets: entries keyed by the executor, declaring or filtering on it, and entries under a target-name or glob key that resolves to one of them. Configurations without `outputs`, configurations whose lockfile entry is not pnpm's, and paths already present are left untouched. The two root sources, plus a `runtime` input probing the pnpm major (which decides whether the settings land in the emitted `pnpm-workspace.yaml` or the emitted `package.json` when the root manifest has no `packageManager` field; it is added even when the field currently pins a pnpm version, because the migration runs once and the pin can be removed later), go somewhere else, because a target's own `inputs` array replaces the defaults' rather than merging with it. Each target is classified by its merged `outputs`, so inheriting or spreading the lockfile entry still counts, and the sources are added once: to the target's own `inputs` when it declares them, and otherwise to the `targetDefaults` entry supplying the array it inherits. A target inheriting nothing gets nx's own default spelled out first, so nothing that was hashed stops being hashed. A source the target already hashes is left alone. A workspace with no prune-lockfile target at all, configuring them purely through `targetDefaults`, has the sources added to every compatible entry under the key that declares its own `inputs` (each can be the array a future target inherits, and a sibling's array covers nothing); when no filter-less entry declares any, the spelled-out default is prepended as a new entry, under a name or glob key pinned to the executor so same-name targets of other executors do not inherit it. The fallback is only authored where it cannot change which key a target resolves: under the executor key when a compatible filter-less entry already exists, and under a name or glob key when a compatible filter-less entry already pins the executor. Any other key gets no fallback, whether its compatible entries are all filtered or its filter-less entries do not pin the executor, because a new entry would make that key win selection for targets that previously resolved their defaults elsewhere. The migration only ever appends to an array that already exists or writes that spelled-out default; it never authors a `"..."` of its own. Whether a spread finds anything to expand against depends on the identity resets in nx's own document-order merge, and one that finds nothing would leave the target hashing only the two root files. #### Sample code changes ##### Before ```json title="apps/app1/project.json" { "targets": { "prune-lockfile": { "executor": "@nx/js:prune-lockfile", "outputs": [ "{workspaceRoot}/dist/apps/app1/package.json", "{workspaceRoot}/dist/apps/app1/pnpm-lock.yaml" ] } } } ``` ##### After ```json title="apps/app1/project.json" { "targets": { "prune-lockfile": { "executor": "@nx/js:prune-lockfile", "inputs": [ "default", "^default", "{workspaceRoot}/pnpm-workspace.yaml", "{workspaceRoot}/package.json", { "runtime": "node -e \"try{console.log('pnpm major '+require('child_process').execSync('pnpm --version',{stdio:['ignore','pipe','ignore']}).toString().trim().split('.')[0])}catch{console.log('pnpm major unavailable')}\"" } ], "outputs": [ "{workspaceRoot}/dist/apps/app1/package.json", "{workspaceRoot}/dist/apps/app1/pnpm-lock.yaml", "{workspaceRoot}/dist/apps/app1/pnpm-workspace.yaml", "{workspaceRoot}/dist/apps/app1/patches", "{workspaceRoot}/dist/apps/app1/local_path_modules" ] } } } ``` ### `update-23-2-0-add-pnpm-deploy-output-cache-inputs` **Version**: 23.2.0-beta.11 Add the workspace root pnpm-workspace.yaml, the pnpm settings fields of the root package.json, and a pnpm-major runtime probe to the inputs of build targets that emit the pruned pnpm deploy output (webpack, rspack, vite, esbuild with generatePackageJson; next, remix, tsc, swc with generateLockfile), so revoking a build-script approval, changing another pnpm install setting, or crossing the pnpm 10/11 boundary does not replay a cached output that still carries the old one. #### Add the pnpm install settings sources to the inputs of build targets that emit the pruned deploy output In a pnpm workspace, build executors that generate a deployable `package.json` also emit the pruned deploy output next to it: the pruned `pnpm-lock.yaml`, a settings-only `pnpm-workspace.yaml`, the `.patch` files of a `pnpm patch` workspace, and vendored local-path dependencies. webpack, rspack, vite and esbuild builds do this when `generatePackageJson` is enabled; next, remix, tsc and swc builds when `generateLockfile` is. The build-script approvals and `supportedArchitectures` those artifacts carry come from the workspace root `pnpm-workspace.yaml` and the `pnpm` field of the root `package.json`, and are recorded in no lockfile, so nothing in the build target's default task hash moves when they change. Revoking an approval after a package turns out to be malicious would replay the previous build from cache and ship an output that still grants it. The migration adds three inputs to the affected targets: the root `pnpm-workspace.yaml` as a fileset; a `json` input hashing only the fields of the root `package.json` the output is built from (`packageManager`, which selects whether the settings land in the emitted `pnpm-workspace.yaml` or the emitted `package.json`, plus `pnpm.onlyBuiltDependencies`, `pnpm.neverBuiltDependencies`, `pnpm.allowBuilds`, `pnpm.supportedArchitectures` and `pnpm.patchedDependencies`), so ordinary dependency bumps in the root manifest do not invalidate every build; and a `runtime` input probing the pnpm major, which decides that same file selection when the `packageManager` field is absent. The probe prints only the major, so pnpm patch and minor releases do not move the hash, and a sentinel when no pnpm binary is available, so it is safe in every workspace. It is added even when the field currently pins a pnpm version: the migration runs once, and the pin can be removed later. The contents of vendored non-workspace local-path dependencies (`file:` directories and tarballs, `link:` targets) also ship in the deploy output but are not covered by these inputs: their set is derived from the lockfile at build time, so no statically written input list can stay correct as dependencies change. A target's own `inputs` array replaces the defaults' rather than merging with it, so the inputs are added to the layer whose array the runtime actually uses: the target's own `inputs` when it declares them, otherwise the `targetDefaults` entry supplying the array it inherits, and otherwise the target itself with nx's own default spelled out first, so nothing that was hashed stops being hashed. The gating option is read from the merged view, so one supplied by a matching `targetDefaults` entry or set only in a configuration still counts. A source the target already hashes, including via a whole-file root `package.json` fileset or an existing `json` input covering the settings fields, is left alone. The migration never authors a `"..."` of its own. Targets inferred by the `@nx/webpack` and `@nx/rspack` plugins declare the `pnpm-workspace.yaml` and `json` inputs themselves on every graph build, and the `runtime` probe while the root `package.json` has no `packageManager` field pinning a pnpm version, but a project-level target entry (under the exact name or a matching glob-pattern key) or a matching `targetDefaults` entry that supplies its own `inputs` array without `"..."` replaces the plugin-generated one and discards them. The migration repairs those overlays the same way, appending the complete set of three inputs to the replacing array, because it runs once and the pin can be removed later; when several entries match, only the last one applies, and it takes the repair. An inferred target with no such overlay is left alone, as is one whose entry changes what the target runs once merged: a different executor or effective command means the plugin's build no longer runs there. An entry that merely restates the identity the plugin infers is still an overlay and gets the repair. #### Sample code changes ##### Before ```json title="apps/app1/project.json" { "targets": { "build": { "executor": "@nx/webpack:webpack", "options": { "generatePackageJson": true } } } } ``` ##### After ```json title="apps/app1/project.json" { "targets": { "build": { "executor": "@nx/webpack:webpack", "inputs": [ "default", "^default", "{workspaceRoot}/pnpm-workspace.yaml", { "json": "{workspaceRoot}/package.json", "fields": [ "packageManager", "pnpm.onlyBuiltDependencies", "pnpm.neverBuiltDependencies", "pnpm.allowBuilds", "pnpm.supportedArchitectures", "pnpm.patchedDependencies" ] }, { "runtime": "node -e \"try{console.log('pnpm major '+require('child_process').execSync('pnpm --version',{stdio:['ignore','pipe','ignore']}).toString().trim().split('.')[0])}catch{console.log('pnpm major unavailable')}\"" } ], "options": { "generatePackageJson": true } } } } ``` ## 23.1.x ### `23-1-0-add-ignore-deprecations-for-ts6` **Version**: 23.1.0-beta.8 Adds `"ignoreDeprecations": "6.0"` to tsconfig files whose compilerOptions (or ts-node.compilerOptions) carry a TypeScript 6 deprecated option value, set directly or inherited through `extends` including from a base this migration does not edit (e.g. moduleResolution node/node10/classic, baseUrl, target es5, esModuleInterop false, outFile, module amd/umd/system/none, alwaysStrict false, allowSyntheticDefaultImports false, downlevelIteration set). Also pins `"strict": false`, `"noUncheckedSideEffectImports": false`, `"types": ["*"]`, and `"esModuleInterop": false` in chain-root tsconfigs (no "extends") that lack each key, preserving pre-TS6 behavior where these defaults changed; `esModuleInterop` false is itself deprecated (removed in TS7) so it also receives `ignoreDeprecations`, deferring the interop change to the TS7 migration. Also adds `ignoreDeprecations` to every `tsconfig.json` (the exact name jest/ts-node auto-resolve when compiling a config file; ts-node injects a deprecated default `target: es5` there, so the load can hit a TS6 error even on a clean config), even one with no deprecated value of its own; a stale local `ignoreDeprecations` (e.g. `5.0`) that would otherwise override the inherited flag is upgraded. #### Requires | Name | Version | |------|---------| `typescript` | `>=6.0.0` | #### Keep Existing Workspaces Compiling on TypeScript 6 TypeScript 6 turns several long-deprecated compiler options into hard errors and flips a few option defaults to stricter values. So that an existing workspace keeps compiling on TypeScript 6 without being migrated to a full TypeScript 6 setup, this migration edits the `tsconfig*.json` files in the workspace: - Adds `"ignoreDeprecations": "6.0"` to any `compilerOptions` (or `ts-node.compilerOptions`) block whose effective options carry a TypeScript 6 deprecated option, set directly or inherited through `extends` (including from a base this migration does not edit, such as a package-provided config). Examples: `moduleResolution` set to `node`/`node10`/`classic`, `baseUrl`, `target` set to `es5`, `esModuleInterop: false`, `outFile`, `module` set to `amd`/`umd`/`system`/`none`, `alwaysStrict: false`, `allowSyntheticDefaultImports: false`, or `downlevelIteration`. A block that already inherits an effective `"6.0"` and sets no local value is left alone; a stale local value such as `"5.0"` is upgraded to `"6.0"`, since it would otherwise override the inherited flag and still error. - Pins `"strict": false`, `"noUncheckedSideEffectImports": false`, `"types": ["*"]`, and `"esModuleInterop": false` on every chain-root tsconfig (one without an `extends`) that does not already set them. TypeScript 6 treats an absent `strict` as `true` (it was `false` when unset before), defaults `noUncheckedSideEffectImports` to `true` (which turns a bare side-effect import such as `import './styles.css'` without an ambient module declaration into an error), no longer auto-loads every `@types` package when `types` is unset the way TypeScript 5 did (the `"*"` wildcard restores that last default), and flips `esModuleInterop` from `false` to `true` (so an `import * as x from ''` binds a non-callable namespace object and a call or `new` on that import fails at runtime). Pinning all four preserves the pre-TypeScript 6 behavior. Because `esModuleInterop: false` is itself a TypeScript 6 deprecated value, this pin runs before the `ignoreDeprecations` edit above, so the added `false` is silenced in the same run. - Adds `"ignoreDeprecations": "6.0"` to every `tsconfig.json` (the exact file name jest and ts-node auto-resolve), even one that carries no deprecated option of its own. Those loaders compile config files such as `jest.config.ts`, and ts-node injects a default `target: es5` when the config leaves it unset. TypeScript 6 rejects `es5` as a deprecated value, so the flag keeps that load working. It is inert wherever nothing is actually deprecated. Files that use `extends` inherit the pinned settings from their chain root, so the pins are not repeated on them, and pure solution-style tsconfigs (`"files": []` with no `include`) receive no pins either, though a solution-style `tsconfig.json` still gets the config-load flag. The migration only runs when the workspace is on TypeScript 6. #### Sample Code Changes ##### Before ```json title="tsconfig.json" { "compilerOptions": { "target": "es5", "module": "esnext", "moduleResolution": "bundler" } } ``` ##### After ```json title="tsconfig.json" {6-10} { "compilerOptions": { "target": "es5", "module": "esnext", "moduleResolution": "bundler", "strict": false, "noUncheckedSideEffectImports": false, "types": ["*"], "esModuleInterop": false, "ignoreDeprecations": "6.0" } } ``` ### `23-1-0-set-tsconfig-root-dir-for-ts6` **Version**: 23.1.0-rc.2 Sets an explicit `rootDir` on project `tsconfig*.json` files that lack one, pinned to the source directory TypeScript 5 inferred implicitly, so programs keep compiling and emitting the same layout under TypeScript 6, which otherwise hard-fails with TS5011 or TS6059 (for example a spec tsconfig importing another project's source through a `paths` alias). The pin is written even when the inferred directory already equals the tsconfig directory, because tools like ts-jest with `isolatedModules` compile a program per file and re-infer a deeper directory from that subset. The value is computed by the compiler, so emit layout is unchanged. Each config is written on its own; the migration never writes to a shared `extends` base, so a config never inherits a value computed for a sibling. Composite projects are pinned to their own directory: `rootDir` already defaults there under `tsc` so it is a no-op for a real composite build, but ts-jest strips `composite` for its per-file transpile and the explicit value is needed to avoid TS5011 there. #### Requires | Name | Version | |------|---------| `typescript` | `>=6.0.0` | #### Pin the Inferred `rootDir` for TypeScript 6 Before TypeScript 6, a tsconfig that did not set `rootDir` had it inferred as the common directory of the program's non-declaration input files. TypeScript 6 changed that default to the tsconfig's own directory. A program whose files resolve outside that directory (most commonly a spec or e2e tsconfig that imports another project's source through a `paths` alias) now hard-fails with `TS5011` or `TS6059` because a file falls outside the assumed root. For every project `tsconfig*.json` that does not already set `rootDir` (directly or through `extends`), this migration pins `rootDir` to exactly the directory TypeScript 5 would have inferred, so both compilation and emit layout stay the same under TypeScript 6. The value is computed by the TypeScript compiler itself, so it matches `tsc` exactly, including project-reference redirects. The pin is written even when the inferred directory already equals the tsconfig directory: inference is per-program, and a tool that compiles a subset of the config's files — ts-jest with `isolatedModules` builds a program per test file — re-infers a deeper common directory from that subset and fails with `TS5011`. Configs that cannot hit the error are skipped: ones without an output option (`outDir`, `outFile`, `sourceRoot`, `mapRoot`, or `declaration` + `declarationDir`), and ones with no input files. The migration only runs when the workspace is on TypeScript 6. Composite projects are pinned to their own directory (`"."`). Under `tsc` a composite `rootDir` already defaults there, so the pin is a no-op for a real composite build — but ts-jest strips `composite` for its per-file transpile, and TypeScript 6 only exempts genuinely-composite programs from the containment check, so a composite spec config compiled by ts-jest still fails with `TS5011` without an explicit `rootDir`. Pinning the own directory (rather than a deeper file-derived value) keeps the composite build's emit layout unchanged while fixing the ts-jest case. Each config is updated on its own; the migration never writes `rootDir` to a shared `extends` base, so a config never inherits a value computed for a sibling. Because every config that emits is given its own explicit `rootDir`, none is left to inherit a value pinned on a base. #### Sample Code Changes ##### Before A `libs/products/e2e/tsconfig.json` whose specs import a shared library's source: ```json title="libs/products/e2e/tsconfig.json" { "extends": "../../../tsconfig.base.json", "compilerOptions": { "outDir": "../../../dist/out-tsc" }, "include": ["src/**/*.ts"] } ``` ##### After ```json title="libs/products/e2e/tsconfig.json" {4} { "extends": "../../../tsconfig.base.json", "compilerOptions": { "rootDir": "../../..", "outDir": "../../../dist/out-tsc" }, "include": ["src/**/*.ts"] } ``` ### 23.1.0-typescript-package-updates **Version**: 23.1.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `typescript` | `~6.0.3` | Updated only ### 23.1.0-swc-cli-package-updates **Version**: 23.1.0-rc.3 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@swc/cli` | `~0.8.1` | Updated only ## 23.0.x ### `23-0-0-rewrite-internal-subpath-imports` **Version**: 23.0.0-beta.14 Rewrites `@nx/js/src/*` subpath imports to the new `@nx/js/internal` entry. The `./src/*` wildcard has been removed from `@nx/js`'s exports map; `@nx/js/src/release/version-actions` is preserved as a non-wildcard entry for back-compat with existing nx.json release configs. If a rewritten import resolves to a symbol that lives on the public `@nx/js` entry (e.g. `libraryGenerator`, `extractTsConfigBase`, `resolvePathsBaseUrl`), change the specifier to `@nx/js`. ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/js/typescript` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/js` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/js/typescript` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/js/typescript'; ``` ##### After ```ts import { createNodes } from '@nx/js/typescript'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/js/typescript` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ### 23.0.0-swc-cli-package-updates **Version**: 23.0.0-beta.17 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@swc/cli` | `~0.8.0` | Updated only ## 22.6.x ### 22.6.4-package-updates **Version**: 22.6.4 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `verdaccio` | `^6.3.2` | Updated only ## 22.5.x ### 22.5.0-package-updates **Version**: 22.5.0-beta.1 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@swc/core` | `^1.15.5` | Updated only | `@swc/helpers` | `^0.5.18` | Updated only | `@swc-node/register` | `^1.11.1` | Updated only ### 22.5.0-swc-cli-package-updates **Version**: 22.5.0-beta.1 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@swc/cli` | `^0.7.10` | Updated only ## 22.1.x ### `remove-redundant-ts-project-references` **Version**: 22.1.0-rc.1 Removes redundant TypeScript project references from project's tsconfig.json files when runtime tsconfig files (e.g., tsconfig.lib.json, tsconfig.app.json) exist. #### Removes Redundant TypeScript Project References from tsconfig.json Files Removes redundant TypeScript project references from `tsconfig.json` files when runtime tsconfig files (e.g., `tsconfig.lib.json`, `tsconfig.app.json`) exist. Previously, external project references were duplicated in both the project's `tsconfig.json` and runtime tsconfig files. This migration syncs the TypeScript project references to match the project graph, ensuring that external references only appear in runtime tsconfig files when they exist. #### Examples When a project has runtime tsconfig files like `tsconfig.lib.json`, the migration will remove external project references from the project's `tsconfig.json` file: ##### Before ```jsonc {7-9} // libs/my-lib/tsconfig.json { "compilerOptions": { "composite": true, }, "references": [ { "path": "../other-lib", }, ], } ``` ##### After ```jsonc {6} // libs/my-lib/tsconfig.json { "compilerOptions": { "composite": true, }, "references": [], } ``` The external references remain in the runtime tsconfig file where they belong: ##### Before ```jsonc {7-9} // libs/my-lib/tsconfig.lib.json { "compilerOptions": { "composite": true, }, "references": [ { "path": "../other-lib/tsconfig.lib.json", }, ], } ``` ##### After ```jsonc {7-9} // libs/my-lib/tsconfig.lib.json { "compilerOptions": { "composite": true, }, "references": [ { "path": "../other-lib/tsconfig.lib.json", }, ], } ``` For projects without runtime tsconfig files, the project's `tsconfig.json` file will continue to contain external project references: ##### Before ```jsonc {7-9} // libs/legacy-lib/tsconfig.json { "compilerOptions": { "composite": true, }, "references": [ { "path": "../other-lib", }, ], } ``` ##### After ```jsonc {7-9} // libs/legacy-lib/tsconfig.json { "compilerOptions": { "composite": true, }, "references": [ { "path": "../other-lib", }, ], } ``` Internal project references (references within the same project directory) are preserved in the project's `tsconfig.json`: ##### Before ```jsonc {7-12} // libs/my-lib/tsconfig.json { "compilerOptions": { "composite": true, }, "references": [ { "path": "./tsconfig.lib.json", }, { "path": "./tsconfig.spec.json", }, ], } ``` ##### After ```jsonc {7-12} // libs/my-lib/tsconfig.json { "compilerOptions": { "composite": true, }, "references": [ { "path": "./tsconfig.lib.json", }, { "path": "./tsconfig.spec.json", }, ], } ``` ## 22.0.x ### `remove-external-options-from-js-executors` **Version**: 22.0.0-beta.0 Remove the deprecated `external` and `externalBuildTargets` options from the `@nx/js:swc` and `@nx/js:tsc` executors. #### Remove the `external` and `externalBuildTargets` Options from the `@nx/js:swc` and `@nx/js:tsc` Executors Remove the deprecated `external` and `externalBuildTargets` options from the `@nx/js:swc` and `@nx/js:tsc` executors. These options were used for inlining dependencies, which was an experimental feature and has been deprecated for a long time. The migration only removes the options from the project configuration and target defaults. If you rely on inlining dependencies, you need to make sure they are all buildable or use a different build tool that supports bundling. #### Sample Code Changes Remove `external` and `externalBuildTargets` from the `@nx/js:swc` or `@nx/js:tsc` executor options in project configuration. ##### Before ```json title="libs/my-lib/project.json" {9-10} { "targets": { "build": { "executor": "@nx/js:swc", "options": { "main": "libs/my-lib/src/index.ts", "outputPath": "dist/libs/my-lib", "tsConfig": "libs/my-lib/tsconfig.lib.json", "external": ["react", "react-dom"], "externalBuildTargets": ["build"] } } } } ``` ##### After ```json title="libs/my-lib/project.json" { "targets": { "build": { "executor": "@nx/js:swc", "options": { "main": "libs/my-lib/src/index.ts", "outputPath": "dist/libs/my-lib", "tsConfig": "libs/my-lib/tsconfig.lib.json" } } } } ``` Remove `external` and `externalBuildTargets` from the `@nx/js:swc` or `@nx/js:tsc` executor target defaults in `nx.json`. ##### Before ```json title="nx.json" {8-9} { "targetDefaults": { "@nx/js:swc": { "options": { "main": "{projectRoot}/src/index.ts", "outputPath": "dist/{projectRoot}", "tsConfig": "{projectRoot}/tsconfig.lib.json", "external": "all", "externalBuildTargets": ["build"] } } } } ``` ##### After ```json title="nx.json" { "targetDefaults": { "@nx/js:swc": { "options": { "main": "{projectRoot}/src/index.ts", "outputPath": "dist/{projectRoot}", "tsConfig": "{projectRoot}/tsconfig.lib.json" } } } } ``` ## 21.5.x ### `migrate-development-custom-condition` **Version**: 21.5.0-beta.2 Migrate the legacy 'development' custom condition to a workspace-unique custom condition name. #### Migrate `development` custom condition to unique workspace-specific name Replace the TypeScript `development` custom condition with a unique workspace-specific name to avoid conflicts when consuming packages in other workspaces. #### Examples The migration will update the custom condition name in both `tsconfig.base.json` and all workspace package.json files that use the `development` custom condition: ##### Before ```json title="tsconfig.base.json" {3} { "compilerOptions": { "customConditions": ["development"] } } ``` ##### After ```json title="tsconfig.base.json" {3} { "compilerOptions": { "customConditions": ["@my-org/source"] // assuming the root package.json name is `@my-org/source` } } ``` The migration also updates `package.json` files that use the `development` condition in their `exports` field and point to TypeScript files: ##### Before ```json title="libs/my-lib/package.json" {5} { "name": "@myorg/my-lib", "exports": { ".": { "development": "./src/index.ts", "default": "./dist/index.js" } } } ``` ##### After ```json title="libs/my-lib/package.json" {5} { "name": "@myorg/my-lib", "exports": { ".": { "@my-org/source": "./src/index.ts", "default": "./dist/index.js" } } } ``` If the custom condition is not set to `["development"]` or the `package.json`'s `exports` field doesn't point to TypeScript files, the migration will not modify the configuration: ##### Before ```json title="libs/my-lib/package.json" {5} { "name": "@myorg/my-lib", "exports": { ".": { "development": "./dist/index.js", "default": "./dist/index.js" } } } ``` ##### After ```json title="libs/my-lib/package.json" {5} { "name": "@myorg/my-lib", "exports": { ".": { "development": "./dist/index.js", "default": "./dist/index.js" } } } ``` ### 21.5.0-package-updates **Version**: 21.5.0-beta.2 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `typescript` | `~5.9.2` | Updated only ## 21.2.x ### 21.2.0-package-updates **Version**: 21.2.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `typescript` | `~5.8.2` | Updated only --- ## Vue Get started with [Nx with Vue](/docs/technologies/vue/introduction), or browse the topics below. {% index_page_cards path="technologies/vue" /%} --- ## @nx/vue Generators The @nx/vue plugin provides various generators to help you create and configure vue projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/vue introduction](/docs/technologies/vue/introduction). ## `application` Create a Vue application for Nx. ### Examples ###### Simple Application Create an application named `my-app`: ```shell nx g @nx/vue:app apps/my-app ``` ###### Specify style extension Create an application named `my-app` in the `my-dir` directory and use `scss` for styles: ```shell nx g @nx/vue:app apps/my-dir/my-app --style=scss ``` ###### Add tags Add tags to the application (used for linting). ```shell nx g @nx/vue:app apps/my-app --tags=scope:admin,type:ui ``` **Usage:** ```bash nx generate @nx/vue:application [options] ``` **Aliases:** `app` **Arguments:** ```bash nx generate @nx/vue:application [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--bundler` | string | The bundler to use. | `"vite"` | | `--e2eTestRunner` | string | Test runner to use for end to end (E2E) tests. | `"playwright"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--formatter` | string | The tool to use for code formatting. | | | `--inSourceTests` | boolean | When using Vitest, separate spec files will not be generated and instead will be included within the source files. Read more on the Vitest docs site: https://vitest.dev/guide/in-source.html | `false` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | The name of the application. | | | `--rootProject` | boolean | Create a application at the root of the workspace | `false` | | `--routing` | boolean | Generate application with routes. | `false` | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--strict` | boolean | Whether to enable tsconfig strict mode or not. | `true` | | `--style` | string | The file extension to be used for style files. | `"css"` | | `--tags` | string | Add tags to the application (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `component` Create a Vue Component for Nx. **Usage:** ```bash nx generate @nx/vue:component [options] ``` **Aliases:** `c` **Arguments:** ```bash nx generate @nx/vue:component [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--export` | boolean | When true, the component is exported from the project `index.ts` (if it exists). | `false` | | `--fileName` | string | Create a component with this file name. | | | `--inSourceTests` | boolean | When using Vitest, separate spec files will not be generated and instead will be included within the source files. Read more on the Vitest docs site: https://vitest.dev/guide/in-source.html | `false` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--routing` | boolean | Generate a library with routes. | | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipTests` | boolean | When true, does not create `spec.ts` test files for the new component. | `false` | ## `library` Create a Vue Library for an Nx workspace. **Usage:** ```bash nx generate @nx/vue:library [options] ``` **Aliases:** `lib` **Arguments:** ```bash nx generate @nx/vue:library [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--appProject` | string | The application project to add the library route to. | | | `--bundler` | string | The bundler to use. Choosing 'none' means this library is not buildable. | `"none"` | | `--component` | boolean | Generate a default component. | `false` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--importPath` | string | The library name used to import it, like `@myorg/my-awesome-lib`. | | | `--inSourceTests` | boolean | When using Vitest, separate spec files will not be generated and instead will be included within the source files. | `false` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--minimal` | boolean | Create a Vue library with a minimal setup, no separate test files. | `false` | | `--name` | string | Library name | | | `--publishable` | boolean | Create a publishable library. | | | `--routing` | boolean | Generate library with routes. | | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--skipTsConfig` | boolean | Do not update `tsconfig.json` for development experience. | `false` | | `--strict` | boolean | Whether to enable tsconfig strict mode or not. | `true` | | `--tags` | string | Add tags to the library (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"none"` | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `stories` Generate stories/specs for all components declared in a project. This generator will generate stories for all your components in your project. The stories will be generated using [Component Story Format 3 (CSF3)](https://storybook.js.org/blog/storybook-csf3-is-here/). You can also use this generator to generate stories for your **Nuxt** project: ```bash nx g @nx/vue:stories project-name ``` or ```bash nx g @nx/nuxt:stories project-name ``` You can read more about how this generator works, in the [Storybook for Vue overview page](/recipes/storybook/overview-vue#auto-generate-stories). When running this generator, you will be prompted to provide the following: - The `name` of the project you want to generate the configuration for. - Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, a `play` function will be added to your stories, and all the necessary dependencies will be installed. You can read more about this in the [Nx Storybook interaction tests documentation page](/recipes/storybook/storybook-interaction-tests#setup-storybook-interaction-tests).. You must provide a `name` for the generator to work. By default, this generator will also set up [Storybook interaction tests](https://storybook.js.org/docs/angular/writing-tests/interaction-testing). If you don't want to set up Storybook interaction tests, you can pass the `--interactionTests=false` option, but it's not recommended. There are a number of other options available. Let's take a look at some examples. ### Examples #### Ignore certain paths when generating stories ```bash nx g @nx/vue:stories --name=ui --ignorePaths=libs/ui/src/not-stories/**,**/**/src/**/*.other.* ``` This will generate stories for all the components in the `ui` project, except for the ones in the `libs/ui/src/not-stories` directory, and also for components that their file name is of the pattern `*.other.*`. This is useful if you have a project that contains components that are not meant to be used in isolation, but rather as part of a larger component. By default, Nx will ignore the following paths: ```text *.stories.ts, *.stories.tsx, *.stories.js, *.stories.jsx, *.stories.mdx ``` but you can change this behaviour easily, as explained above. #### Generate stories using JavaScript instead of TypeScript ```bash nx g @nx/vue:stories --name=ui --js=true ``` This will generate stories for all the components in the `ui` project using JavaScript instead of TypeScript. So, you will have `.stories.js` files next to your components. **Usage:** ```bash nx generate @nx/vue:stories [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--project` | string [**required**] | Project for which to generate stories. | | | `--ignorePaths` | array | Paths to ignore when looking for components. | `["*.stories.ts,*.stories.tsx,*.stories.js,*.stories.jsx,*.stories.mdx"]` | | `--interactionTests` | boolean | Set up Storybook interaction tests. | `true` | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | ## `storybook-configuration` Set up Storybook for a Vue project. This generator will set up Storybook for your **Vue** project. You can also use this generator to generate Storybook configuration for your **Nuxt** project. ```bash nx g @nx/vue:storybook-configuration project-name ``` or ```bash nx g @nx/nuxt:storybook-configuration project-name ``` You can read more about how this generator works, in the [Storybook for Vue overview page](/recipes/storybook/overview-vue#generate-storybook-configuration-for-a-vue-project). When running this generator, you will be prompted to provide the following: - The `name` of the project you want to generate the configuration for. - Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/vue/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, a `play` function will be added to your stories, and all the necessary dependencies will be installed. Also, a `test-storybook` target will be generated in your project's `project.json`, with a command to invoke the [Storybook `test-runner`](https://storybook.js.org/docs/vue/writing-tests/test-runner). You can read more about this in the [Nx Storybook interaction tests documentation page](/recipes/storybook/storybook-interaction-tests#setup-storybook-interaction-tests).. - Whether you want to `generateStories` for the components in your project. If you choose `yes`, a `.stories.ts` file will be generated next to each of your components in your project. You must provide a `name` for the generator to work. By default, this generator will also set up [Storybook interaction tests](https://storybook.js.org/docs/vue/writing-tests/interaction-testing). If you don't want to set up Storybook interaction tests, you can pass the `--interactionTests=false` option, but it's not recommended. There are a number of other options available. Let's take a look at some examples. ### Examples #### Generate Storybook configuration ```bash nx g @nx/vue:storybook-configuration ui ``` This will generate Storybook configuration for the `ui` project using TypeScript for the Storybook configuration files (the files inside the `.storybook` directory, eg. `.storybook/main.ts`). #### Ignore certain paths when generating stories ```bash nx g @nx/vue:storybook-configuration ui --generateStories=true --ignorePaths=libs/ui/src/not-stories/**,**/**/src/**/*.other.*,apps/my-app/**/*.something.ts ``` This will generate a Storybook configuration for the `ui` project and generate stories for all components in the `libs/ui/src/lib` directory, except for the ones in the `libs/ui/src/not-stories` directory, and the ones in the `apps/my-app` directory that end with `.something.ts`, and also for components that their file name is of the pattern `*.other.*`. This is useful if you have a project that contains components that are not meant to be used in isolation, but rather as part of a larger component. By default, Nx will ignore the following paths: ```text *.stories.ts, *.stories.tsx, *.stories.js, *.stories.jsx, *.stories.mdx ``` but you can change this behaviour easily, as explained above. #### Generate stories using JavaScript instead of TypeScript ```bash nx g @nx/vue:storybook-configuration ui --generateStories=true --js=true ``` This will generate stories for all the components in the `ui` project using JavaScript instead of TypeScript. So, you will have `.stories.js` files next to your components. #### Generate Storybook configuration using JavaScript ```bash nx g @nx/vue:storybook-configuration ui --tsConfiguration=false ``` By default, our generator generates TypeScript Storybook configuration files. You can choose to use JavaScript for the Storybook configuration files of your project (the files inside the `.storybook` directory, eg. `.storybook/main.js`). **Usage:** ```bash nx generate @nx/vue:storybook-configuration [options] ``` **Arguments:** ```bash nx generate @nx/vue:storybook-configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--configureStaticServe` | boolean | Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times. | `true` | | `--generateStories` | boolean | Automatically generate `*.stories.ts` files for components declared in this project? | `true` | | `--ignorePaths` | array | Paths to ignore when looking for components. | `["*.stories.ts,*.stories.tsx,*.stories.js,*.stories.jsx,*.stories.mdx"]` | | `--interactionTests` | boolean | Set up Storybook interaction tests. | `true` | | `--js` | boolean | Generate JavaScript story files rather than TypeScript story files. | `false` | | `--linter` | string | The tool to use for running lint checks. | | | `--tsConfiguration` | boolean | Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js. | `true` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/vue: --help ``` --- ## Nx with Vue [Vue](https://vuejs.org/) is a progressive JavaScript framework for building user interfaces. In a Vue monorepo, Nx caches task results and runs only the tasks a change affects. The `@nx/vue` plugin adds first-class Vue support to an Nx workspace, so you can scaffold, build, and test apps and libraries. ## Requirements The `@nx/vue` plugin supports the following package versions. | Package | Supported Versions | | ------- | ------------------ | | `vue` | ^3.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setting up @nx/vue ### Generating a new workspace To create a new workspace with Vue, run `npx create-nx-workspace@latest --preset=vue`. ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/vue` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/vue` by running the following command: ```shell nx add @nx/vue ``` This will install the correct version of `@nx/vue`. ## Using the @nx/vue plugin ### Generate a new project using Vue To generate a Vue application, run the following: ```shell nx g @nx/vue:app apps/my-app ``` To generate a Vue library, run the following: ```shell nx g @nx/vue:lib libs/my-lib ``` ## Set up CI for your Vue monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/vue Migrations For an overview of the plugin and setup instructions, see the [@nx/vue introduction](/docs/technologies/vue/introduction). The @nx/vue plugin provides various migrations to help you migrate to newer versions of vue projects within your Nx workspace. Below is a complete reference for all available migrations. ## 22.0.x ### 22.0.0-package-updates **Version**: 22.0.0-beta.8 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `@vitejs/plugin-vue` | `^6.0.1` | Updated only --- ## Nuxt Get started with [Nx with Nuxt](/docs/technologies/vue/nuxt/introduction), or browse the topics below. {% index_page_cards path="technologies/vue/nuxt" /%} --- ## @nx/nuxt Generators The @nx/nuxt plugin provides various generators to help you create and configure nuxt projects within your Nx workspace. Below is a complete reference for all available generators and their options. For an overview of the plugin and setup instructions, see the [@nx/nuxt introduction](/docs/technologies/vue/nuxt/introduction). ## `application` Create a Nuxt Application for Nx. Your new Nuxt application will be generated with the following directory structure, following the suggested [directory structure](https://nuxt.com/docs/guide/directory-structure) for Nuxt applications: ```text my-nuxt-app ├── nuxt.config.ts ├── project.json ├── src │   ├── app.vue │   ├── assets │   │   └── css │   │   └── styles.css │   ├── components │   │   └── NxWelcome.vue │   ├── pages │   │   ├── about.vue │   │   └── index.vue │   ├── public │   │   └── favicon.ico │   └── server │   ├── api │   │   └── greet.ts │   └── tsconfig.json ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.spec.json └── vitest.config.ts ``` Your new app will contain the following: - Two pages (home and about) under `pages` - A component (`NxWelcome`) under `components` - A `greet` API endpoint that returns a JSON response under `/api/greet` - Configuration for `vitest` - Your app's entrypoint (`app.vue`) will contain the navigation links to the home and about pages, and the `nuxt-page` component to display the contents of your pages. ### Examples ###### Create app in a nested directory ```shell nx g @nx/nuxt:app apps/nested/myapp ``` ###### Create app with vitest configured ```shell nx g @nx/nuxt:app apps/nested/myapp --unitTestRunner=vitest ``` ###### Use plain JavaScript (not TypeScript) ```shell nx g @nx/nuxt:app apps/myapp --js ``` ### Generate pages and components You can use the the [`@nx/vue:component` generator](/nx-api/vue/generators/component) to generate new pages and components for your application. You can read more on the [`@nx/vue:component` generator documentation page](/nx-api/vue/generators/component), but here are some examples: ###### New page ```shell nx g @nx/nuxt:component my-app/src/pages/my-page ``` ###### New component ```shell nx g @nx/nuxt:component my-app/src/components/my-cmp ``` **Usage:** ```bash nx generate @nx/nuxt:application [options] ``` **Aliases:** `app` **Arguments:** ```bash nx generate @nx/nuxt:application [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--e2eTestRunner` | string | Test runner to use for end to end (E2E) tests. | `"playwright"` | | `--enableTypedLinting` | boolean | Whether to enable typed linting. For flat configs, this configures the recommended `parserOptions.projectService` and `tsconfigRootDir`. For legacy `.eslintrc` configs, this configures `parserOptions.project`. We do not enable this by default for lint performance reasons. | `false` | | `--formatter` | string | The tool to use for code formatting. | | | `--js` | boolean | Generate JavaScript files rather than TypeScript files. | `false` | | `--linter` | string | The tool to use for running lint checks. Defaults to the linter the workspace already uses. | | | `--name` | string | The name of the application. | | | `--rootProject` | boolean | Create an application at the root of the workspace. | `false` | | `--setParserOptionsProject` | boolean | Deprecated alias for `enableTypedLinting`. | `false` | | `--skipFormat` | boolean | Skip formatting files. | `false` | | `--skipPackageJson` | boolean | Do not add dependencies to `package.json`. | `false` | | `--style` | string | The file extension to be used for style files. | `"css"` | | `--tags` | string | Add tags to the application (used for linting). | | | `--unitTestRunner` | string | Test runner to use for unit tests. | `"none"` | | `--useAppDir` | boolean | Use Nuxt 4 app/ directory structure instead of src/ directory. Defaults to true for Nuxt v4, false for Nuxt v3. | | | `--useProjectJson` | boolean | Use a `project.json` configuration file instead of inlining the Nx configuration in the `package.json` file. | | ## `storybook-configuration` Set up Storybook for a Nuxt project. This generator calls the `@nx/vue:storybook-configuration` generator under the hood. It will set up Storybook for your **Nuxt** project. ```bash nx g @nx/nuxt:storybook-configuration project-name ``` You can read more about how this generator works, in the [Storybook for Vue overview page](/recipes/storybook/overview-vue#generate-storybook-configuration-for-a-vue-project). When running this generator, you will be prompted to provide the following: - The `name` of the project you want to generate the configuration for. - Whether you want to set up [Storybook interaction tests](https://storybook.js.org/docs/vue/writing-tests/interaction-testing) (`interactionTests`). If you choose `yes`, a `play` function will be added to your stories, and all the necessary dependencies will be installed. Also, a `test-storybook` target will be generated in your project's `project.json`, with a command to invoke the [Storybook `test-runner`](https://storybook.js.org/docs/vue/writing-tests/test-runner). You can read more about this in the [Nx Storybook interaction tests documentation page](/recipes/storybook/storybook-interaction-tests#setup-storybook-interaction-tests).. - Whether you want to `generateStories` for the components in your project. If you choose `yes`, a `.stories.ts` file will be generated next to each of your components in your project. You must provide a `name` for the generator to work. By default, this generator will also set up [Storybook interaction tests](https://storybook.js.org/docs/vue/writing-tests/interaction-testing). If you don't want to set up Storybook interaction tests, you can pass the `--interactionTests=false` option, but it's not recommended. There are a number of other options available. Let's take a look at some examples. ### Examples #### Generate Storybook configuration ```bash nx g @nx/nuxt:storybook-configuration ui ``` This will generate Storybook configuration for the `ui` project using TypeScript for the Storybook configuration files (the files inside the `.storybook` directory, eg. `.storybook/main.ts`). #### Ignore certain paths when generating stories ```bash nx g @nx/nuxt:storybook-configuration ui --generateStories=true --ignorePaths="libs/ui/src/not-stories/**,**/**/src/**/*.other.*,apps/my-app/**/*.something.ts" ``` This will generate a Storybook configuration for the `ui` project and generate stories for all components in the `libs/ui/src/lib` directory, except for the ones in the `libs/ui/src/not-stories` directory, and the ones in the `apps/my-app` directory that end with `.something.ts`, and also for components that their file name is of the pattern `*.other.*`. This is useful if you have a project that contains components that are not meant to be used in isolation, but rather as part of a larger component. By default, Nx will ignore the following paths: ```text *.stories.ts, *.stories.tsx, *.stories.js, *.stories.jsx, *.stories.mdx ``` but you can change this behaviour easily, as explained above. #### Generate stories using JavaScript instead of TypeScript ```bash nx g @nx/nuxt:storybook-configuration ui --generateStories=true --js=true ``` This will generate stories for all the components in the `ui` project using JavaScript instead of TypeScript. So, you will have `.stories.js` files next to your components. #### Generate Storybook configuration using JavaScript ```bash nx g @nx/nuxt:storybook-configuration ui --tsConfiguration=false ``` By default, our generator generates TypeScript Storybook configuration files. You can choose to use JavaScript for the Storybook configuration files of your project (the files inside the `.storybook` directory, eg. `.storybook/main.js`). **Usage:** ```bash nx generate @nx/nuxt:storybook-configuration [options] ``` **Arguments:** ```bash nx generate @nx/nuxt:storybook-configuration [options] ``` #### Options | Option | Type | Description | Default | |--------|------|-------------|---------| | `--configureStaticServe` | boolean | Specifies whether to configure a static file server target for serving storybook. Helpful for speeding up CI build/test times. | `true` | | `--generateStories` | boolean | Automatically generate `*.stories.ts` files for components declared in this project? | `true` | | `--ignorePaths` | array | Paths to ignore when looking for components. | `["*.stories.ts,*.stories.tsx,*.stories.js,*.stories.jsx,*.stories.mdx"]` | | `--interactionTests` | boolean | Set up Storybook interaction tests. | `true` | | `--js` | boolean | Generate JavaScript story files rather than TypeScript story files. | `false` | | `--linter` | string | The tool to use for running lint checks. | | | `--tsConfiguration` | boolean | Configure your project with TypeScript. Generate main.ts and preview.ts files, instead of main.js and preview.js. | `true` | ## Getting Help You can get help for any generator by adding the `--help` flag: ```bash nx generate @nx/nuxt: --help ``` --- ## Guides {% index_page_cards path="technologies/vue/nuxt/guides" /%} --- ## Nx with Nuxt [Nuxt](https://nuxt.com/) is a full stack web framework built on Vue. In a Nuxt monorepo, Nx caches task results and runs only the tasks a change affects. The `@nx/nuxt` plugin adds generators, task inference, and caching to your Nuxt projects. ## Requirements The `@nx/nuxt` plugin supports the following package versions. | Package | Supported Versions | | ------- | ------------------ | | `nuxt` | ^3.0.0 \|\| ^4.0.0 | [Nx generators](/docs/features/generate-code) install the latest supported versions automatically when scaffolding new projects. ## Setting up a new Nx workspace with @nx/nuxt You can create a new workspace that uses Nuxt with one of the following commands: - Generate a new monorepo with a Nuxt app ```shell npx create-nx-workspace@latest --preset=nuxt ``` ### Installation {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/nuxt` version that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} In any Nx workspace, you can install `@nx/nuxt` by running the following command: ```shell nx add @nx/nuxt ``` This will install the correct version of `@nx/nuxt`. ### How @nx/nuxt infers tasks The `@nx/nuxt` plugin will create a task for any project that has an Nuxt configuration file present. Any of the following files will be recognized as an Nuxt configuration file: - `nuxt.config.js` - `nuxt.config.ts` - `nuxt.config.mjs` - `nuxt.config.mts` - `nuxt.config.cjs` - `nuxt.config.cts` The configuration directory must also contain a `package.json` or `project.json`. ### View inferred tasks To view inferred tasks for a project, open the [project details view](/docs/concepts/mental-model#inferred-tasks) in Nx Console or run `nx show project my-project --web` in the command line. ### @nx/nuxt configuration The `@nx/nuxt/plugin` is configured in the `plugins` array in `nx.json`. ```json {% meta="{2-11}" %} // nx.json { "plugins": [ { "plugin": "@nx/nuxt/plugin", "options": { "buildTargetName": "build", "serveTargetName": "serve", "buildStaticTargetName": "build-static", "serveStaticTargetName": "serve-static", "buildDepsTargetName": "build-deps", "watchDepsTargetName": "watch-deps" } } ] } ``` | Option | Type | Default | Inferred behavior | | ----------------------- | ------ | -------------- | -------------------------------------------------------------- | | `buildTargetName` | string | `build` | Runs and caches `nuxt build`, including `.nuxt` and `.output`. | | `serveTargetName` | string | `serve` | Runs `nuxt dev` continuously. | | `buildStaticTargetName` | string | `build-static` | Runs and caches `nuxt build --prerender`. | | `serveStaticTargetName` | string | `serve-static` | Serves `dist` continuously after the static build task. | | `buildDepsTargetName` | string | none | Name of an optional dependency build task. | | `watchDepsTargetName` | string | none | Name of an optional dependency watch task. | Use `include` and `exclude` glob patterns on the plugin entry to scope inference. A [target defaults](/docs/reference/nx-json#target-defaults) `plugin` filter must use the exact identifier `@nx/nuxt/plugin`. ## Using Nuxt ### Generate a new Nuxt app ```shell nx g @nx/nuxt:app apps/my-app ``` ### Deploy a Nuxt app Once you are ready to deploy your Nuxt application, you have absolute freedom to choose any hosting provider that fits your needs. We have detailed [how to deploy your Nuxt application to Vercel in a separate guide](/docs/kb/deploy-nuxt-to-vercel). ### E2E testing By default `nuxt` **does not** generate static HTML files when you run the `build` command. However, Nx provides a `build-static` target that you can use to generate static HTML files for your Nuxt application. Essentially, this target runs the `nuxt build --prerender` command to generate static HTML files. To perform end-to-end (E2E) testing on static HTML files using a test runner like Cypress. When you create a Nuxt application, Nx automatically creates a `serve-static` target. This target is designed to serve the static HTML files produced by the `build-static` command. This feature is particularly useful for testing in continuous integration (CI) pipelines, where resources may be constrained. Unlike the `serve` target, `serve-static` does not require a Nuxt's Nitro server to operate, making it more efficient and faster by eliminating background processes, such as file change monitoring. To use the `serve-static` target for testing, run the following command: ```shell nx serve-static my-nuxt-app-e2e ``` This command performs several actions: 1. It will build the Nuxt application and generate the static HTML files. 2. It will serve the static HTML files using a simple HTTP server. 3. It will run the Cypress tests against the served static HTML files. ## Set up CI for your Nuxt monorepo In CI, Nx runs [`nx affected`](/docs/features/ci-features/affected) to rebuild and retest only the projects a change touches, and [caches](/docs/features/cache-task-results) results to skip repeated work. For a complete pipeline, see [Set up CI](/docs/getting-started/setup-ci). --- ## @nx/nuxt Migrations For an overview of the plugin and setup instructions, see the [@nx/nuxt introduction](/docs/technologies/vue/nuxt/introduction). The @nx/nuxt plugin provides various migrations to help you migrate to newer versions of nuxt projects within your Nx workspace. Below is a complete reference for all available migrations. ## 23.0.x ### `update-23-0-0-migrate-create-nodes-v2-import` **Version**: 23.0.0-beta.24 Rename imports of `createNodesV2` from `@nx/nuxt/plugin` to the canonical `createNodes` export. #### Rename `createNodesV2` imports to `createNodes` `@nx/nuxt` renamed its primary inferred-plugin export from `createNodesV2` to `createNodes`. The `createNodesV2` name is preserved as a deprecated alias for now, but new code should use `createNodes`. This migration scans every `.ts`, `.tsx`, `.cts`, and `.mts` file in your workspace and rewrites named imports and re-exports of `createNodesV2` from `@nx/nuxt/plugin` to `createNodes`. #### Sample Code Changes ##### Before ```ts import { createNodesV2 } from '@nx/nuxt/plugin'; ``` ##### After ```ts import { createNodes } from '@nx/nuxt/plugin'; ``` Aliases are preserved (`createNodesV2 as cn` becomes `createNodes as cn`), and if a file already imports both names (`{ createNodes, createNodesV2 }`) the redundant binding is dropped. #### What is not rewritten Only static `import`/`export` named bindings from `@nx/nuxt/plugin` are rewritten. Namespace imports, dynamic `import(...)`, `require(...)` destructuring, and property access such as `plugin.createNodesV2` are left untouched — they keep working through the `createNodesV2` runtime alias. Update those by hand if you want to drop the deprecated name everywhere. ## 22.2.x ### `update-22-2-0-create-ai-instructions-for-nuxt-4` **Version**: 22.2.0-beta.0 Create AI Instructions to help migrate workspaces to Nuxt 4. ### 22.2.0-package-updates **Version**: 22.2.0-beta.0 #### Packages The following packages will be updated: | Name | Version | Always add to `package.json` |---------|----------|---------| | `nuxt` | `^4.0.0` | Updated only | `@nuxt/eslint-config` | `^1.10.0` | Updated only | `@nuxt/kit` | `^4.0.0` | Updated only | `@nuxt/schema` | `^4.0.0` | Updated only | `@nuxt/devtools` | `^3.0.0` | Updated only # Reference --- ## References {% index_page_cards path="reference" /%} --- ## Angular For setup and guides, see [Nx with Angular](/docs/technologies/angular/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Angular" /%} --- ## Benchmarks {% index_page_cards path="reference/benchmarks" /%} --- ## Code Formatting Reference Nx formats code when you run `nx format:write` or `nx format:check` and at the end of most generators and migrations. All of them use the same formatter, which Nx detects from your workspace. Two formatters are supported: [oxfmt](https://oxc.rs) and [Prettier](https://prettier.io). Prettier is the default wherever workspace creation asks which one to use, and oxfmt is offered as experimental while it is pre-1.0. ## How Nx picks a formatter Detection runs in this order, and the first match wins: 1. An oxfmt config file exists at the workspace root. 2. A Prettier config file exists at the workspace root, or `package.json` has a `prettier` key. 3. `package.json` declares an `oxfmt` dependency. 4. `package.json` declares a `prettier` dependency. Steps 3 and 4 read the root `package.json` only. A formatter that reaches `node_modules` as a transitive dependency doesn't count. If nothing matches, `nx format:write` and `nx format:check` warn and exit `0`, and generators skip formatting. The oxfmt config files Nx looks for: - `.oxfmtrc.json` - `.oxfmtrc.jsonc` - `oxfmt.config.ts` - `oxfmt.config.mts` ### When both are configured oxfmt wins, and Nx warns once so the switch is not silent. Adding an `.oxfmtrc.json` to a workspace that already has a `.prettierrc` switches the whole workspace over, including generator formatting. To go back, delete the oxfmt config. ## Switch an existing workspace to oxfmt ```shell npm add -D oxfmt ``` ```json {% title=".oxfmtrc.json" %} { "singleQuote": true, "printWidth": 80 } ``` These match an Nx-generated `.prettierrc`. If you wrote your own, match what it set instead. oxfmt defaults to double quotes and a width of 100. oxfmt also sorts `package.json` keys, which Prettier does not. Expect every manifest in the workspace to be reordered on the first run. Set `"sortPackageJson": false` in `.oxfmtrc.json` to keep your current order. Then delete `.prettierrc` and run `nx format:write --all` to reformat everything in one commit. Keep `.prettierignore` if you have one. oxfmt reads it too, so deleting it starts formatting files you had excluded. ## Generator formatting and `nx format` differ `nx format:write` runs the formatter's own CLI. Generators format files that exist only in memory, so Nx applies the configuration itself. With oxfmt, both resolve from each file's own directory upwards: the nearest oxfmt config, its `overrides` and `ignorePatterns`, and every `.gitignore` and `.prettierignore` along the way. With Prettier, both read the root ignore files only, so a nested `.prettierignore` has no effect either way. A nested oxfmt config replaces the one above it rather than merging with it, so any option it leaves out falls back to the oxfmt default and not to the parent's value. `.editorconfig` is the exception. oxfmt looks it up once from the directory it runs in rather than from each file upwards. For `nx format` that directory is the one you run the command from; generators anchor the lookup at the workspace root, so a nested `.editorconfig` never affects generated files. The lookup walks up until a file sets `root = true`, so a checkout containing your workspace can contribute settings. A root `.editorconfig` that sets `indent_size` without `indent_style` also leaves the two disagreeing. Generator formatting applies it and `nx format:write` ignores it, so your generated files fail your own `nx format:check`. Set `indent_style` alongside it. ## Skip formatting Set [`NX_SKIP_FORMAT=true`](/docs/reference/environment-variables) to stop generators and migrations from formatting. It also covers the files `nx init` and `nx import` write, and the formatting pass at the end of `create-nx-workspace`. TypeScript path sorting is separate and stays off by default. Turn it on with `NX_FORMAT_SORT_TSCONFIG_PATHS=true`, or per call with `formatFiles`' `sortRootTsconfigPaths` option. ```shell NX_SKIP_FORMAT=true nx g @nx/react:app my-app ``` ## Choose a formatter at workspace creation `create-nx-workspace` asks which formatter you want on every preset that sets one up: a stack (React, Vue, Angular, or Node), `web-components`, the TypeScript monorepo, and `ts-standalone`. Whether the workspace uses package manager workspaces makes no difference. The `apps` and `npm` presets generate no project, so they set up no formatter unless you pass `--formatter`, and neither does `@nx/workspace:new` run directly. Pass `--formatter` on any path to choose explicitly: ```shell npx create-nx-workspace@latest my-workspace --formatter=prettier ``` The accepted values are `oxfmt`, `prettier`, and `none`. --- ## Conformance For setup and guides, see [Run Language-Agnostic Conformance Rules](/docs/enterprise/conformance). The pages below are the API reference. {% index_page_cards path="reference/conformance" /%} --- ## Create a Conformance Rule For local conformance rules, the resolution utilities from `@nx/js` are used in the same way they are for all other JavaScript/TypeScript files in Nx. Therefore, you can simply reference an adhoc JavaScript file or TypeScript file in your `"rule"` property (as long as the path is resolvable based on your package manager and/or tsconfig setup), and the rule will be loaded/transpiled as needed. The rule implementation file should also have a `schema.json` file next to it that defines the available rule options, if any. In practice, writing your local conformance rules in an Nx generated library is the easiest way to organize them and ensure that they are easily resolvable via TypeScript. The library in question could also be an Nx plugin, but it does not have to be. ## Generate a conformance rule To write your own conformance rule, run the `@nx/conformance:create-rule` generator and answer the prompts. ```text {% title="nx g @nx/conformance:create-rule" frame="terminal" %} NX Generating @nx/conformance:create-rule ✔ What is the name of the rule? · local-conformance-rule-example ✔ Which directory do you want to create the rule directory in? · packages/my-plugin/local-conformance-rule ✔ What category does this rule belong to? · security ✔ What is the description of the rule? · an example of a conformance rule CREATE packages/my-plugin/local-conformance-rule/local-conformance-rule-example/index.ts CREATE packages/my-plugin/local-conformance-rule/local-conformance-rule-example/schema.json ``` The generated rule definition file should look like this: ```ts // packages/my-plugin/local-conformance-rule/index.ts import { createConformanceRule, ConformanceViolation } from '@nx/conformance'; export default createConformanceRule({ name: 'local-conformance-rule-example', category: 'security', description: 'an example of a conformance rule', implementation: async (context) => { const violations: ConformanceViolation[] = []; return { severity: 'low', details: { violations, }, }; }, }); ``` To enable the rule, you need to register it in the `nx.json` file. ```json // nx.json { "conformance": { "rules": [ { "rule": "./packages/my-plugin/local-conformance-rule/index.ts" } ] } } ``` Note that the severity of the error is defined by the rule author and can be adjusted based on the specific violations that are found. ## Understanding rule context The implementation function of the rule is passed a `context` object which contains: - **`tree`**: A `ReadOnlyConformanceTree` that can be used to read files from the workspace instead of directly from disk. Useful for unit testing rules as a test tree can be provided to the rule implementation (see [Testing Conformance Rules](/docs/reference/conformance/test-conformance-rule)). - **`projectGraph`**: The Nx project graph - **`fileMapCache`**: The Nx file map cache - **`ruleOptions`**: The resolved rule configuration options based on the current workspace ## Violation interface Violations must follow this interface: ```typescript interface ConformanceViolation { message: string; file?: string; // Used if the violation is attributed to a specific file sourceProject?: string; // Used if the violation is attributed to a specific project workspaceViolation?: boolean; // Used if the violation is attributed to the entire workspace } ``` ### Automatic project inference ```typescript violations.push({ message: 'File violates standards', file: 'libs/my-lib/src/problematic.ts', // sourceProject auto-inferred as 'my-lib' }); ``` ### Best practices for violations 1. **Use `workspaceViolation: true`** for issues affecting the entire workspace (global configs, workspace structure, etc.) 2. **Use `sourceProject` only** for project-wide issues (missing configuration, structure problems) 3. **Use `file` (and optional explicit `sourceProject`)** for violations tied to specific files in projects 4. **Use `file` only** for files that may not belong to projects (CI configs, root files, etc.) ## Conformance rule examples The following examples demonstrate how to write rules that report violations at different scopes. {% tabs %} {% tabitem label="workspace violation" %} This rule checks to see if there is a root README.md file in the workspace, and if there is not, it reports on the workspace itself. ```ts import { workspaceRoot } from '@nx/devkit'; import { createConformanceRule, ConformanceViolation } from '@nx/conformance'; import { join } from 'node:path'; import { existsSync } from 'node:fs'; export default createConformanceRule({ name: 'readme-file', category: 'maintainability', description: 'The workspace should have a root README.md file', implementation: async () => { const violations: ConformanceViolation[] = []; const readmePath = join(workspaceRoot, 'README.md'); if (!existsSync(readmePath)) { violations.push({ message: 'The workspace should have a root README.md file', workspaceViolation: true, }); } return { severity: 'low', details: { violations, }, }; }, }); ``` {% /tabitem %} {% tabitem label="project violation" %} The `@nx/conformance:ensure-owners` rule provides us an example of how to write a rule that reports on a project being in violation of the rule. The `@nx/owners` plugin adds an `owners` metadata property to every project node that has an owner in the project graph. This rule checks each project node metadata to make sure that each project has some owner defined. ```ts import { ProjectGraphProjectNode } from '@nx/devkit'; import { createConformanceRule, ConformanceViolation } from '@nx/conformance'; export default createConformanceRule({ name: 'ensure-owners', category: 'consistency', description: 'Ensure that all projects have owners defined via Nx Owners.', implementation: async (context) => { const violations: ConformanceViolation[] = []; for (const node of Object.values( context.projectGraph.nodes ) as ProjectGraphProjectNode[]) { const metadata = node.data.metadata; if (!metadata?.owners || Object.keys(metadata.owners).length === 0) { violations.push({ sourceProject: node.name, message: `This project currently has no owners defined via Nx Owners.`, }); } } return { severity: 'medium', details: { violations, }, }; }, }); ``` {% /tabitem %} {% tabitem label="file violation" %} This rule uses TypeScript AST processing to ensure that `index.ts` files use a client-side style of export syntax and `server.ts` files use a server-side style of export syntax. ```ts import { createConformanceRule, ConformanceViolation } from '@nx/conformance'; import { existsSync, readFileSync } from 'node:fs'; import { join } from 'node:path'; import { createSourceFile, isExportDeclaration, isStringLiteral, isToken, ScriptKind, ScriptTarget, } from 'typescript'; export default createConformanceRule({ name: 'server-client-public-api', category: 'consistency', description: 'Ensure server-only and client-only public APIs are not mixed', implementation: async ({ projectGraph }) => { const violations: ConformanceViolation[] = []; for (const nodeId in projectGraph.nodes) { const node = projectGraph.nodes[nodeId]; const sourceRoot = node.data.root; const indexPath = join(sourceRoot, 'src/index.ts'); const serverPath = join(sourceRoot, 'src/server.ts'); if (existsSync(indexPath)) { const fileContent = readFileSync(indexPath, 'utf8'); violations.push( ...processEntryPoint(fileContent, indexPath, nodeId, 'client') ); } if (existsSync(serverPath)) { const fileContent = readFileSync(serverPath, 'utf8'); violations.push( ...processEntryPoint(fileContent, serverPath, nodeId, 'server') ); } } return { severity: 'medium', details: { violations }, }; }, }); export function processEntryPoint( fileContent: string, entryPoint: string, project: string, style: 'server' | 'client' ) { const violations: ConformanceViolation[] = []; const sf = createSourceFile( entryPoint, fileContent, ScriptTarget.Latest, true, ScriptKind.TS ); let hasNotOnlyExports = false; sf.forEachChild((node) => { if (isExportDeclaration(node)) { const moduleSpecifier = node.moduleSpecifier && isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.getText() : ''; if (isModuleSpecifierViolated(moduleSpecifier, style)) { if ( violations.find( (v) => v.file === entryPoint && v.sourceProject === project ) ) { // we already have a violation for this file and project, so we don't need to add another one return; } violations.push({ message: style === 'client' ? 'Client-side only entry point cannot export from server-side modules' : 'Server-side only entry point can only export server-side modules ', file: entryPoint, sourceProject: project, }); } } else if (isToken(node) && node === sf.endOfFileToken) { // do nothing } else { hasNotOnlyExports = true; } }); if (hasNotOnlyExports) { violations.push({ message: `Entry point should only contain exported APIs`, file: entryPoint, sourceProject: project, }); } return violations; } function isModuleSpecifierViolated( moduleSpecifier: string, style: 'server' | 'client' ) { // should not get here. if this is the case, it's a grammar error in the source code. if (!moduleSpecifier) return false; if (style === 'server' && !moduleSpecifier.includes('.server')) { return true; } if (style === 'client' && moduleSpecifier.includes('.server')) { return true; } return false; } ``` {% /tabitem %} {% /tabs %} ## Auto-fixing violations with fix generators Rules can optionally implement a `fixGenerator` function that will be used to automatically fix violations. ### When fix generators run - **`nx conformance`**: Evaluates rules and applies any available fix generators. Changes are written to disk and rules are evaluated once more to calculate how many violations were fixed. - **`nx conformance:check`**: Evaluates rules only. Fix generators are not applied (useful for CI). Fix generators are only ever applied for rules whose final status is not `disabled`. ### Fix generator function signature Fix generators are essentially standard Nx generators. They receive a `WritableConformanceTree` (an extension of the `FsTree` used in other Nx generators) and a schema containing violations, rule options, and optional extra data exposed by the rule implementation via `result.details.fixGeneratorData`. ```typescript type ConformanceRuleFixGenerator = ( tree: WritableConformanceTree, schema: { violations: ConformanceViolation[]; ruleOptions: RuleOptions; fixGeneratorData?: Record; } ) => Promise | void; ``` During rule evaluation (diagnostics phase) the `tree` is read-only. During the fix phase, the `tree` is writable for generators to modify files. ### Passing data from rules to fix generators There are two supported ways to pass data from your rule implementation to its fix generator: 1. **Per-violation data**: The exact `details.violations` array returned by your rule is provided to the fix generator. Each violation can provide `fixGeneratorData` for targeted fixes. ```typescript violations.push({ message: 'Missing license header', file: 'libs/my-lib/src/index.ts', fixGeneratorData: { header: '/* LICENSE */\n', missing: true }, }); ``` Note: Before final results are reported, any `fixGeneratorData` fields are stripped out. 2. **Global data**: Put shared data on `details.fixGeneratorData`. If present, it will be passed as `schema.fixGeneratorData` to the fix generator and then stripped from the final report. ```typescript return { severity: 'low', details: { violations, fixGeneratorData: { dryRun: ruleOptions.addHeader === false }, }, }; ``` Useful for expensive precomputed lookups or workspace-wide context that applies to all violations. ### Additional notes - **Project filtering**: If the rule is configured with `projects`, the runner filters the violations accordingly before calling the fix generator. The generator receives only the filtered set. - **File-to-project inference**: If a violation specifies `file` but not `sourceProject`, the runner attempts to infer the owning project and will include that on the violation provided to the fix generator where possible. - **Data privacy**: Both `details.fixGeneratorData` and per-violation `fixGeneratorData` are never included in the emitted report. They are only available to the fix generator. ### Example conformance rule with fix generator ```typescript import { createConformanceRule } from '@nx/conformance'; import type { ConformanceViolation } from '@nx/conformance'; type RuleOptions = { addHeader: boolean; }; export default createConformanceRule({ name: 'license-header', category: 'maintainability', description: 'Ensure files contain a license header', implementation: async ({ tree, ruleOptions }) => { const violations: ConformanceViolation[] = []; for (const filePath of tree.children('libs/my-lib/src')) { if (!filePath.endsWith('.ts')) continue; const contents = tree.read(filePath, 'utf-8') ?? ''; if (!contents.startsWith('/* LICENSE */')) { violations.push({ message: 'Missing license header', file: `libs/my-lib/src/${filePath}`, fixGeneratorData: { header: '/* LICENSE */\n', missing: true }, }); } } return { severity: 'low', details: { violations, fixGeneratorData: { dryRun: ruleOptions.addHeader === false }, }, }; }, fixGenerator: async (tree, { violations, ruleOptions, fixGeneratorData }) => { if (fixGeneratorData?.dryRun) return; for (const v of violations) { if (!('file' in v) || !v.file) continue; const header = (v as any).fixGeneratorData?.header ?? '/* LICENSE */\n'; const existing = tree.read(v.file, 'utf-8') ?? ''; if (!existing.startsWith(header) && ruleOptions.addHeader !== false) { tree.write(v.file, header + existing); } } }, }); ``` ### Best practices for fix generators - **Idempotent**: Generators should be safe to run multiple times without changing files after the first successful run. - **Minimal changes**: Modify only what is necessary to address the reported violations. - **Respect options**: Honor `ruleOptions` so users can tune behavior. - **Avoid re-discovery**: Prefer using the provided `violations` and optional `fixGeneratorData` rather than rescanning the workspace. - **Clear boundaries**: Keep heavy computation inside the rule implementation and pass the results via `fixGeneratorData` to the generator. ## Share conformance rules across workspaces If you have an Enterprise Nx Cloud contract, you can share your conformance rules across every repository in your organization. Read more in these articles: - [Publish Conformance Rules to Nx Cloud](/docs/enterprise/publish-conformance-rules-to-nx-cloud) - [Configure Conformance Rules in Nx Cloud](/docs/enterprise/configure-conformance-rules-in-nx-cloud) --- ## @nx/conformance - Executors ## `bundle-rules` Compile and bundle one or more Nx Conformance Rules ### Options | Options | Type | Description | Default | | --------------------------- | ------ | -------------------------------------------------------------------------------------------- | ------------------------------- | | `outputPath` **[required]** | string | The output path for the bundled rules to be written to. | | | `rulesDirectory` | string | The directory, relative to the workspace root, containing the rule subdirectories to bundle. | Defaults to `{projectRoot}/src` | --- ## @nx/conformance - Generators ## `create-rule` Create a new conformance rule **Usage:** ```shell nx g @nx/conformance:create-rule [options] ``` | Options | Type | Description | Default | | ------------- | ----------------------------------------------------------------------------- | ------------------------------------------------ | ------------------------- | | `category` | `consistency`, `maintainability`, `reliability`, or `security` **[required]** | The category of the rule | | | `directory` | string **[required]** | A directory where the rule directory is created. | `{projectRoot}/src/rules` | | `name` | string **[required]** | The name of the rule. | | | `description` | string | The description of the rule. | | ## `preset` Generate a Nx workspace with a working conformance rule **Usage:** ```shell npx create-nx-workspace@latest --preset=@nx/conformance ``` --- ## Overview of the Nx Conformance Plugin The `@nx/conformance` plugin allows [Nx Enterprise](https://nx.dev/enterprise) users to write and apply rules for your entire workspace that help with **consistency**, **maintainability**, **reliability** and **security**. The conformance plugin allows you to encode your own organization's standards so that they can be enforced automatically. Conformance rules can also complement linting tools by enforcing that those tools are configured in the recommended way. The rules are written in TypeScript but can be applied to any language in the codebase or focus entirely on configuration files. For setup and guides, see [Run Language-Agnostic Conformance Rules](/docs/enterprise/conformance). The plugin also provides the following pre-written rules: - [**Enforce Project Boundaries**](#enforce-project-boundaries): Similar to the Nx [ESLint Enforce Module Boundaries rule](/docs/features/enforce-module-boundaries), but enforces the boundaries on every project dependency, not just those created from TypeScript imports or `package.json` dependencies. - [**Ensure Owners**](#ensure-owners): Require every project to have an owner defined for the [`@nx/owners` plugin](/docs/reference/owners) {% aside type="note" title="This plugin requires an active Nx Enterprise license" %} In order to use `@nx/conformance`, you need to have an active [Nx Enterprise license](https://nx.dev/enterprise). If you don't have a license or it has expired, the `nx conformance` command will fail. {% /aside %} ## Set up @nx/conformance 1. [Activate your license](/docs/enterprise/activate-license) if you haven't already 2. Install the package ```shell nx add @nx/conformance ``` 3. Configure Conformance Rules Configure the `@nx/conformance` plugin in the `nx.json` file or in individual project configuration files. Consult the [Conformance Configuration Reference](#conformance-configuration-reference) section for more details. 4. Run the `nx conformance` or `nx conformance:check` command The `@nx/conformance` plugin enables two commands: - **`nx conformance`**: Evaluates rules and applies any available fix generators. Changes are written to disk and rules are evaluated once more to calculate how many violations were fixed. Use this locally while working on feature branches. - **`nx conformance:check`**: Evaluates rules only. Fix generators are not applied. Use this in CI to enforce conformance rules for every PR. {% tabs %} {% tabitem label="Local Development" %} ```shell npx nx conformance ``` {% /tabitem %} {% tabitem label="CI without Nx Cloud" %} ```yaml - name: Enforce all conformance rules run: npx nx conformance:check ``` {% /tabitem %} {% tabitem label="CI with Nx Cloud" %} ```yaml - name: Enforce all conformance rules run: npx nx record -- npx nx conformance:check ``` Use `npx nx record --` to capture the logs for `nx conformance:check` in the Nx Cloud dashboard. {% /tabitem %} {% /tabs %} ## Conformance configuration reference ```jsonc // nx.json { "conformance": { "rules": [ { /** * Relative path to a local rule implementation or node_module path. */ "rule": "@nx/conformance/enforce-project-boundaries", /** * Rule specific configuration options. (Optional) */ "options": {}, /** * The projects array allows users to opt in or out of violations for specific projects being reported by the current rule. * The array can contain any valid matchers for findMatchingProjects(), by default the implied value is ["*"]. (Optional) */ "projects": ["*"], }, ], }, } ``` ## Provided conformance rules The following rules are provided by Nx along with the `@nx/conformance` plugin. ### Enforce project boundaries This rule is similar to the Nx [ESLint Enforce Module Boundaries rule](/docs/kb/enforce-module-boundaries), but enforces the boundaries on every project dependency, not just those created from TypeScript imports or `package.json` dependencies. Set the `rule` property to: `@nx/conformance/enforce-project-boundaries` ```json // nx.json { "conformance": { "rules": [ { "rule": "@nx/conformance/enforce-project-boundaries", "options": { // Optional // Can be a boolean or an object with an array of buildTargetNames "requireBuildableDependenciesForBuildableProjects": { // Defaults to ["build"] "buildTargetNames": ["build", "compile"] }, // Optional "ignoredCircularDependencies": [["projectA", "projectB"]], // Optional "depConstraints": [ { // Must define either `sourceTag` or `allSourceTags` "sourceTag": "string", "allSourceTags": ["string"], // Optional "onlyDependOnProjectsWithTags": [], // Optional "notDependOnProjectsWithTags": [] } ] } } ] } } ``` #### Options | Property | Type | Default | Description | | ------------------------------------------------ | ------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ignoredCircularDependencies | _Array<[string, string]>_ | _[]_ | List of project pairs that should be skipped from `Circular dependencies` checks, including the self-circular dependency check. E.g. `['feature-project-a', 'myapp']`. Project name can be replaced by catch all `*` for more generic matches. | | requireBuildableDependenciesForBuildableProjects | _boolean_ | _false_ | Enable to restrict the buildable projects from importing non-buildable libraries | | depConstraints | _Array_ | _[]_ | List of dependency constraints between projects | #### Dependency constraints The `depConstraints` is an array of objects representing the constraints defined between source and target projects. A constraint must include `sourceTag` or `allSourceTags`. The constraints are applied with **AND** logical operation - for a given `source` project the resulting constraints would be **all** that match its tags. | Property | Type | Description | | ---------------------------- | --------------- | ---------------------------------------------------------------------------------- | | sourceTag | _string_ | Tag that source project must contain to match the constraint | | allSourceTags | _Array_ | List of tags the source project must contain to match the constraint | | onlyDependOnProjectsWithTags | _Array_ | The source **can depend only** on projects that contain at least one of these tags | | notDependOnProjectsWithTags | _Array_ | The source **can not depend** on projects that contain at least one of these tags | ### Ensure owners This rule requires every project to have an owner defined for the [`@nx/owners` plugin](/docs/reference/owners) Set the `rule` property to: `@nx/conformance/ensure-owners` ```json // nx.json { "conformance": { "rules": [ { "rule": "@nx/conformance/ensure-owners" } ] } } ``` ## Next steps For more information about the conformance plugin, consult the following articles: - [Create a Conformance Rule](/docs/reference/conformance/create-conformance-rule) - [Testing Conformance Rules](/docs/reference/conformance/test-conformance-rule) - [Publish Conformance Rules to Nx Cloud](/docs/enterprise/publish-conformance-rules-to-nx-cloud) - [Configure Conformance Rules in Nx Cloud](/docs/enterprise/configure-conformance-rules-in-nx-cloud) --- ## Test a Conformance Rules The `@nx/conformance/testing` package provides utilities for testing conformance rules in a controlled environment. It's recommended for all conformance rule tests to follow the same pattern using `createReadOnlyTree` and `createStubbedProjectGraphAndFileMapCache` when setting up test workspaces: ```typescript import type { ReadOnlyConformanceTree } from '@nx/conformance'; import { applyProjectNodesAndFiles, createReadOnlyTree, createStubbedProjectGraphAndFileMapCache, } from '@nx/conformance/testing'; import type { ProjectGraph } from '@nx/devkit'; import type { FileMapCache } from 'nx/src/project-graph/nx-deps-cache'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import rule from './index'; describe('my-rule', () => { let tree: ReadOnlyConformanceTree; let projectGraph: ProjectGraph; let fileMapCache: FileMapCache; let cleanup: () => void; beforeEach(async () => { // Create the test workspace in a temporary directory and return a read-only tree and a cleanup function ({ tree, cleanup } = await createReadOnlyTree()); // Prepare a stubbed project graph and file map cache for the test workspace ({ projectGraph, fileMapCache } = await createStubbedProjectGraphAndFileMapCache(tree, [ // Optionally add projects and files to the tree, project graph and file map cache if the rule implementation needs them // This can also be done later in specific tests using the `applyProjectNodesAndFiles` function ])); }); // Invoke the cleanup function to remove the generated temporary directory after each test afterEach(() => cleanup()); it('should return a violation when something specific happens', async () => { // Optionally add projects and files to the tree, project graph and file map cache if the rule implementation needs them applyProjectNodesAndFiles(tree, projectGraph, fileMapCache, [ { projectNode: { name: 'my-lib', type: 'lib', data: { root: 'libs/my-lib', }, }, projectFiles: [ // If our specific rule needs to know about project graph dependencies, we add them like so // In this example this entry causes my-lib to depend on my-app on the project graph and have it correctly attributed to my-lib in the file map cache { projectRootRelativeFile: 'src/index.ts', depsItCreates: ['my-app'], }, ], }, { projectNode: { name: 'my-app', type: 'app', data: { root: 'apps/my-app', }, }, }, ]); const result = await rule.implementation({ tree, projectGraph, fileMapCache, ruleOptions: {}, }); expect(result.details.violations).toMatchInlineSnapshot(` // YOUR SNAPSHOT HERE `); }); }); ``` ## Adding files to the test workspace You can optionally provide an async callback to `createReadOnlyTree` to add files or even run Nx generators before the tests run: ```typescript const { tree, cleanup } = await createReadOnlyTree(async (writableTree) => { // Add files to the tree before it becomes read-only writableTree.write( 'libs/my-lib/custom-config.json', JSON.stringify({ setting: 'value' }) ); // Run some Nx generator await libraryGenerator(writableTree, { // generator options }); }); ``` ## Testing fix generators If your rule includes a fix generator, you can test it by converting the read-only tree to a writable one using `convertToWritable` and then running the fix generator: ```typescript import { convertToWritable, convertToReadOnly } from '@nx/conformance/testing'; it('should fix violations when fix generator is applied', async () => { // ... setup and rule implementation test ... // Convert to a WritableConformanceTree and run fix generator const writableTree = convertToWritable(tree); await rule.fixGenerator(writableTree, { violations: result.details.violations, ruleOptions: {}, }); const resultAfterFix = await rule.implementation({ // Convert back to a ReadOnlyConformanceTree for the rule implementation and verify the fix worked tree: convertToReadOnly(writableTree), projectGraph, fileMapCache, ruleOptions: {}, }); expect(resultAfterFix.details.violations).toMatchInlineSnapshot(`[]`); }); ``` ## Complete example Here's a complete example testing a rule that checks for license headers and includes a fix generator: ```typescript import type { ReadOnlyConformanceTree } from '@nx/conformance'; import { applyProjectNodesAndFiles, createReadOnlyTree, createStubbedProjectGraphAndFileMapCache, convertToWritable, convertToReadOnly, } from '@nx/conformance/testing'; import type { ProjectGraph } from '@nx/devkit'; import type { FileMapCache } from 'nx/src/project-graph/nx-deps-cache'; import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import rule from './license-header-rule'; describe('license-header-rule', () => { let tree: ReadOnlyConformanceTree; let projectGraph: ProjectGraph; let fileMapCache: FileMapCache; let cleanup: () => void; beforeEach(async () => { ({ tree, cleanup } = await createReadOnlyTree(async (writableTree) => { // Create a test file without a license header writableTree.write( 'libs/my-lib/src/index.ts', 'export const foo = "bar";' ); })); ({ projectGraph, fileMapCache } = await createStubbedProjectGraphAndFileMapCache(tree, [ { projectNode: { name: 'my-lib', type: 'lib', data: { root: 'libs/my-lib', }, }, }, ])); }); afterEach(() => cleanup()); it('should detect missing license header', async () => { const result = await rule.implementation({ tree, projectGraph, fileMapCache, ruleOptions: { addHeader: true }, }); expect(result.details.violations).toHaveLength(1); expect(result.details.violations[0]).toMatchObject({ message: 'Missing license header', file: 'libs/my-lib/src/index.ts', }); }); it('should fix missing license header when fix generator is applied', async () => { const result = await rule.implementation({ tree, projectGraph, fileMapCache, ruleOptions: { addHeader: true }, }); expect(result.details.violations).toHaveLength(1); // Apply the fix const writableTree = convertToWritable(tree); await rule.fixGenerator(writableTree, { violations: result.details.violations, ruleOptions: { addHeader: true }, }); // Verify the fix worked const resultAfterFix = await rule.implementation({ tree: convertToReadOnly(writableTree), projectGraph, fileMapCache, ruleOptions: { addHeader: true }, }); expect(resultAfterFix.details.violations).toHaveLength(0); // Verify the file content const fixedContent = writableTree.read('libs/my-lib/src/index.ts', 'utf-8'); expect(fixedContent).toContain('/* LICENSE */'); }); }); ``` ## Conformance testing best practices 1. **Use snapshots**: Use `toMatchInlineSnapshot()` for violation arrays to easily see changes in test output. 2. **Test isolation**: Always use cleanup in the `afterEach` (or your testing framework equivalent) to ensure tests don't interfere with each other. 3. **Test both detection and fixes**: If your rule has a fix generator, test both that violations are detected and that the fix works correctly. 4. **Test edge cases**: Test with empty workspaces, missing files, and various project configurations. ### Writing performant conformance rules Each rule will show its respective execution time and you can use this to identify rules that are slow to run. #### Avoid blocking the main thread Sometimes you may notice that a rule that seems to take a while to complete when run as part of the full rule set but is much faster when run individually. This is because another rule is blocking the main thread. Conformance rules are executed in parallel on the main thread so it is important to avoid blocking actions in a particular rule or it will impact the execution of others. ## Available conformance testing utilities The `@nx/conformance/testing` package exports the following utilities: ### `createReadOnlyTree(callback?)` Creates a read-only tree with a basic Nx workspace structure. Returns `Promise<{ tree, cleanup }>`. **Parameters:** - `callback` (optional): An async function that receives a writable tree before it becomes read-only. Use this to add files or run generators. **Returns:** - `tree`: A `ReadOnlyConformanceTree` instance - `cleanup`: A function to call in `afterEach` to remove the temporary directory ### `createStubbedProjectGraphAndFileMapCache(tree, projectNodesWithFiles)` Creates a stubbed project graph and file map cache for testing. Returns `Promise<{ projectGraph, fileMapCache }>`. **Parameters:** - `tree`: The `ReadOnlyConformanceTree` from `createReadOnlyTree` - `projectNodesWithFiles`: An array of `ProjectNodesWithFiles` to initialize the graph with (can be empty) **Returns:** - `projectGraph`: A stubbed `ProjectGraph` instance - `fileMapCache`: A stubbed `FileMapCache` instance ### `applyProjectNodesAndFiles(tree, projectGraph, fileMapCache, projectNodesWithFiles)` Adds projects and files to the test workspace, updating both the tree and the project graph. **Parameters:** - `tree`: The `ReadOnlyConformanceTree` - `projectGraph`: The `ProjectGraph` to update - `fileMapCache`: The `FileMapCache` to update - `projectNodesWithFiles`: An array of `ProjectNodesWithFiles` describing projects and their files to add ### `convertToWritable(tree)` Converts a `ReadOnlyConformanceTree` to a `WritableConformanceTree` for fix generator testing. **Parameters:** - `tree`: The `ReadOnlyConformanceTree` to convert **Returns:** A `WritableConformanceTree` instance ### `convertToReadOnly(tree)` Converts a `WritableConformanceTree` back to a `ReadOnlyConformanceTree`. **Parameters:** - `tree`: The `WritableConformanceTree` to convert **Returns:** A `ReadOnlyConformanceTree` instance ### `type ProjectNodesWithFiles` A type that represents the projects and files to be added to the test workspace: ```typescript type ProjectNodesWithFiles = { projectNode: { name: string; type: string; data: { root: string; // ... other project data }; }; projectFiles?: Array<{ projectRootRelativeFile: string; depsItCreates?: string[]; }>; }; ``` --- ## Cypress For setup and guides, see [Nx with Cypress](/docs/technologies/test-tools/cypress/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Cypress" /%} --- ## Deprecated {% index_page_cards path="reference/deprecated" /%} --- ## affected:graph - CLI command **Deprecated:** Use `nx graph --affected`, or `nx affected --graph` instead depending on which best suits your use case. The `affected:graph` command was removed in Nx 19. Graph dependencies affected by changes ## Usage ```shell nx affected:graph ``` Install `nx` globally to invoke the command directly using `nx`, or use `npx nx`, `yarn nx`, or `pnpm nx`. ### Examples Open the project graph of the workspace in the browser, and highlight the projects affected by changing the index.ts file: ```shell nx affected:graph --files=libs/mylib/src/index.ts ``` Open the project graph of the workspace in the browser, and highlight the projects affected by the changes between main and HEAD (e.g., PR): ```shell nx affected:graph --base=main --head=HEAD ``` Save the project graph of the workspace in a json file, and highlight the projects affected by the changes between main and HEAD (e.g., PR): ```shell nx affected:graph --base=main --head=HEAD --file=output.json ``` Generate a static website with project graph data in an html file, highlighting the projects affected by the changes between main and HEAD (e.g., PR): ```shell nx affected:graph --base=main --head=HEAD --file=output.html ``` Open the project graph of the workspace in the browser, and highlight the projects affected by the last commit on main: ```shell nx affected:graph --base=main~1 --head=main ``` Open the project graph of the workspace in the browser, highlight the projects affected, but exclude project-one and project-two: ```shell nx affected:graph --exclude=project-one,project-two ``` ## Options ### base Type: `string` Base of the current branch (usually main) ### exclude Type: `string` Exclude certain projects from being processed ### file Type: `string` Output file (e.g. --file=output.json or --file=dep-graph.html) ### files Type: `string` Change the way Nx is calculating the affected command by providing directly changed files, list of files delimited by commas or spaces ### focus Type: `string` Use to show the project graph for a particular project and every node that is either an ancestor or a descendant. ### groupByFolder Type: `boolean` Group projects by folder in the project graph ### head Type: `string` Latest commit of the current branch (usually HEAD) ### help Type: `boolean` Show help ### host Type: `string` Bind the project graph server to a specific ip address. ### open Type: `boolean` Default: `true` Open the project graph in the browser. ### port Type: `number` Bind the project graph server to a specific port. ### targets Type: `string` The target to show tasks for in the task graph ### uncommitted Type: `boolean` Uncommitted changes ### untracked Type: `boolean` Untracked changes ### version Type: `boolean` Show version number ### view Type: `string` Choices: [projects, tasks] Default: `projects` Choose whether to view the projects or task graph ### watch Type: `boolean` Default: `true` Watch for changes to project graph and update in-browser --- ## Angular Schematics and Builders As of Nx 17, official Nx plugins no longer export [schematics](https://angular.dev/tools/cli/schematics) and [builders](https://angular.dev/tools/cli/cli-builder) that can be directly consumed by the Angular CLI. Since Angular CLI users are not a large portion of Nx user base, it made sense to stop maintaining that code. Currently, the Angular CLI can only run its own schematics and builders. The Nx CLI is still able to directly run any schematics or builders created for the Angular CLI as well as Nx own generators and executors. The only thing that has changed is that Nx is no longer taking the extra step of converting our own code into a format that the Angular CLI recognizes. To switch from using the Angular CLI to using the Nx CLI follow the [migrating an Angular CLI project to Nx](/docs/kb/migrate-angular-cli-to-nx) recipe. ## Interop between Nx generators and Angular schematics You can wrap any Nx generator with the [convertNxGenerator](/docs/reference/devkit/convertNxGenerator) function and re-export it in your own plugin as a schematic. This is helpful if you still want to support the Angular CLI. To convert an existing Angular Schematic to an Nx Generator, use the [wrapAngularDevkitSchematic](/docs/reference/devkit/ngcli_adapter/wrapAngularDevkitSchematic) utility function. This is helpful if you want to [programmatically call](/docs/kb/composing-generators) an Angular Schematic inside of a [custom Nx Generator](/docs/kb/local-generators). ## Interop between Nx executors and Angular builders You can wrap any Nx executor with the [convertNxExecutor](/docs/reference/devkit/convertNxExecutor) function and re-export it in your own plugin as a builder. There is no existing utility function to convert an Angular Builder to an Nx Executor. If you want to [programmatically call](/docs/kb/compose-executors) an Angular Builder inside of a [custom Nx Executor](/docs/kb/local-executors), you can import the Angular Builder into your Nx Executor's implementation and call it directly. --- ## As Provided vs. Derived Generator Path Options Nx is moving toward a more transparent and predictable way of determining where code will be generated when you run a generator. The new behavior is to take generator options _as provided_ and place the generated code in the exact folder that you specify. Nx will only use the new behavior in Nx version 20. The directory can be specified with a `--directory` flag or use a fully specified path (i.e. `libs/shared/my-lib/src/my-component`). The `--directory` flag will be calculated relative to where you run the command. In the past, Nx had lots of logic to try and _derive_ the best place to put code for you. This approach worked for some people, but there were always people that wanted to generate their code in a slightly different location. Accounting for these variations lead to either (1) people in repos with a different structure not being able to use these generators or (2) Nx introducing lots of flags to allow people to tweak where code was generated (flags like `--project`, `--flat`, `--pascalCaseFiles`, `--pascalCaseDirectory`, `--fileName`). It also caused some confusion when code was not generated in the expected location or frustration when the user did not know how to get Nx to derive the right thing. Here are some of the issues with the `derived` behavior that are addressed with `as-provided`: - Some generators had multiple flags for the same thing: `directory`, `path`, etc. Going forward, we only use `--directory` across all generators. - Generators resolved the directory option inconsistently. Some resolved it relative to the workspace root while others resolved it relative to the project's source root. In the future, all generators will resolve the directory option relative to the current working directory. As such, your terminal's path autocomplete will work as expected and you can also `cd` right into the directory you want. - Nx transformed the provided names to `kebab-case`. In the future, the name will be taken as provided. This allows for users to deviate from the `kebab-case` directory structure. Nx also included the directory in the project name, which lead to verbose names such as `products-product-detail-page`. Now that Nx takes options as provided, names will be as simple as you would like. You can keep it simple for feature projects (.e.g. `home-page`), or name libraries based on their import path like `@my-org/design-system/buttons`. You can do a mix of both, or deviate from this completely. Note that projects still need to have unique names so you might encounter an error when the name you provide is not valid, but you can resolve the conflict however suits your workspace best. ## Using Nx Console You can use [Nx Console](/docs/getting-started/editor-setup) for an intuitive experience running generators. 1. If you right-click a folder and choose `Nx generate`, the code generation will be run from that folder. 2. As you fill out the generate form, Nx Console will show you a preview of where the new files will be generated. ## Prompting To mitigate the impact of this change of direction, Nx will prompt you when running most generators until Nx 20. We know changing these habits may take time so this prompt allows you to choose the previous behavior. ```text {% title="nx g lib my-lib --directory=shared/my-lib" frame="none" %} ✔ Which generator would you like to use? · @nx/node:library NX Generating @nx/node:library ? What should be the project name and where should it be generated? … ❯ As provided: Name: my-lib Root: shared/my-lib Derived: Name: shared-my-lib-my-lib Root: packages/shared/my-lib/my-lib ``` There are two different kinds of generators that will prompt you to choose between `as-provided` and `derived`: - Project Generators (applications and libraries) - Other Code Generators (components and other code) Your intent is your intent. However, the commands you write and run are based on how you think Nx will act. Nx used to derive the location of your code, but Nx will not in the future. In the future for the reasons stated above, Nx will _only_ take options as provided so you should no longer expect Nx to derive the location. We know it will take time to get used to the new way Nx calculates generated code paths and names. ## Project generators As of Nx 16.8.0, generating a project will prompt you to choose how Nx will calculate where the project should be located. For now, you can choose between `as-provided` and `derived` but `as-provided` will be the only option in Nx 20. The CLI flag to choose between the two algorithms for project generators is `--projectNameAndRootFormat`. ### Generate paths and names `as-provided` This setting makes app or lib generators behave in the following way: - `nx g app my-app` creates a new application named `my-app` in the `/my-app` folder - `nx g lib my-lib` creates a new library named `my-lib` in the `/my-lib` folder - `cd apps/nested/my-app && nx g app my-app` creates a new application named `my-app` in the `/apps/nested/my-app` folder - `nx g app my-app --directory=apps/nested/my-app` creates a new application named `my-app` in the `/apps/nested/my-app` folder - `nx g lib my-lib --directory=libs/shared/ui/my-lib` creates a new library named `my-lib` in the `/libs/shared/ui/my-lib` folder ### Use the old `derived` paths and names Choosing `derived` makes Nx behave the way it did before version 16.8.0. Nx will check the `workspaceLayout` property in `nx.json` to determine how it should calculate the path to the new project. ```json { "workspaceLayout": { "appsDir": "demos", "libsDir": "packages" } } ``` These settings would store apps in `/demos/` and libraries in `/packages/`. The paths specified are relative to the workspace root. This makes app or lib generators behave in the following way: - `nx g app my-app` creates a new application named `my-app` in the `/demos/my-app` folder - `nx g lib my-lib` creates a new library named `my-lib` in the `/packages/my-lib` folder - `nx g app my-app --directory=nested` creates a new application named `nested-my-app` in the `/demos/nested/my-app` folder - `nx g lib my-lib --directory=shared/ui` creates a new library named `shared-ui-my-lib` in the `/packages/shared/ui/my-lib` folder If you accidentally generate a project in the wrong folder, use the [move generator](/docs/reference/workspace/generators#move) to move it to the correct location. ## Code generators As of Nx 17, code generators such as `component`, `service`, and others will prompt you to choose whether or not Nx will derive the location based on your options or not. In Nx 20, Nx will take the generator options as provided. The following flags are deprecated and will be removed in Nx 20: `--project`, `--flat`, `--pascalCaseFiles`, `--pascalCaseDirectory`, `--fileName`. The CLI flag to choose between the two algorithms for code generators is `--nameAndDirectoryFormat`. ### Generate paths and names `as-provided` This setting makes generators behave in the following way: - `nx g component my-component` creates a component in the root. - `cd apps/nested/my-app && nx g component my-component` creates a new component named `my-component` in the `/apps/nested/my-app` folder - `nx g component my-component --directory=apps/nested/my-app` creates a new component named `my-component` in the `/apps/nested/my-app` folder - `nx g component apps/nested/my-app/my-component` creates a new component named `my-component` in the `/apps/nested/my-app` folder {% callout type="note" title="Must generate inside a project" %} If the directory specified is not inside a project, an error will be thrown. {% /callout %} ### Use the old `derived` paths and names Choosing `derived` makes Nx behave the way it did before version 17. Nx will use the deprecated flags (`--project`, `--flat`, `--pascalCaseFiles`, `--pascalCaseDirectory`, `--fileName`) to calculate where to generate the code. This behavior will not be available in Nx 20. This makes generators behave in the following way: - `nx g component my-component` creates a component in the default project. - `nx g component my-component --project=my-app` creates a new component named `my-component` in the `/apps/nested/my-app` folder - `nx g component apps/nested/my-app/my-component` creates a new component named `my-component` in the `/apps/nested/my-app` folder ## Writing scripts If you are using `nx generate` in a script or another environment where it cannot have an interactive prompt, the command will still behave as it did before. You should opt into the new behavior and change the command with a command line flag. ```diff - nx g lib lib1 --no-interactive` + nx g lib lib1 --directory libs/lib1 --projectNameAndRootFormat as-provided --no-interactive - nx g c button --project lib1 --no-interactive + nx g c button --directory libs/lib1/src/lib/button --nameAndDirectoryFormat as-provided --no-interactive ``` --- ## Cacheable Operations In Nx < 17, the way to define which tasks were cacheable was to add the task name to the `cacheableOperations` array in `nx.json`. This way of defining cacheable tasks required all tasks named `test` to be either cacheable or not cacheable. In Nx 17, use the `cache` property in `targetDefaults` or individual target definitions in the project level configuration. --- ## Deprecating Custom Tasks Runner The Custom Task Runners API was created many years ago and has not been supported for several years. It was developed before we introduced a formal method for extending Nx through [plugins](/docs/kb/add-language-support). The API potentially allows modifications to the lifecycle of the Nx command execution, which breaks important invariants that Nx depends on. The new API leverages the plugins API for better performance and functionality. This page guides you on how to migrate off the now deprecated Custom Task Runner API for both: - choosing the best remote cache option for your organization - implementing pre/post processing logic when running tasks ## Remote cache There are several ways to set up remote caching with Nx. [Read more about remote cache options](/docs/kb/self-hosted-caching). ## The preTasksExecution and postTasksExecution hooks Starting with Nx 20.4, a dedicated plugin-based API has been introduced that allows you to safely hook into the task running lifecycle. This new approach provides pre and post execution hooks without compromising Nx internal operations. For comprehensive documentation on this feature, see [Hook into the Task Running Lifecycle](/docs/kb/task-running-lifecycle). Let's look into some concrete examples of how you can migrate from the previous custom task runners to this new API. ### Before: custom tasks runner version Let's imagine that you have implemented a custom task runner as follows: ```typescript function serializeTasksResults(taskResults: { [taskId: string]: TaskResult }) { // ... } function validateEnv() { // ... } export default async function customTasksRunner(tasks, options, context) { if (process.env.QA_ENV) { process.env.NX_SKIP_NX_CACHE = 'true'; } if (!validateEnv()) { throw new Error('Env is not set up correctly'); } const allTaskResults = {}; const lifeCycle = { endTasks(taskResults) { taskResults.forEach((tr) => { allTaskResults[tr.task.id] = tr; }); }, }; const ret = await defaultTasksRunner( tasks, { ...options, lifeCycle }, context ); if (options.reportAnalytics) { await fetch(process.env.DATACAT_API, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: serializeTasksResults(allTaskResults), }); } return ret; } ``` And let's imagine you configured it in `nx.json` as follows: ```json { "tasksRunnerOptions": { "default": { "runner": "npm-package-with-custom-tasks-runner", "options": { "reportAnalytics": true } } } } ``` ### After: Using the New API The new API includes `preTasksExecution` and `postTasksExecution` hooks that plugins can register. These hooks do not affect task execution and cannot violate any invariants. The custom task runner above can be implemented as follows: ```typescript function serializeTasksResults(taskResults: { [taskId: string]: TaskResult }) { // task results contain timings and status information useful for gathering analytics } function validateEnv() { // ... } // context contains workspaceRoot and nx.json configuration export async function preTasksExecution(options: any, context) { if (process.env.QA_ENV) { process.env.NX_SKIP_NX_CACHE = 'true'; } if (!validateEnv()) { throw new Error('Env is not set up correctly'); } } // context contains workspaceRoot, nx.json configuration, and task results export async function postTasksExecution(options: any, context) { if (options.reportAnalytics) { await fetch(process.env.DATACAT_API, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: serializeTasksResults(context.taskResults), }); } } ``` Because it's a regular plugin, it can be configured as follows in `nx.json`: ```json { "plugins": [ { "plugin": "npm-package-with-the-plugin", "options": { "reportAnalytics": true } } ] } ``` As with all plugin hooks, the `preTasksExecution` and `postTasksExecution` hooks must be exported so that they load properly when the "npm-package-with-the-plugin" is initialized. ### Multiple tasks runners You can implement multiple tasks runners using the new hooks. Imagine you have the following `nx.json`: ```json { "tasksRunnerOptions": { "a": { "runner": "package-a" }, "b": { "runner": "package-b" } } } ``` You can replace it with two plugins: ```json { "plugins": [ { "plugin": "package-a" }, { "plugin": "package-b" } ] } ``` Simply add a condition to your hooks as follows: ```typescript export async function preTasksExecution() { if (process.env.RUNNER != 'a') return; } export async function postTasksExecution(options, tasksResults) { if (process.env.RUNNER != 'a') return; } ``` You can then choose which hooks to use by setting the RUNNER env variable. ### Passing options You can no longer augment options passed to the default tasks runner, so instead you need to set env variables in the `preTasksExecution` hook. ### Composing plugins Implementing hooks in plugins offers several advantages. It allows multiple plugins to be loaded simultaneously, enabling a clean separation of concerns. ### Keeping state across command invocations By default, every plugin initiates a long-running process, allowing you to maintain state across command invocations, which can be very useful for advanced analytics. ## Unhandled custom tasks runner use cases? If you have a use case that the new API doesn't handle, please [open an issue](http://github.com/nrwl/nx). --- ## Global Implicit Dependencies Since v14.4, Nx supports [`inputs` and `namedInputs`](/docs/kb/configure-inputs) for setting up implicit dependencies. As of Nx v16, the `implicitDependencies` defined in `nx.json` are ignored and do not influence the affected graph. This field will be removed in v17. The [`implicitDependencies` in the project configuration](/docs/reference/project-configuration#implicitdependencies) are still the best way to manually set up a dependency between two projects that Nx is not able to detect automatically. ## Projects depending on global files The old way to have the `myapp` project depend on specific files in the root of the workspace was to use `implicitDependencies`, like this: ```json { "implicitDependencies": { "globalConfig.js": ["myapp"], "styles/**/*.css": ["myapp"] } } ``` To express the same dependencies with `inputs` and `namedInputs`, modify the default `sharedGlobals` named input: ```json // nx.json { "namedInputs": { "sharedGlobals": [ "{workspaceRoot}/globalConfig.js", "{workspaceRoot}/styles/**/*.css" ], "default": [ "sharedGlobals" // etc ] } } ``` The `sharedGlobals` are included in the `default` named input, so most targets will be set up to depend on them. For a more detailed explanation, read the [Customizing Inputs and Named Inputs guide](/docs/kb/configure-inputs) ### Dependencies on sections of the root `package.json` file You used to be able to set up dependencies on specific packages in the `dependencies` and `devDependencies` sections of the `package.json` file, like this: ```json { "implicitDependencies": { "package.json": { "dependencies": "*", "devDependencies": { "mypackage": ["mylib"] } } } } ``` As of Nx 15, this is inferred automatically by Nx based on the `import` statements in your code. These `implicitDependencies` can be safely deleted. --- ## Integrated Repos vs. Package-Based Repos vs. Standalone Apps {% aside type="note" title="" %} As of Nx 20, it is no longer useful to draw a distinction between integrated and package-based repositories. We've removed references to these terms in the rest of the documentation, but this page is kept here to describe the terms for historical reasons. Instead of using terms that classify Nx repositories into two different types, we want to highlight the fact that Nx features can be enabled independently of each other. Choose the features that make sense for your repository. {% /aside %} There are many different ways to structure a repository and Nx is designed to support them all. To better discuss how Nx can improve a repository, it is helpful to define some terms. - Standalone Application - A repository with a single application - Package-Based Repository - A repository with multiple projects that depend on each other via `package.json` and often have nested `node_modules` - Integrated Repository - A repository with multiple projects that depend on each other via typescript imports and often employ a single version policy Nx features can be enabled in each of these types of repositories. Just as each repository is unique and may not exactly fit in one of these categories, the way Nx is used will vary between repositories. {% cardgrid %} {% linkcard title="Packaged based vs Integrated Style - Use Nx however it works best for you" description="Choose your style and what works best for you!" href="https://youtu.be/ArmERpNvC8Y" /%} {% linkcard title="Getting Started with Package-Based Repos" description="Walkthrough for creating a package-based monorepo with Nx" href="https://youtu.be/hzTMKuE3CDw" /%} {% linkcard title="Getting Started with Integrated Repos" description="Walkthrough for creating an integrated monorepo with Nx" href="https://youtu.be/weZ7NAzB7PM" /%} {% /cardgrid %} ## Package-based repos A package-based repo is a collection of packages that depend on each other via `package.json` files and nested `node_modules`. With this setup, you typically have [different dependencies for each project](/docs/kb/dependency-management). Build tools like Jest and Webpack work as usual, since everything is resolved as if each package was in a separate repo and all of its dependencies were published to npm. Moving an existing package into a package-based repo is very easy since you generally leave that package's existing build tooling untouched. Creating a new package inside the repo is just as difficult as spinning up a new repo since you have to create all the build tooling from scratch. Lerna, Yarn, Lage, [Turborepo](/docs/kb/from-turborepo) and Nx support this style. Someone who appreciates the flexibility of a package-based repository will be most interested in the following features of Nx: - Add [caching](/docs/features/cache-task-results) and [task orchestration](/docs/features/run-tasks) without modifying tooling or file structure - Import existing projects into the repo without modifying their tooling - Easily create new projects or tools with [code generators](/docs/features/generate-code) {% linkcard title="Learn Nx Tutorial" description="Add Nx to an existing TypeScript repo" href="/docs/getting-started/tutorials/crafting-your-workspace" /%} ## Integrated repos An integrated repo contains projects that depend on each other through standard import statements. There is typically a [single version of every dependency](/docs/kb/dependency-management) defined at the root. Sometimes build tools like Jest and Webpack need to be wrapped to work correctly. It's harder to add an existing package to this repo style because the build tooling for that package may need to be modified. It's straightforward to add a brand-new project to the repo because all the tooling decisions have already been made. Bazel and Nx support this style. Someone who appreciates the structure and consistency of an integrated repository will be most interested in the following features of Nx: - [Enforce architectural decisions](/docs/features/enforce-module-boundaries) with tagging rules - Encourage consistency with custom [code generators](/docs/features/generate-code) - [Automate updating dependencies](/docs/features/automate-updating-dependencies) of the entire toolchain {% cardgrid %} {% linkcard title="Learn Nx Tutorial" description="Create a monorepo with Nx" href="/docs/getting-started/tutorials/crafting-your-workspace" /%} {% /cardgrid %} ## Standalone applications Nx plugins, especially the [generators](/docs/features/generate-code), [executors](/docs/kb/executors-and-configurations) and [migrations](/docs/features/automate-updating-dependencies) that come with them, are not only valuable for a monorepo scenario. In fact, many developers use Nx not primarily for its monorepo support, but for its tooling support, particularly its ability to modularize a codebase and, thus, better scale it. Nx supports standalone applications, which are like an integrated monorepo setup, but with just a single, root-level application. Think of it as an advanced, more capable Create-React-App or Angular CLI. And obviously, you can still leverage all the generators and executors and structure your application into libraries or submodules. Someone whose main focus is on improving their single application will be most interested in the following features of Nx: - Set up a [fast CI system](/docs/kb/setup-ci) without CI expertise - Easily [add new tooling](/docs/plugin-registry) {% linkcard title="Standalone Applications with Nx" description="Learn what Standlone Apps are and how Nx can be useful" href="https://youtu.be/qEaVzh-oBBc" /%} ## How to choose Nx itself doesn't care which style you choose. You can use all the features of Nx whether you are in a package based repo or integrated repo. Certain Nx features will be more or less valuable for a standalone app, but all the features of Nx are still available to be put in place as soon as that repo grows to include more apps. You can be successful working in any style, and there are ways to transition between them. At a high level - **standalone apps -** for when you want a single project that can be nicely structured and modularized. It's a good starting point if you're not looking into a monorepo but with the option to expand later. - **package-based repos -** ideally when you already have a monorepo (e.g. yarn/npm/pnpm workspace) and you want Nx primarily for speed and task scheduling. Also, when you want Nx to stay mostly out of your way and you set up everything on your own. - **integrated repos -** when you want more help from Nx. It takes away the burden of the configuration by coming up with a pre-configured setup that scales well, and provides scaffolding support and automated code migrations. Organizations choose this approach if they are bought into monorepos and want to scale up. Integrated repos might restrict some choices to allow Nx to help you more but result in better maintainability and more value in the long run. The comparison between package-based repos and integrated repos is similar to that between JSDoc and TypeScript. The former is easier to adopt and provides some good benefits. The latter takes more work but offers more value, especially at a larger scale. --- ## Legacy Cache In Nx 21, the legacy file system cache will be removed in favor of a new database cache. The new database cache stores metadata in a database, rather than blindly trusting the file system. The database cache has the following benefits: 1. Cache reads and writes are faster. 2. The local cache is more secure since Nx will no longer retrieve artifacts it does not recognize. The legacy file system cache can still be used in Nx 20 by setting `useLegacyCache: true` in your `nx.json` file. To gain the benefits above, remove `useLegacyCache: true` from your `nx.json`. If you are currently using a custom task runner or the `NX_REJECT_UNKNOWN_LOCAL_CACHE` environment variable continue reading below. ## tasksRunnerOptions As of Nx 20, the `tasksRunnerOptions` property in `nx.json` is deprecated. This property was used to register custom task runners. `tasksRunnerOptions` and custom task runners will not work with the new database cache. If you are using a custom tasks runner to customize your cache, you have the following options: Use [Nx Cloud](https://nx.dev/nx-cloud) for your remote cache. This is the safest, lowest-maintenance, most recommended option. If you cannot use Nx Cloud, [build your own caching server](/docs/kb/self-hosted-caching#build-your-own-caching-server) using the OpenAPI specification. **To learn more about migrating from custom task runners, [please refer to this detailed guide](/docs/reference/deprecated/custom-tasks-runner).** ## NX_REJECT_UNKNOWN_LOCAL_CACHE The `NX_REJECT_UNKNOWN_LOCAL_CACHE` environment variable does not work with the new database cache. We have introduced a new cache which will recognize artifacts from other machines. If you are using `NX_REJECT_UNKNOWN_LOCAL_CACHE` to share your local cache on a network drive, you have a few options moving forward: 1. Use [Nx Cloud](https://nx.dev/nx-cloud) for your remote cache. This is the safest, lowest-maintenance, most recommended option. 2. [Build your own caching server](/docs/kb/self-hosted-caching#build-your-own-caching-server) using the OpenAPI specification. --- ## NPM Scope The `npmScope` property of the `nx.json` file is deprecated as of version 16.2.0. `npmScope` was used as a prefix for the names of newly created projects. The new recommended way to define the organization prefix is to set the `name` property in the root `package.json` file to `@my-org/root`. Then `@my-org/` will be used as a prefix for all newly created projects. In Nx 16, if the `npmScope` property is present, it will be used as a prefix. If the `npmScope` property is not present, the `name` property of the root `package.json` file will be used to infer the prefix. In Nx 17+, the `npmScope` property is ignored. --- ## print-affected - CLI command **Deprecated:** Use `nx show projects --affected`, `nx affected --graph -t build` or `nx graph --affected` depending on which best suits your use case. The `print-affected` command was removed in Nx 19. Prints information about the projects and targets affected by changes ## Usage ```shell nx print-affected ``` Install `nx` globally to invoke the command directly using `nx`, or use `npx nx`, `yarn nx`, or `pnpm nx`. ### Examples Print information about affected projects and the project graph: ```shell nx print-affected ``` Print information about the projects affected by the changes between main and HEAD (e.g,. PR): ```shell nx print-affected --base=main --head=HEAD ``` Prints information about the affected projects and a list of tasks to test them: ```shell nx print-affected -t test ``` Prints the projects property from the print-affected output: ```shell nx print-affected -t build --select=projects ``` Prints the tasks.target.project property from the print-affected output: ```shell nx print-affected -t build --select=tasks.target.project ``` ## Options ### base Type: `string` Base of the current branch (usually main) ### configuration Type: `string` This is the configuration to use when performing tasks on projects ### exclude Type: `string` Exclude certain projects from being processed ### files Type: `string` Change the way Nx is calculating the affected command by providing directly changed files, list of files delimited by commas or spaces ### head Type: `string` Latest commit of the current branch (usually HEAD) ### help Type: `boolean` Show help ### select Type: `string` Select the subset of the returned json document (e.g., --select=projects) ### targets Type: `string` Tasks to run for affected projects ### type Type: `string` Choices: [app, lib] Select the type of projects to be returned (e.g., --type=app) ### uncommitted Type: `boolean` Uncommitted changes ### untracked Type: `boolean` Untracked changes ### version Type: `boolean` Show version number --- ## Rescoping Packages from @nrwl to @nx As of version 16, all the official Nx plugins moved from the `@nrwl` npm scope to `@nx`. The goal is for anyone looking through a list of dependencies to easily make the connection between the `nx` package and the `@nx` plugins that are associated with it. As of version 20, the `@nrwl` scoped packages will no longer be published to npm. ## What do you need to do? For new repos, run `npx create-nx-workspace` as usual. The correct packages will be added. For existing repos, run `nx migrate` as you normally would. When migrating to version 16 or higher, the package names will be automatically updated for you. If you have a local script, update it to use `@nx/*` packages. If you maintain a Nx plugin, please update it to use the `@nx/*` packages. > If you come across Nrwl in blog posts, videos, or elsewhere, that content is likely referring to these packages. Depending on when the content was published, it is likely still relevant. ### Rename The packages are being renamed as defined in the tables below: The following packages were not simply rescoped `@nrwl` to `@nx`. | Nx 15 and lower | Nx 16 and higher | | ---------------------- | ----------------- | | @nrwl/nx-cloud | nx-cloud | | @nrwl/eslint-plugin-nx | @nx/eslint-plugin | | @nrwl/nx-plugin | @nx/plugin | | Nx 15 and lower | Nx 16 | Nx 17 | | --------------- | ---------- | ---------- | | @nrwl/linter | @nx/linter | @nx/eslint | ### Rescope only All other packages keep the same name, but change scope from `@nrwl` to `@nx`. | Nx 15 and lower | Nx 16 | | ------------------ | ---------------- | | @nrwl/angular | @nx/angular | | @nrwl/aws-lambda | @nx/aws-lambda | | @nrwl/cypress | @nx/cypress | | @nrwl/detox | @nx/detox | | @nrwl/devkit | @nx/devkit | | @nrwl/esbuild | @nx/esbuild | | @nrwl/expo | @nx/expo | | @nrwl/express | @nx/express | | @nrwl/gatsby | @nx/gatsby | | @nrwl/jest | @nx/jest | | @nrwl/js | @nx/js | | @nrwl/nest | @nx/nest | | @nrwl/netlify | @nx/netlify | | @nrwl/next | @nx/next | | @nrwl/node | @nx/node | | @nrwl/react | @nx/react | | @nrwl/react-native | @nx/react-native | | @nrwl/remix | @nx/remix | | @nrwl/rollup | @nx/rollup | | @nrwl/rspack | @nx/rspack | | @nrwl/storybook | @nx/storybook | | @nrwl/vite | @nx/vite | | @nrwl/web | @nx/web | | @nrwl/webpack | @nx/webpack | | @nrwl/workspace | @nx/workspace | ## @nrwl scope end of life Starting in version 20, the `@nrwl` scoped packages will no longer be published to npm. Only the `@nx` scoped packages will be available on the npm registry. --- ## Runtime Cache Inputs The `runtimeCacheInputs` property was used as a way to add extra inputs to the Nx cache, like the version of node on that particular machine. `runtimeCacheInputs` were set as follows: ```json { "tasksRunnerOptions": { "default": { "runner": "nx/tasks-runners/default", "options": { "cacheableOperations": ["build", "lint", "test", "e2e"], "runtimeCacheInputs": ["node -v"] } } } } ``` Instead of specifying the runtime inputs in `tasksRunnerOptions`, in Nx 14.4 you can include them as runtime inputs in the standard [`inputs` and `namedInputs` area of your project configuration](/docs/reference/project-configuration#inputs-and-named-inputs) or [`nx.json`](/docs/reference/nx-json#inputs-namedinputs). The new style looks like this: ```jsonc { "targets": { "build": { "inputs": ["^build", { "runtime": "node -v" }], // ... }, }, } ``` --- ## Deprecation notice: self-hosted remote cache packages `@nx/s3-cache`, `@nx/gcs-cache`, `@nx/azure-cache`, and `@nx/shared-fs-cache` are deprecated as of 2026-05-21. The CREEP vulnerability ([CVE-2025-36852](https://www.cve.org/CVERecord?id=CVE-2025-36852)) affects all four packages. The flaw is in their design and cannot be patched. These packages use a single credential that grants read and write access across the entire cache. Nothing in the bucket tracks which branch produced which artifact. An attacker can open a PR off `main` with no source changes but a modified CI workflow that builds a malicious artifact. The CI workflow isn't part of the cache key, so the PR hashes to the same key that `main` will hash to. If the PR uploads its artifact first, every later `main` build with that key gets a cache hit on the poisoned artifact and ships it without rebuilding. Supply chain attacks against open-source ecosystems are now a near-weekly occurrence, and cache poisoning is a known vector. We have no evidence that these packages have been exploited in the wild, but the design above guarantees that any attempt will succeed. Treat these packages as a live risk and migrate. The packages stay on npm so existing builds don't break for now. They will not receive updates or security patches, and may be removed in the future. For information on the vulnerability, see [The CREEP vulnerability and build cache security](https://nx.dev/blog/creep-vulnerability-build-cache-security#the-creep-vulnerability). ## Affected packages - `@nx/s3-cache` - `@nx/gcs-cache` - `@nx/azure-cache` - `@nx/shared-fs-cache` ## Recommended: Migrate to Nx Cloud OR disable remote cache [Nx Cloud](https://nx.dev/nx-cloud) is the best solution for remote cache. It is free to get started and requires no infrastructure on your side. To connect your workspace, see [Connect to Nx Cloud](/docs/getting-started/nx-cloud). If you need on-premises storage, see [Self-hosted caching](/docs/kb/self-hosted-caching). If you cannot use Nx Cloud right now, we recommend disabling remote cache to avoid exploitation of the CREEP vulnerability. ## Advanced: Build your own {% aside type="caution" title="Self-hosting a remote cache is high-risk" %} Implementing a remote cache server yourself means accepting full responsibility for the threat model that CREEP exposed. You must understand cache poisoning, artifact integrity, and access control before you deploy one. A misconfigured implementation reproduces the same vulnerability these deprecated packages had. The OpenAPI spec requires a 409 Conflict response when a client tries to write a cache key that already exists. Implementations that allow overwriting existing entries are vulnerable even without a race. Your implementation must enforce 409 on existing keys. Most teams should use Nx Cloud instead. {% /aside %} If you have the resources to harden and operate a cache server, you can implement the [Nx remote cache OpenAPI specification](/docs/kb/self-hosted-caching#build-your-own-caching-server). The four deprecated packages will not be updated to match that specification. ## FAQ ### Were these packages compromised? No. These remote cache packages have not been compromised. The security issue is that bucket-based cache solutions are open to cache poisoning attacks by design. ### What should I do if I'm using one of these packages today? Migrate to Nx Cloud, or implement the OpenAPI specification yourself if you have the resources to harden it. The packages stay on npm but will not be patched. ### Will these packages receive security patches? No. The vulnerability is in the design of the packages, not in a fixable bug. ### Will the packages be unpublished from npm? No. They remain on npm for now so existing builds do not break immediately. They will not receive updates. --- ## Extending the Project Graph (v1 API) {% aside type="caution" title="Experimental" %} This API has been superceded by the [v2 API](/docs/kb/add-language-support) and will be removed in Nx 20. If targeting Nx version 16.7 or higher, please use the v2 API instead. {% /aside %} The v1 plugin API for modifying the project graph was split into two parts: - [Project Inference Plugins](#project-inference-plugins) - [Project Graph Plugins](#project-graph-plugins) Project inference plugins are used to infer projects from the file system. Project graph plugins are used to modify the project graph after projects have been identified. In the v2 API, there are still two parts but they are divided differently: - [createNodes](/docs/kb/add-language-support#create-projects-and-tasks) - [createDependencies](/docs/kb/add-language-support#create-dependencies-between-projects) These are much clearer in terms of responsibility, and are more flexible. The v1 API is still documented below for reference. ## Adding plugins to workspace You can register a plugin by adding it to the plugins array in `nx.json`: ```jsonc // nx.json { ..., "plugins": [ "awesome-plugin" ] } ``` ## Project inference plugins Project inference describes the ability of Nx to discover and work with projects based on source code and configuration files in your repo. Out of the box, Nx identifies projects based on the presence of `package.json` and `project.json` files. It also identifies targets in the `package.json` scripts and the `project.json` `target`s. Project inference plugins allow you to extend this functionality of Nx to other languages and file structures. ### Project file patterns Project file patterns are used in two scenarios: - Inferring projects - Determining which files should be passed into `registerProjectTargets`. Let's use the below plugin and workspace layout as an example: ```typescript // libs/awesome-plugin/index.ts export const projectFilePatterns = ['project.json', 'my-other-project-file']; export function registerProjectTargets(projectFilePath) { console.log(projectFilePath); } ``` > workspace layout ```text my-workspace/ ├─ node_modules/ ├─ libs/ │ ├─ my-project/ │ │ ├─ my-other-project-file │ ├─ nx-project/ │ │ ├─ my-other-project-file │ │ ├─ project.json ├─ nx.json └─ package.json ``` During initialization, we would expect to see "libs/my-project/my-other-project-file", "libs/nx-project/my-other-project-file", "libs/nx-project/project.json" all logged out to the console. Nx was able to infer `my-project` from the layout. ### Implementing a project target configurator A project target configurator is a function that takes a path to a project file, and returns the targets inferred from that file. Plugins should export a function named `registerProjectTargets` that infers the targets from each matching project file. This function receives the path to the project file as its sole parameter. The `registerProjectTargets` function should return a `Record`, which describes the targets inferred for that specific project file. ```typescript import { TargetConfiguration } from '@nx/devkit'; export const projectFilePatterns = ['project.json', 'my-other-project-file']; export function registerProjectTargets( projectFilePath: string ): Record { return { build: { /** * This object should look exactly like a target * configured inside `project.json` */ }, }; } ``` For guidance on implementing a similar function in the v2 API, see the documentation on [createNodes](/docs/kb/add-language-support#create-projects-and-tasks). ### Multiple matches It is possible that the registerProjectTargets function may be called multiple times for one project. This could occur in a few cases, one of which is demonstrated above. - One plugin may list multiple file patterns, and a project may match more than one of them. - Multiple plugins may list similar patterns, and pick up the project separately. **In the first case**, the plugin that you are writing will be called into multiple times. If you return the same target (e.g. `build`) on each call, whichever is ran last would be the target that Nx calls into. The order that the function would be called is **NOT** guaranteed, so you should try to avoid this when possible. If specifying multiple patterns, they should either be mutually exclusive (e.g. one match per project) or the plugin should conditionally add targets based on the file passed in. **In the second case**, different plugins may attempt to register the same target on a project. If this occurs, whichever target was registered by the plugin listed latest in `nx.json` would be the one called into by Nx. As an example, assume `plugin-a`, `plugin-b`, and `plugin-c` all match a file and register `build` as a target. If `nx.json` included `"plugins": ["plugin-a", "plugin-b", "plugin-c"]`, running `nx build my-project` would run the target as defined by `"plugin-c"`. Alternatively, if `nx.json` included `"plugins": ["plugin-c", "plugin-b", "plugin-a"]`, running `nx build my-project` would run the target as defined by `"plugin-a"`. ### Development tips There is a cache that Nx uses to avoid recalculating the project graph as much as possible, but it may need to be skipped during plugin development. You can set the following environment variable to disable the project graph cache: `NX_CACHE_PROJECT_GRAPH=false`. It might also be a good idea to ensure that the dep graph is not running on the nx daemon by setting `NX_DAEMON=false`, as this will ensure you will be able to see any `console.log` statements you add as you're developing. You can also leave the daemon active, but `console.log` statements would only appear in its log file. ## Project graph plugins The Project Graph is the representation of the source code in your repo. Projects can have files associated with them. Projects can have dependencies on each other. One of the best features of Nx the ability to construct the project graph automatically by analyzing your source code. Currently, this works best within the JavaScript ecosystem, but it can be extended to other languages and technologies using plugins. ### Implementing a project graph processor A Project Graph Processor takes a project graph and returns a new project graph. It can add/remove nodes and edges. Plugins should export a function named `processProjectGraph` that handles updating the project graph with new nodes and edges. This function receives two things: - A `ProjectGraph` - `graph.nodes` lists all the projects currently known to Nx. - `graph.dependencies` lists the dependencies between projects. - A `Context` - `context.workspace` contains the combined configuration for the workspace. - `files` contains all the files found in the workspace. - `filesToProcess` contains all the files that have changed since the last invocation and need to be reanalyzed. The `processProjectGraph` function should return an updated `ProjectGraph`. This is most easily done using `ProjectGraphBuilder`. The builder is there for convenience, so you don't have to use it. ```typescript import { ProjectGraph, ProjectGraphBuilder, ProjectGraphProcessorContext, DependencyType, } from '@nx/devkit'; export function processProjectGraph( graph: ProjectGraph, context: ProjectGraphProcessorContext ): ProjectGraph { const builder = new ProjectGraphBuilder(graph); // We will see how this is used below. return builder.getUpdatedProjectGraph(); } ``` ### Adding new nodes to the project graph You can add nodes to the project graph. Since first-party code is added to the graph automatically, this is most commonly used for third-party packages. A Project Graph Plugin can add them to the project graph. After these packages are added as nodes to the project graph, dependencies can then be drawn from the workspace projects to the third party packages as well as between the third party packages. ```typescript // Add a new node builder.addNode({ name: 'new-project', type: 'npm', data: { files: [], }, }); ``` {% aside type="note" title="More details" %} You can designate any type for the node. This differentiates third party projects from projects in the workspace. If you are writing a plugin for a different language, it's common to use IPC to get the list of nodes which you can then add using the builder. {% /aside %} ### Adding new dependencies to the project graph It's more common for plugins to create new dependencies. First-party code contained in the workspace is added to the project graph automatically. Whether your project contains TypeScript or say Java, both projects will be created in the same way. However, Nx does not know how to analyze Java sources, and that's what plugins can do. You can create 2 types of dependencies. #### Implicit dependencies An implicit dependency is not associated with any file, and can be created as follows: ```typescript // Add a new edge builder.addImplicitDependency('existing-project', 'new-project'); ``` {% aside type="note" title="More details" %} Even though the plugin is written in JavaScript, resolving dependencies of different languages will probably be more easily written in their native language. Therefore, a common approach is to spawn a new process and communicate via IPC or `stdout`. {% /aside %} Because an implicit dependency is not associated with any file, Nx doesn't know when it might change, so it will be recomputed every time. #### Static dependencies Nx knows what files have changed since the last invocation. Only those files will be present in the provided `filesToProcess`. You can associate a dependency with a particular file (e.g., if that file contains an import). ```typescript // Add a new edge builder.addStaticDependency( 'existing-project', 'new-project', 'libs/existing-project/src/index.ts' ); ``` If a file hasn't changed since the last invocation, it doesn't need to be reanalyzed. Nx knows what dependencies are associated with what files, so it will reuse this information for the files that haven't changed. #### Dynamic dependencies Dynamic dependencies are a special type of explicit dependencies. In contrast to standard `explicit` dependencies, they are only imported in the runtime under specific conditions. A typical example would be lazy-loaded routes. Having separation between these two allows us to identify situations where static import breaks the lazy-loading. ```typescript import { DependencyType } from '@nx/devkit'; // Add a new edge builder.addDynamicDependency( 'existing-project', 'lazy-route', 'libs/existing-project/src/router-setup.ts' ); ``` ### Visualizing the project graph You can then visualize the project graph as described [here](/docs/features/explore-graph). However, there is a cache that Nx uses to avoid recalculating the project graph as much as possible. As you develop your project graph plugin, it might be a good idea to set the following environment variable to disable the project graph cache: `NX_CACHE_PROJECT_GRAPH=false`. It might also be a good idea to ensure that the dep graph is not running on the nx daemon by setting `NX_DAEMON=false`, as this will ensure you will be able to see any `console.log` statements you add as you're developing. ### Example project graph plugin The [nrwl/nx-go-project-graph-plugin](https://github.com/nrwl/nx-go-project-graph-plugin) repo contains an example project graph plugin which adds [Go](https://golang.org/) dependencies to the Nx Project Graph! A similar approach can be used for other languages. {% github_repository url="https://github.com/nrwl/nx-go-project-graph-plugin" /%} --- ## Workspace Executors In Nx 13.10+, local nx plugins can contain executors that are used in the workspace. When creating a custom executor for your workspace, look into the [local executor guide](/docs/kb/local-executors) to simplify the build process. ## Converting workspace executors to local executors - If you don't already have a local plugin, use Nx to generate one: ```shell npm add -D @nx/plugin nx g @nx/plugin:plugin tools/my-plugin ``` - Use the Nx CLI to generate the initial files needed for your executor. Replace `my-executor` with the name of your workspace executor. ```shell nx generate @nx/plugin:executor tools/my-plugin/src/executors/my-executor ``` - Copy the code for your workspace executor into the newly created executor's folder. e.g. `libs/my-plugin/src/executors/my-executor/` - Now you can reference the executor like this: ```jsonc { "executor": "@my-org/my-plugin:my-executor", } ``` --- ## Workspace Generators In Nx 13.10, we introduced the ability to run generators from Nx plugins in the workspace they were created in. By using a "local" plugin, you can set the plugin as your workspace's default collection and get several other affordances that are not provided to workspace generators. This is the preferred method for "workspace generators", and existing generators will eventually be transitioned to use a local plugin. Check the [local generator guide](/docs/kb/local-generators) for information on creating a new plugin. ## Converting workspace generators to local generators {% aside type="note" %} When migrating to Nx 16, a new workspace plugin is automatically generated in the tools folder if you already have workspace-generators. {% /aside %} - If you don't already have a local plugin, use Nx to generate one: ```shell npm add -D @nx/plugin nx g @nx/plugin:plugin tools/my-plugin ``` - Use the Nx CLI to generate the initial files needed for your generator. Replace `my-generator` with the name of your workspace generator. ```shell nx generate @nx/plugin:generator tools/my-plugin/src/generators/my-generator ``` - Copy the code for your workspace generator into the newly created generator's folder. e.g. `libs/my-plugin/src/generators/my-generator/` - Now you can run the generator like this: ```shell nx g my-generator ``` --- ## workspace.json Nx used to have a `workspace.json` file at the root of the repo that at various points performed these functions: 1. Identified the locations of all project in the repo 2. Contained the target configuration for all projects Identifying the locations of projects is now done automatically through project inference. You can even customize how projects are inferred with a [project inference plugin](/docs/kb/add-language-support). The target configuration for each project is now stored in individual `project.json` files or `package.json` files. ## Removing workspace.json To remove `workspace.json` in favor of `project.json` files, run: ```shell nx g @nx/workspace:fix-configuration ``` See [fix-configuration](/docs/reference/workspace/generators#fix-configuration) for more options. After this command, `workspace.json` should look like this: ```jsonc { "version": 2, "projects": { "my-app": "apps/my-app", "some-lib": "libs/some-lib", // ... }, "$schema": "./node_modules/nx/schemas/workspace-schema.json", } ``` If every project is listed as a string, instead of an object with project configuration properties, then it is safe to delete the `workspace.json` file. --- ## Detox For setup and guides, see [Nx with Detox](/docs/technologies/test-tools/detox/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Detox" /%} --- ## .NET For setup and guides, see [Nx with .NET](/docs/technologies/dotnet/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/.NET" /%} --- ## Nx Environment Variables Nx loads environment variables from the process and from environment files in your workspace. You can use environment variables to configure task behavior, pass values to applications, and change how Nx operates. Frameworks and bundlers can have additional rules for exposing environment variables to application code. Refer to their documentation for those rules. ## Loading environment variables Nx loads environment variables from the following files, in order: 1. `[project-root]/.env.[target-name].[target-configuration-name].local` 2. `[project-root]/.env.[target-name].[target-configuration-name]` 3. `[project-root]/.[target-name].[target-configuration-name].local.env` 4. `[project-root]/.[target-name].[target-configuration-name].env` 5. `[project-root]/.env.[target-configuration-name].local` 6. `[project-root]/.env.[target-configuration-name]` 7. `[project-root]/.[target-configuration-name].local.env` 8. `[project-root]/.[target-configuration-name].env` 9. `[project-root]/.env.[target-name].local` 10. `[project-root]/.env.[target-name]` 11. `[project-root]/.[target-name].local.env` 12. `[project-root]/.[target-name].env` 13. `[project-root]/.env.local` 14. `[project-root]/.local.env` 15. `[project-root]/.env` 16. `.env.[target-name].[target-configuration-name].local` 17. `.env.[target-name].[target-configuration-name]` 18. `.[target-name].[target-configuration-name].local.env` 19. `.[target-name].[target-configuration-name].env` 20. `.env.[target-configuration-name].local` 21. `.env.[target-configuration-name]` 22. `.[target-configuration-name].local.env` 23. `.[target-configuration-name].env` 24. `.env.[target-name].local` 25. `.env.[target-name]` 26. `.[target-name].local.env` 27. `.[target-name].env` 28. `.env.local` 29. `.local.env` 30. `.env` Nx checks environment files using these priorities: - Project-specific files before workspace-level files - Target and configuration-specific files before general files - Local files before non-local files {% aside type="caution" title="Order is important" %} Nx moves through the list, ignores files it cannot find, and loads environment variables into the current process. If a variable is already loaded, Nx does not overwrite it. This behavior: 1. Prevents developers from accidentally overwriting important system-level variables such as `NODE_ENV`. 2. Allows `.env.local` or `.local.env` files to override project defaults in `.env`. 3. Allows target-specific files such as `.env.build` or `.build.env` to override variables for a particular target. For example: 1. `apps/my-app/.env.local` contains `NX_PUBLIC_API_URL=http://localhost:3333`. 2. `apps/my-app/.env` contains `NX_PUBLIC_API_URL=https://api.example.com`. 3. Nx loads the value from `apps/my-app/.env.local` first. Because `NX_PUBLIC_API_URL` is already set when Nx reads `apps/my-app/.env`, Nx ignores the second value. Store application-specific environment files in the application directory, such as `apps/my-app`. Use workspace-level environment files for workspace-wide settings, such as the [Nx Cloud access token](/docs/kb/access-tokens). {% /aside %} {% aside type="caution" title="Package managers can set variables before Nx runs" %} A package manager can add variables to the process before Nx reads environment files. Nx preserves those values because they are already set. For example, the npm `node-options` setting from a project, user, or global `.npmrc` becomes a `NODE_OPTIONS` environment variable when you run `npm run nx ...` or `npx nx ...`. If `.env` also defines `NODE_OPTIONS`, the `.npmrc` value takes precedence. Running `nx` directly uses the value from `.env`. If you rely on a variable from an environment file, make sure nothing else sets it first. {% /aside %} {% aside type="caution" title="Task-specific environment files are not loaded in batch mode" %} Nx does not load task-specific environment files for tasks run in [batch mode](/docs/reference/glossary#batch-mode). Gradle and Maven tasks run in batch mode by default. Batch processes receive variables from the current environment and root environment files such as `.env` and `.env.local`. Variables in files such as `.env.[target-name]` are not available. {% /aside %} ### Environment variables for atomized targets Atomized target names are created dynamically, typically by using file names as suffixes. Because those names are difficult to use in environment file names, Nx searches for the atomized target's parent and non-atomized target instead. For example, instead of looking for `.env.e2e-ci--path/to/atomized/file`, Nx looks for: - `.env.e2e-ci` - `.env.e2e` The combinations of targets, configurations, and path priorities follow this order: - `[project-root]/[parent-target-name].[target-configuration-name]` - `[project-root]/[non-atomized-target-name].[target-configuration-name]` - `[project-root]/[target-configuration-name]` - `[project-root]/[parent-target-name]` - `[project-root]/[non-atomized-target-name]` - `[project-root]/{general environment variables}` - `[target-name].[target-configuration-name]` - `[target-configuration-name]` - `[parent-target-name]` - `[non-atomized-target-name]` - `{general environment variables}` ### Environment variables for configurations Nx only loads environment files for a configuration when that configuration is defined for the task. If the `build` target for `app` does not define a `development` configuration, the following command uses `.env.build` instead of `.env.build.development`: ```shell nx build app --configuration development ``` To load `.env.build.development`, define the `development` configuration for the task, even if the configuration is empty: ```jsonc {% meta="{5-7}" %} // apps/app/project.json { "targets": { "build": { // ... "configurations": { "development": {}, }, }, }, } ``` ### Load a custom environment file To load variables from an environment file that is not listed above, use one of the following options: 1. Use [env-cmd](https://www.npmjs.com/package/env-cmd): `env-cmd -f .qa.env nx serve` 2. Use [dotenvx](https://github.com/dotenvx/dotenvx): `dotenvx run --env-file=.qa.env -- nx serve` 3. Use the `envFile` option of the [run-commands executor](/docs/kb/run-commands-executor#envfile) ### Ad-hoc environment variables You can also set environment variables for an individual command by using your operating system and shell. **Unix systems** Set the environment variable before the command: ```shell NX_PUBLIC_API_URL=http://localhost:3333 nx build myapp ``` **Windows (cmd.exe)** ```shell set "NX_PUBLIC_API_URL=http://localhost:3333" && nx build myapp ``` **Windows (PowerShell)** ```shell ($env:NX_PUBLIC_API_URL = "http://localhost:3333") -and (nx build myapp) ``` ## Nx environment variables The following environment variables are ones that you can set to change the behavior of Nx in different environments. Everyday settings are listed under **Common**. Settings for debugging, internals, and specialized tooling are under **Advanced**. ### Common | Property | Type | Description | | ----------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `NX_BAIL` | boolean | If set to `true`, Nx will stop command execution after the first failed task. Can be overridden on the command line with `--nxBail`. | | `NX_BASE` | string | The default base branch to use when calculating the affected projects. Can be overridden on the command line with `--base`. | | `NX_BATCH_MODE` | boolean | If set to `true`, Nx will run task(s) in batches for executors which support batches. | | `NX_CACHE_DIRECTORY` | string | The cache for task outputs is stored in a shared `~/.nx//cache` directory by default, where the id identifies the workspace. Set this variable to use a different directory. | | `NX_CACHE_FAILURES` | boolean | If set to `true`, Nx caches failing tasks in addition to successful ones. | | `NX_DAEMON` | boolean | If set to `false`, disables the Nx daemon process. Disable the daemon to print `console.log` statements in plugin code you are developing. | | `NX_DEFAULT_OUTPUT_STYLE` | string | The default output style to use when running tasks. Can be overridden on the command line with `--outputStyle`. | | `NX_DEFAULT_PROJECT` | string | The default project used for commands which require a project. e.g. `nx build`, `nx g component`, etc. | | `NX_DISABLE_NX_CACHE` | boolean | Rerun the tasks even when the results are available in the cache. | | `NX_DISABLE_REMOTE_CACHE` | boolean | Disable all remote caching features. This means that the remote cache will not be read from or written to. Includes Nx Cloud and self-hosted caches. | | `NX_DRY_RUN` | boolean | If set to `true`, will perform a dry run of the generator. No files will be created and no packages will be installed. | | `NX_HEAD` | string | The default head branch to use when calculating the affected projects. Can be overridden on the command line with `--head`. | | `NX_IGNORE_CYCLES` | boolean | If set to `true`, Nx will ignore errors created by a task graph circular dependency. Can be overridden on the command line with `--nxIgnoreCycles` | | `NX_INTERACTIVE` | boolean | If set to `true`, will allow Nx to prompt you in the terminal to answer some further questions when running generators. | | `NX_LOAD_DOT_ENV_FILES` | boolean | If set to 'false', Nx will not load any environment files (e.g. `.local.env`, `.env.local`) | | `NX_MAX_CACHE_SIZE` | string | Alternative to configuring `maxCacheSize` in `nx.json`. Defines the maximum size of the local task cache. See [`maxCacheSize`](/docs/reference/nx-json#max-cache-size) for supported units and behavior details. | | `NX_MAX_MESSAGE_SIZE` | number | Maximum size, in bytes, of a single message on an Nx socket (daemon, plugin worker, and forked process). Defaults to 2147483648 (2 GiB). A peer that declares a larger message is refused before the payload is buffered. Set to 0 to remove the limit. The daemon reads it at startup, so run `nx reset` after changing it. | | `NX_MIGRATE_CLI_VERSION` | string | The version of Nx to use for running the `nx migrate` command. If not set, it defaults to `latest`. | | `NX_MIGRATE_SKIP_INSTALL` | boolean | If set to `true`, `nx migrate --run-migrations` will not automatically perform the installation of the packages. | | `NX_MIGRATE_USE_LOCAL` | boolean | If set to `true`, will use the locally installed version of `nx` instead of downloading the latest version to run the `nx migrate` command. | | `NX_MULTI_MAJOR_MODE` | string | How `nx migrate` handles a jump across majors. `direct` goes straight to the target, `gradual` goes to the smallest recommended step. Overrides [`migrate.multiMajorMode`](/docs/reference/nx-json#migrate). | | `NX_PARALLEL` | number | The number of tasks Nx should run in parallel. Overrides any configured value inside nx.json | | `NX_SKIP_NX_CACHE` | boolean | Rerun the tasks even when the results are available in the cache. | | `NX_SKIP_REMOTE_CACHE` | boolean | Disable all remote caching features. This means that the remote cache will not be read from or written to. Includes Nx Cloud and self-hosted caches. | | `NX_TUI` | boolean | If set to `true` or `false`, will enable or disable the Terminal UI (TUI) for running tasks. TUI provides an interactive visual interface for task execution | | `NX_TUI_AUTO_EXIT` | boolean/number | Controls whether the TUI automatically exits after tasks finish. If `true`, exits immediately. If `false`, never exits automatically. If a number, shows a countdown for that many seconds before exiting | | `NX_USE_LOCAL` | boolean | If set to `true`, Nx will use the locally installed version instead of downloading the latest version from npm for commands like `nx init` and `nx migrate`. | | `NX_VERBOSE_LOGGING` | boolean | If set to `true`, will print debug information useful for troubleshooting | | `NX_WORKSPACE_DATA_DIRECTORY` | string | The project graph database is stored in a shared `~/.nx//databases` directory by default, where the id identifies the workspace. This checkout's own `.nx/workspace-data` holds its daemon logs, file map and source maps. Set this variable to put all of them in the directory you name, which also opts the cache out of its shared location. | ### Advanced | Property | Type | Description | | ------------------------------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `NX_ADD_PLUGINS` | boolean | If set to `false`, Nx will not add plugins to infer tasks. This is `true` by default. | | `NX_CACHE_PROJECT_GRAPH` | boolean | If set to `false`, disables the project graph cache. Most useful when developing a plugin that modifies the project graph. | | `NX_COMPILE_CACHE` | boolean | If set to `false`, disables Node's built-in V8 bytecode compile cache for Nx processes (CLI, daemon, plugin workers). The cache is enabled by default on Node 22.8+ and speeds up repeated invocations by reusing compiled bytecode. Has no effect on older Node versions. | | `NX_COMPLETE` | string | Internal. Set by the shell completion wrapper scripts (generated by `nx completion `) to put Nx in tab-completion mode. The value is the shell name (`bash`, `zsh`, `fish`, or `powershell`). Do not set this manually. | | `NX_DAEMON_SOCKET_DIR` | string | Alias of `NX_SOCKET_DIR`, used only when `NX_SOCKET_DIR` is not set. Despite the name, it controls all Nx socket placements, not just the daemon socket. Prefer `NX_SOCKET_DIR` in new setups. | | `NX_FORCE_REUSE_CACHED_GRAPH` | boolean | If set to `true`, Nx will reuse an existing cached project graph when available and skip recomputing it. Useful in short-lived CI steps that run immediately after a step which already computed the graph. | | `NX_FORMAT_SORT_TSCONFIG_PATHS` | boolean | If set to `true`, generators will sort the TypeScript path mappings in the root tsconfig file. | | `NX_GENERATE_QUIET` | boolean | If set to `true`, will prevent Nx logging file operations during generate | | `NX_INVOCATION_ROOT_PID` | number | Internal. Set by Nx to the PID of the root Nx process. Used to detect recursive task invocation loops across nested Nx processes. Do not set this manually. | | `NX_ISOLATE_PLUGINS` | boolean | Forces plugin isolation on or off, overriding the automatic detection. Set to `true` to always run inference plugins in isolated workers, or `false` to always run them in-process. | | `NX_MIGRATE_INSTALL_CONCURRENCY` | number | Limits the number of concurrent package installs when fetching migration metadata. Useful for avoiding package manager cache conflicts on Windows with private registries. If not set, installs run with no concurrency limit. | | `NX_MIGRATE_SKIP_REGISTRY_FETCH` | boolean | **Deprecated, will be removed in Nx 24** - use `NX_MIGRATE_USE_REGISTRY_RESOLUTION` (set to `false`) instead. If set to `true`, will skip fetching metadata from the registry and instead use the installation method directly. Legacy alias for setting `NX_MIGRATE_USE_REGISTRY_RESOLUTION` to `false`; the newer variable takes precedence when both are set. | | `NX_MIGRATE_USE_REGISTRY_RESOLUTION` | boolean | Whether `nx migrate` resolves package versions and migration metadata via the npm registry (faster) instead of a package-manager install. Set to `false` to always resolve through your package manager. Takes precedence over the legacy `NX_MIGRATE_SKIP_REGISTRY_FETCH` variable and the `migrate.useRegistryResolution` setting in `nx.json`. Defaults to `true`. | | `NX_NATIVE_COMMAND_RUNNER` | boolean | If set to `false`, disables the native pseudo-terminal command runner and falls back to the standard Node.js child process. Enabled by default. | | `NX_NATIVE_FILE_CACHE_DIRECTORY` | string | The cache for native `.node` files is stored under a per-user temp directory by default. Set this variable to use a different directory, interpreted as an absolute path. It has to be a directory only your user can reach, since Nx loads a native binding out of it; Nx skips the cache and loads the binding in place if it isn't. | | `NX_NATIVE_FILE_LOGGING` | string | Enables the native (Rust) tracing logger and writes logs to `.nx/workspace-data/nx.log`. Accepts the same filter directives as `NX_NATIVE_LOGGING` (e.g. `debug`, `info`, `[{project_name=myapp}]`). Useful for debugging native code paths. | | `NX_NATIVE_LOGGING` | string | Filter directive for the native (Rust) tracing logger. Defaults to `nx::native=info`. Set to a log level (`trace`, `debug`, `info`, `warn`, `error`, `off`) to control all native logs, or scope by crate or module (e.g. `nx=trace`, `nx::native::tasks::hashers=debug`, `[{project_name=myapp}]`). | | `NX_PERF_LOGGING` | boolean | If set to `true`, will print debug information useful for profiling executors and Nx itself | | `NX_PLUGIN_NO_TIMEOUTS` | boolean | If set to `true`, plugin operations will not timeout | | `NX_PREFER_NODE_STRIP_TYPES` | boolean | When the Node.js runtime exposes native TypeScript stripping (`process.features.typescript` - Node 23.6+ unflagged, 22.18+ LTS unflagged, or 22.6+ with `--experimental-strip-types`), Nx loads TypeScript configuration files via Node directly (faster, no `@swc-node/register`/`ts-node` registration). Set to `false` to opt out and force `@swc-node/register` or `ts-node`. When native stripping fails on an unsupported construct (e.g. `enum`, runtime `namespace`), Nx falls back to `@swc-node/register`/`ts-node` + `tsconfig-paths` automatically; set `NX_VERBOSE_LOGGING=true` to see when fallback triggers. | | `NX_DISABLE_TSCONFIG_PATHS` | boolean | Skip `tsconfig-paths` registration on the `@swc-node/register`/`ts-node` fallback path. Set to `true` when relying on package manager workspaces (pnpm/yarn/npm) for project linking and `tsconfig` path aliases aren't needed. | | `NX_PREFER_TS_NODE` | boolean | If set to `true`, Nx will use `ts-node` for local execution of plugins even if `@swc-node/register` is installed. | | `NX_PROCESS_KILL_GRACE_PERIOD` | number | Time in milliseconds to wait for child processes to exit gracefully before force-killing them. Defaults to `5000`. | | `NX_PROFILE` | string | Prepend `NX_PROFILE=profile.json` before running targets with Nx to generate a file that be [loaded in Chrome dev tools](/docs/kb/performance-profiling) to visualize the performance of Nx across multiple processes. | | `NX_PROJECT_GRAPH_CACHE_DIRECTORY` | string | Alias of `NX_WORKSPACE_DATA_DIRECTORY`, used only when `NX_WORKSPACE_DATA_DIRECTORY` is not set. Prefer `NX_WORKSPACE_DATA_DIRECTORY` in new setups. | | `NX_REJECT_UNKNOWN_LOCAL_CACHE` | boolean | Legacy cache safety toggle. Set to `0` or `false` to allow reading a local cache that wasn't populated by the current machine. Not supported with the new database cache. See [legacy cache](/docs/reference/deprecated/legacy-cache#nxrejectunknownlocalcache). | | `NX_RUNNER` | string | No longer read by Nx. Use `NX_TASKS_RUNNER` instead. | | `NX_SELF_HOSTED_REMOTE_CACHE_ACCESS_TOKEN` | string | Bearer token passed as the `Authorization` header when contacting the self-hosted remote cache server configured via `NX_SELF_HOSTED_REMOTE_CACHE_SERVER`. | | `NX_SELF_HOSTED_REMOTE_CACHE_SERVER` | string | URL of a self-hosted remote cache server. When set, Nx uses this endpoint for remote cache reads and writes instead of Nx Cloud. | | `NX_SKIP_ATOMIZER_VALIDATION` | boolean | If set to `true`, suppresses the atomizer warning Nx shows when Nx Cloud isn't configured. | | `NX_SKIP_FORMAT` | boolean | If set to `true`, skips code formatting in generators, migrations, `nx init`, `nx import`, and `create-nx-workspace`. Useful for repositories that format with a tool Nx does not drive, such as Biome or dprint, or that have custom formatting requirements. | | `NX_SKIP_LOG_GROUPING` | boolean | If set to `true`, Nx will not group command's logs on CI. | | `NX_SKIP_NATIVE_FILE_CACHE` | boolean | If set to `true`, disables the native `.node` file cache. Nx otherwise copies native bindings to a cache directory to avoid file-locking issues when multiple processes load them. | | `NX_SKIP_PROVENANCE_CHECK` | boolean | If set to `true`, skips `npm` provenance verification when installing packages during `nx migrate`. This is a security-sensitive check. Only disable it if you understand the implications. | | `NX_SKIP_VSCODE_EXTENSION_INSTALL` | boolean | If set to `true`, skips the automatic installation of the Nx Console extension for supported editors. Set this in environments where the temp file that we store this information in otherwise isn't accessible. | | `NX_SOCKET_DIR` | string | Directory for all Nx sockets (daemon, forked process, and plugin). Used as the socket directory itself, replacing the default locations rather than joining them. Mainly a workaround for a socket path that exceeds the OS length limit, or a default location the environment restricts. Takes precedence over `NX_DAEMON_SOCKET_DIR`. Must name a directory only your user can reach. | | `NX_TASKS_RUNNER` | string | The name of task runner from the config to use. Can be overridden on the command line with `--runner`. | | `NX_TASKS_RUNNER_DYNAMIC_OUTPUT` | boolean | If set to `false`, will use non-dynamic terminal output strategy (what you see in CI), even when you terminal can support the dynamic version | | `NX_USE_V8_SERIALIZER` | boolean | If set to `true`, Nx will use v8 serialization on its socket channels (the daemon client and server, plugin workers, and pseudo-IPC) instead of JSON. Improves throughput for workspaces with large task payloads. | | `NX_WRAPPER_SKIP_INSTALL` | boolean | If set to `true`, the `.nx/nxw.js` wrapper skips verifying and self-installing the pinned Nx version before each command. | ## Plugin environment variables The following environment variables can be used to configure specific Nx plugins. This is useful in CI environments where the required tooling (e.g., Java) is not available or where plugin behavior needs to be customized. | Property | Type | Description | | --------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `NX_DOTNET_DISABLE` | boolean | If set to `true`, disables the `@nx/dotnet` plugin. Useful in environments where .NET SDK is not available. | | `NX_DOTNET_PROJECT_GRAPH_TIMEOUT` | number | Timeout in seconds for .NET project graph generation. Defaults to `600` in CI and `120` locally. | | `NX_GRADLE_DISABLE` | boolean | If set to `true`, disables the `@nx/gradle` plugin. Useful in environments where Java/Gradle is not available. | | `NX_GRADLE_PROJECT_GRAPH_TIMEOUT` | number | Timeout in seconds for Gradle project graph generation. Defaults to `600` in CI and `120` locally. Increase this value for large Gradle workspaces that need more time to resolve. | | `NX_MAVEN_DISABLE` | boolean | If set to `true`, disables the `@nx/maven` plugin. Useful in environments where Java/Maven is not available. | | `NX_MAVEN_ANALYSIS_TIMEOUT` | number | Timeout in seconds for Maven analysis. Defaults to `600` in CI and `120` locally. | Nx will set the following environment variables so they can be accessible within the process even outside of executors and generators. | Property | Type | Description | | ------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `NX_TASK_TARGET_PROJECT` | string | Set to the project name of the task being run. Use this to tell which project is being run. | | `NX_TASK_TARGET_TARGET` | string | Set to the target name of the task being run. Use this to tell which target of the project is being run. | | `NX_TASK_TARGET_CONFIGURATION` | string | Set to the configuration name of the task being run. Use this to tell which configuration of the target is being run. | | `NX_GRAPH_CREATION` | boolean | Set to `true` during the graph creation process. Use this to have inference plugins run different code during graph creation versus during task execution | | `NX_DRY_RUN` | boolean | Set to `true` during dry runs of generators. Use this to avoid side effects during generators. | | `NX_INTERACTIVE` | boolean | Set to `false` when running generators with `--interactive=false`. Use this to prevent prompting during generators | | `NX_RUNNING_NX_IMPORT` | boolean | Set to `true` while the `nx import` command is executing. Use this to have plugin code or scripts behave differently during an import (e.g. adjust telemetry or logging). | | `NX_RUNNING_NX_INIT` | boolean | Set to `true` while the `nx init` command is executing. Use this to have plugin code or scripts behave differently during workspace initialization. | ## Nx Cloud environment variables The following environment variables are specific to Nx Cloud and can be used to configure cloud-specific behavior: {% aside type="tip" title="Verbose Logging" %} Similar to the Nx CLI, Nx Cloud also uses the `NX_VERBOSE_LOGGING` environment variable to output debug information {% /aside %} | Property | Type | Description | | ------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `NX_BRANCH` | string | The current branch name. For most CI providers, `nx-cloud` determines this automatically. Must be set to the PR number for GitHub, BitBucket and GitLab integrations to work properly. | | `NX_CLOUD_DISABLE_METRICS_COLLECTION` | boolean | Disables collection of CPU and memory metrics during task execution (collected by default on Nx 22.1+). See [Resource Usage](/docs/features/ci-features/resource-usage). | | `NX_CLOUD_METRICS_DIRECTORY` | string | Directory where Nx writes resource metrics during task execution. See [Resource Usage](/docs/features/ci-features/resource-usage). | | `NX_CI_EXECUTION_ID` | string | A unique identifier for the current CI run or job. For most CI providers, `nx-cloud` determines this automatically. The value on the main job must match the value on all agents. | | `NX_CI_EXECUTION_ENV` | string | Used when you have multiple main jobs (e.g., running CI on both Linux and Windows). The main job with this env variable will connect to agents with the same env name. Also selects the matching [`overrides`](/docs/reference/nx-cloud/ci-config#overrides) entry in `.nx/ci-config.yaml`. | | `NX_AGENT_NAME` | string | Should only be used when you bring your own compute. Names the agent in logs and in the Nx Cloud UI. Falls back to the CircleCI job name, then to a generated name. | | `NX_AGENT_INSTANCE_NAME` | string | Should only be used when you bring your own compute. Distinguishes individual agent instances that share the same `NX_AGENT_NAME`. | | `NX_AGENT_LAUNCH_TEMPLATE` | string | Should only be used when you bring your own compute. Attaches a launch template type to your agents to leverage assignment rules for task distribution. | | `NX_CLOUD_ACCESS_TOKEN` | string | Configure the Nx Cloud access token. Takes precedence over the `accessToken` property in `nx.json`. Common to have a read-only token in `nx.json` and a read-write token set via this environment variable in CI. | | `NX_CLOUD_API` | string | The URL of the Nx Cloud instance to connect to. Overrides `nxCloudUrl` in `nx.json`. | | `NX_CLOUD_CONTINUOUS_ASSIGNMENT` | boolean | Enables continuous assignment for Nx Agents. Required until Nx Cloud enables continuous assignment by default for all workspaces soon. | | `NX_CLOUD_ENCRYPTION_KEY` | string | Enable end-to-end encryption of artifacts. Artifacts will be encrypted/decrypted on your machine. Can also be set via the `nxCloudEncryptionKey` property in `nx.json`. | | `NX_CLOUD_NO_TIMEOUTS` | boolean | Disables the default 10-second timeout for Nx Cloud requests. | | `NX_NO_OUTPUT_TIMEOUT` | string | Overrides the launch template's [`no-output-timeout`](/docs/kb/launch-templates#launch-templatestemplate-nameno-output-timeout) for an Nx Agent step. Duration string (e.g., `10m`, `1h`); `"0"` disables. | | `NX_NO_CLOUD` | boolean | If set to `true`, prevents Nx Cloud from being used for the current run. No remote cache will be used. Value cannot be set if using [distributed task execution](/docs/features/ci-features/distribute-task-execution) | | `NX_VERBOSE_LOGGING` | boolean | Outputs debug information about agents communicating with the main job. Useful for debugging cache misses and on-premises setup issues. | | `NX_WORKING_DIRECTORY` | string | The subdirectory containing your Nx workspace. Set this in your launch template if your Nx workspace is not at the repository root. Only used by Nx Agents. | | `NX_CLOUD_FORCE_REVALIDATE` | boolean | If set to `true`, forces the Nx Cloud client updater to re-check for a new bundle on every run even when a cached bundle is still valid. Useful when testing new `nx-cloud` client releases. | | `NX_CLOUD_AGENT_TIMEOUT_MS` | number | Timeout in milliseconds an agent waits to hear from Nx Cloud before erroring. Defaults to `3600000` (60 minutes). | | `NX_CLOUD_ORCHESTRATOR_TIMEOUT_MS` | number | Timeout in milliseconds the orchestrator (main job) waits for a completed task response from Nx Cloud. Defaults to `3600000` (60 minutes). | | `NX_CLOUD_NUMBER_OF_RETRIES` | number | How many times Nx Cloud retries HTTP requests that fail with a transient error. Defaults to `10` on CI and `0` locally. | | `NX_CLOUD_DISTRIBUTED_EXECUTION` | boolean | Typically not needed. When set to `false`, disables distributed task execution. Prefer the `--no-dte` or `--no-agents` flags. | ### Deprecated Nx Cloud environment variables | Property | Type | Description | | ------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `NX_RUN_GROUP` | string | Older versions of `nx-cloud` used this instead of `NX_CI_EXECUTION_ID` and `NX_CI_EXECUTION_ENV`. Served the same purpose. | | `NX_CLOUD_DISTRIBUTED_EXECUTION_STOP_AGENTS_ON_FAILURE` | boolean | When set to `true`, tells Nx Cloud to stop agents if a command fails. Use [`lifecycle.fail-after`](/docs/reference/nx-cloud/ci-config#lifecyclefail-after) in `.nx/ci-config.yaml` instead, where `1` stops on the first failure and `0` lets the run finish. | | `NX_CLOUD_AUTH_TOKEN` | string | No longer needed. Use `NX_CLOUD_ACCESS_TOKEN` instead. Still read for backwards compatibility. | | `NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT` | number | No longer needed. Nx Cloud now derives the agent count from the launch template and workspace configuration automatically. | --- ## ESBuild For setup and guides, see [Nx with esbuild](/docs/technologies/build-tools/esbuild/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/ESBuild" /%} --- ## ESLint For setup and guides, see [Nx with ESLint](/docs/technologies/eslint/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/ESLint" /%} --- ## Nx Glossary This is a short list of Nx-specific terms that will help you understand the rest of the documentation. ## Terms ### Application A [project](#project) that can run on its own. Generally uses [libraries](#library). ### Atomizer The Atomizer is an Nx Cloud feature that automatically splits tasks so that they can be executed in parallel. Atomizer automatically splits tasks on [Nx Agents](#nx-agents), providing you detailed task information in the UI, including interpreted logs and artifacts, automatically grouped by project and workflow. > See: [Automatically Split E2E Tasks](/docs/features/ci-features/split-e2e-tasks) ### Batch mode A way to run multiple [tasks](#task) in a single process instead of spawning a separate process for each one. Batch mode reduces per-process startup overhead and lets tools like `tsc` and Gradle share state across tasks. Enable it by setting `NX_BATCH_MODE=true` or passing `--batch`. Not all plugin executors support batch mode, check the specific plugin [technology page](/docs/plugin-registry) to see if it's supported. ### Buildable library A [library](#library) that has a `build` [target](#target). Some libraries can be generated with a `build` target using the `--buildable` flag. > See: [Publishable and Buildable Nx Libraries](/docs/kb/buildable-and-publishable-libraries) ### Cache A mechanism for saving the output of a calculation so it can be replayed later without actually performing the calculation again. > See: [Cache Task Results](/docs/features/cache-task-results) ### Cache hit When the [cache inputs](#cache-inputs) for a [task](#task) match an existing entry in the [cache](#cache) and the [cache outputs](#cache-outputs) can be replayed without actually running the task. > See: [Cache Task Results](/docs/features/cache-task-results) ### Cache inputs Everything that might change the output of a [task](#task). This may include source code, task options, environment variables and other settings determined at run time. These values are combined as a hash to serve as a key for an entry in the [cache](#cache). > See: [Customizing Inputs and Named Inputs](/docs/kb/configure-inputs) ### Cache miss When the [cache inputs](#cache-inputs) for a [task](#task) do not match an existing entry in the [cache](#cache) and the task needs to be executed. > See: [Cache Task Results](/docs/features/cache-task-results) ### Cache outputs The terminal output and any file artifacts created by running a [task](#task). These values are stored in the [cache](#cache) to be replayed later. > See: [Outputs Reference](/docs/reference/project-configuration#outputs) ### CIPE (Continuous integration pipeline execution) CI Pipeline Executions are a grouping mechanism for all Nx and Nx Cloud actions that occur within a single continuous integration pipeline. The Nx Cloud task runner automatically detects when it is running in a CI environment, so this grouping is done automatically. In almost all cases, this detection will be a 1:1 match with your CI provider's concept of a "pipeline" or "workflow". ### Command Anything you run in the terminal. An example command that invokes a [task](#task) is `nx build my-app`. > See: [Run Tasks](/docs/features/run-tasks) ### Configurations A set of preconfigured options for a [target](#target) that should be enabled altogether. For example, a `production` configuration would set all the options needed for a build that could be deployed to production. > See: [Use Task Configurations](/docs/kb/executors-and-configurations#use-task-configurations) ### Distributed task execution A system for running [tasks](#task) in CI across multiple agent processes across many machines. [Nx Agents](#nx-agents) enables distributed task execution on agent machines that are managed by Nx. > See: [Distribute Task Execution](/docs/features/ci-features/distribute-task-execution) ### Executor A script that performs some action on your code. This can include building, linting, testing, serving and many other actions. A [target](#target) configuration specifies an executor and a set of options. Executors can be found in [plugins](#plugin). > See: [Executors and Configurations](/docs/kb/executors-and-configurations) ### Flake rate A metric that measures how often a task succeeds due to flakiness rather than reliable code. It represents the percentage of total successes that came from unreliable (flaky) task hashes: `flaky_successes / (flaky_successes + non_flaky_successes)`. This answers the question: "Of all the times this task succeeded, how many successes came from unreliable code?" > See: [Flaky task analytics](/docs/features/ci-features/flaky-tasks#flaky-task-analytics) ### Flaky tasks Tasks that will sometimes succeed and sometimes fail without any change to the inputs. These tasks are often e2e tests and are particularly problematic in CI. Nx Cloud automatically detects flaky tasks and re-runs them. > See: [Detect and Retry Flaky Tasks](/docs/features/ci-features/flaky-tasks) ### Generator A script that creates or modifies your code. Generators can be found in [plugins](#plugin). > See: [Use Code Generators](/docs/features/generate-code) ### Graph A computer science concept that consists of nodes connected by edges. In the Nx context, there are two graphs: the [project](#project) graph which describes dependencies between projects and the [task](#task) graph which describes dependencies between tasks. > See: [Explore the Graph](/docs/features/explore-graph) ### Impact score A metric used in [Flaky Task Analytics](#flaky-tasks) to prioritize which flaky tasks need attention. Calculated as `flake_rate × sample_size`, it weights frequently-run flaky tasks higher than rarely-run flaky tasks. For example, a task with 50% flake rate and 100 runs has an impact score of 50, the same as a task with 10% flake rate and 500 runs. > See: [Flaky task analytics](/docs/features/ci-features/flaky-tasks#flaky-task-analytics) ### Launch template Launch Templates are used to set up an agent machine. They specify a resource class, an image and a series of set up steps before tasks are executed on that machine. > See: [Launch Templates](/docs/kb/launch-templates) ### Library A [project](#project) that is used by [applications](#application) or other [libraries](#library). ### Monolith A large [application](#application) that is difficult to separate into smaller pieces. ### Monorepo A repository with multiple [projects](#project). > See: [monorepo.tools](https://monorepo.tools) ### Nested project A [project](#project) that is located in a sub-folder of another project. This was made possible in Nx 15.3. ### Nx Agents A paid service that hosts and manages customizable worker machines to run parts of your CI pipeline faster and cheaper than a traditional CI provider. When possible, Nx Agents uses [distributed task execution](#distributed-task-execution) to optimize your pipeline. > See: [Distribute Task Execution](/docs/features/ci-features/distribute-task-execution) ### Nx Cloud The umbrella term for all CI-related products that Nx provides. > See: [CI with Nx](/docs/kb/setup-ci) ### Nx Replay Nx Replay is the product offered by [Nx Cloud](#nx-cloud) that enables [remote caching](#remote-cache). > See: [Use Remote Caching](/docs/features/ci-features/remote-cache) ### Package A [project](#project). It is sometimes published as an npm package. ### Plugin A set of [executors](#executor), [generators](#generator) and other code that extends the functionality of Nx. May be installed from a package manager like NPM or developed directly in the repository. > See: [Create Your Own Plugin](/docs/kb/add-language-support) ### Polyrepo Related [projects](#project) spread across multiple repositories. ### Project The unit of code on which a [task](#task) can be run. A project can be an [application](#application) or a [library](#library). ### Publishable library A [library](#library) that has a `publish` [target](#target). Some libraries can be generated with a `publish` target using the `--publishable` flag. > See: [Publishable and Buildable Nx Libraries](/docs/kb/buildable-and-publishable-libraries) ### Remote cache A [cache](#cache) shared between CI and developer machines. > See: [Share Your Cache](/docs/features/ci-features/remote-cache) ### Root-level project A [project](#project) that has its own root folder at the root of the repository. Every other project in the repo is a [nested project](#nested-project). ### Run Runs in Nx Cloud are Nx commands that result in some number of `Task`s being performed. - `nx run my-app:build` is a run that performs a single task - `nx run-many -t test,build` is a run that performs many tasks. - `nx affected -t test,build` is a run that performs zero to many tasks. When a run exists in a CI environment, Nx Cloud will automatically associate it with the appropriate `Run Group` and `CI Pipeline Execution` ### Run group Run groups are a more specific grouping mechanism for Nx and Nx Cloud actions, and exist as children within a single CI Pipeline Execution. Typically, run groups are used when there are different _hardware_ requirements (ex: a Linux run group, and a MacOS run group) for the different commands. When using Distributed Task Execution, Nx Agents can only belong to a single run group. This means each additional run group requires its own pool of agents. ### Standalone repository A repository with a single [application](#application) at the [root level](#root-level-project). This set up is made possible in Nx 15.3. ### Target The name of an action taken on a [project](#project). > See: [Run Tasks](/docs/features/run-tasks) ### Task An invocation of a [target](#target) on a specific [project](#project). > See: [Run Tasks](/docs/features/run-tasks) ### Task pipeline The set of dependencies between [tasks](#task) that ensure that tasks are run in the correct order. > See: [Task Pipeline Configuration](/docs/concepts/task-pipeline-configuration) ### Workspace A git repository. An Nx workspace is a git repository using Nx. --- ## Inputs and Named Inputs When Nx [computes the hash for a given operation](/docs/concepts/how-caching-works), it takes into account the `inputs` of the target. The `inputs` are a list of **file sets**, **runtime** inputs, **environment variables**, and **working directory** that affect the output of the target. If any of the `inputs` change, the cache is invalidated and the target is re-run. ## Types of inputs Nx can consider the following types of Inputs when computing the hash for a given operation. ### Project configuration Nx always considers the configuration of the project of a task and its dependencies when calculating the hash of a task. ### Command arguments Passing different arguments to an Nx command will often change the behavior of a task. Nx will filter out arguments which are for Nx itself and do not have any effect on running of tasks such as `--parallel` or `--projects`. For example, running `nx build myreactapp --prod` will not reuse the cached output of running `nx build myreactapp`. ### Source files Changing source code will often change the behavior of a task. Nx can consider the contents of files matching a pattern when calculating the computation hash. Source file inputs are defined like this: ```jsonc { "inputs": [ "{projectRoot}/**/*", // All files in a project "{workspaceRoot}/.gitignore", // A specific file in the workspace "{projectRoot}/**/*.ts", // A glob pattern for files "!{projectRoot}/**/*.spec.ts", // Excluding files matching a glob pattern ], } ``` Source file inputs can be specified as strings with glob patterns. These patterns must be prefixed with either `{projectRoot}` or `{workspaceRoot}` to distinguish where the paths should be resolved from. `{workspaceRoot}` should only appear in the beginning of an input but `{projectRoot}` and `{projectName}` can be specified later in the input to interpolate the root or name of the project into the input location. Alternatively, you can use the object format with the `fileset` property: ```jsonc { "inputs": [{ "fileset": "{projectRoot}/**/*" }], } ``` #### Directory paths When specifying a directory as an input, you must use a trailing slash or a glob pattern. A path without a trailing slash or glob pattern will be treated as a file path and will not match any files within the directory. ```jsonc // nx.json { "inputs": [ "{projectRoot}/src/", // ✓ Matches all files in src (note the trailing slash) "{projectRoot}/src/**/*", // ✓ Matches all files in src using glob "{projectRoot}/src", // ✗ Does NOT match files - treated as a file path ], } ``` {% aside type="note" title="Difference from Outputs" %} This behavior differs from `outputs`, which support naked directory paths without a trailing slash. For example, `{projectRoot}/dist` works as an output but would not work as an input. {% /aside %} #### Token behavior with nested projects These tokens behave differently when dealing with nested projects: - `{projectRoot}/**/*` only includes files that are assigned to the specific project. Files in nested projects are excluded. - `{workspaceRoot}/path/**/*` includes all files matching the pattern in the entire workspace, including files from nested projects. For example, in a structure like `packages/parent/nested-child/`, using `{projectRoot}/**/*` for the `parent` project will exclude files from `nested-child`, while `{workspaceRoot}/packages/parent/**/*` will include them. {% aside type="tip" title="Gitignored Files Are Excluded" %} Files listed in `.gitignore` are automatically excluded from inputs. Nx will not consider gitignored files when computing the hash for tasks; therefore, changes to ignored files will not invalidate the cache. {% /aside %} Prefixing a source file input with `!` will exclude the files matching the pattern from the set of files used to calculate the hash. Prefixing a source file input with `^` means this entry applies to the project dependencies of the project, not the project itself. This allows you to specify exactly which files from dependency projects should be considered as inputs. ```jsonc { "inputs": [ "{projectRoot}/**/*.ts", // .ts files in the current project "^{projectRoot}/**/*.ts", // .ts files in all dependency projects "^{workspaceRoot}/tools/**/*", // workspace-level files, applied per dependency ], } ``` You can also use the object format with the `dependencies` property to achieve the same result: ```jsonc { "inputs": [{ "fileset": "{projectRoot}/**/*.ts", "dependencies": true }], } ``` By default, Nx will use all files in a project as well as all files in the project's dependencies when computing a hash for tasks belonging to the project. This may cause Nx to rerun some tasks even when files irrelevant to the task have changed but it ensures that by default, Nx always re-runs the task when it should. To get a better idea of how to use inputs, you can browse some [common input sets](/docs/kb/configure-inputs#common-inputs). ### JSON files Configuration files such as `package.json` or `tsconfig.base.json` hold fields that are irrelevant to most tasks. Nx can hash a subset of a JSON file so that edits to the remaining fields do not invalidate the cache. JSON inputs are defined like this: ```jsonc { "inputs": [ { "json": "{projectRoot}/package.json" }, // The whole file { "json": "{projectRoot}/package.json", "fields": ["dependencies"] }, // Only the listed fields { "json": "{workspaceRoot}/tsconfig.base.json", "excludeFields": ["compilerOptions.paths"], // Everything except the listed fields }, ], } ``` The `json` path must be prefixed with `{workspaceRoot}` or `{projectRoot}`, the same as a source file input, and it can be a glob pattern that matches several files. Both `fields` and `excludeFields` accept dot notation to reach nested values, such as `compilerOptions.target`. Fields that a file does not contain are skipped. When both properties are set, `fields` selects first and `excludeFields` then removes from that selection. A JSON input hashes the parsed contents of the file rather than its raw bytes, so reformatting the file or reordering its keys does not invalidate the cache. Files that parse as neither JSON nor JSONC fall back to hashing the raw contents, and the field filters do not apply to them. ### Environment variables Tools and scripts will often use some environment variables to change their behavior. Nx can consider the value of environment variables when calculating the computation hash in order to invalidate the cache if the environment variable value changes. Environment variable inputs are defined like this: ```jsonc { "inputs": [ { "env": "API_KEY" }, // this will include the value of $API_KEY in the cache hash ], } ``` ### Runtime inputs You can use a Runtime input to provide a script which will output the information you want to include in the computation hash. Runtime inputs are defined like this: ```jsonc { "inputs": [{ "runtime": "node --version" }], } ``` This kind of input is often used to include versions of tools used to run the task. You should ensure that these scripts work on any platform where the workspace is used. Avoid using `.sh` or `.bat` files as these will not work across Windows and \*nix operating systems. ### Working directory Some tasks may behave differently depending on which directory they are run from. Nx can include the current working directory in the computation hash to ensure that tasks are re-run when the working directory changes. Working directory inputs are defined like this: ```jsonc { "inputs": [ { "workingDirectory": "relative" }, // Hash the path relative to workspace root ], } ``` The `workingDirectory` input accepts two values: - `"relative"` - Hashes the relative path from the workspace root to the current working directory. This is useful when tasks should be invalidated when run from different subdirectories within the workspace. - `"absolute"` - Hashes the full absolute path of the current working directory. This is useful when tasks depend on the exact location on the filesystem. Notably, this will invalidate cache entries if the repository is cloned to a different location on the filesystem, such as between CI and local development environments. ### External dependencies Source code often imports from external dependencies installed through package managers. For example, a React application will likely import `react`. It is not needed to configure those `externalDependencies` directly. Nx will always consider external dependencies depended upon by any source code within the project. External dependencies can also be tools used to run tasks. Updating the versions of those tools will change the behavior of those tasks. External dependencies inputs are defined like this: ```jsonc { "inputs": [{ "externalDependencies": ["jest"] }], } ``` By default, if no external dependencies inputs are specified, Nx will include the hash of all external dependencies of the workspace in the computation hash. For many targets, Nx does not know which external dependencies are used and which are not. By considering all external dependencies, Nx will always re-run tasks when necessary even though in some cases, it might have been able to restore from cache. For example, when defining a target to run `eslint .`, Nx does not know which ESLint plugins are used by the eslint config. To be safe, it will consider all external dependencies, including all ESLint plugins, to ensure that the `lint` task is re-run when any of the plugins have been updated. The drawback of this assumption is that Nx will re-run the `lint` task even when external dependencies not used by ESLint are updated. ```jsonc { "targets": { "lint": { "command": "eslint .", }, }, } ``` This default behavior can be overridden by adding any external dependency inputs and enables Nx to use cached results more often. ```jsonc {% meta="{7}" %} { "targets": { "lint": { "command": "eslint .", "inputs": [ "default", { "externalDependencies": ["eslint", "eslint-config-airbnb"] }, ], }, }, } ``` Targets which use an executor from a Nx Plugin maintained by the Nx Team will have the correct set of external dependencies considered when calculating the hash. ### Outputs of dependent tasks When a task depends on another task, it is possible that the outputs of the dependent task will affect the behavior of the task. The Nx caching mechanism can consider the contents of files produced by dependent tasks that match a pattern. Use outputs of dependent tasks as inputs like this: ```jsonc { "inputs": [ { "dependentTasksOutputFiles": "**/*.d.ts" }, { "dependentTasksOutputFiles": "**/*.d.ts", "transitive": true }, ], } ``` The glob pattern is matched against the file paths resolved from workspace root. For example, if a dependent task has `outputs: ["{workspaceRoot}/dist/{projectRoot}"]` and the project root is `libs/my-lib`, the output path becomes `dist/libs/my-lib`. Files such as `dist/libs/my-lib/index.d.ts` would be matched by patterns `**/*.d.ts`, `dist/**/*.d.ts`, or `dist/libs/my-lib/**/*.d.ts`. Setting `transitive` to `true` will also include outputs of all task dependencies of this task in the [task pipeline](/docs/concepts/task-pipeline-configuration). It's possible the dependent tasks can change over time, so be careful to not overly scope the matching glob to prevent accidental false cache hits. ### A subset of the root `tsconfig.json` or `tsconfig.base.json` When a root `tsconfig.json` or `tsconfig.base.json` is present, Nx will always consider parts of the file which apply to the project of a task being run. This includes the full `compilerOptions` section and particular path mappings in the `compilerOptions.paths` property. This allows Nx to not invalidate every single task when a path mapping is added or removed from the root `tsconfig.json` file ### Named inputs from other projects You can reference named inputs from specific projects or from project dependencies using the object format with the `input` property: ```jsonc { "inputs": [ // Reference a named input from specific project(s) { "input": "production", "projects": "mylib" }, { "input": "production", "projects": ["mylib", "myapp"] }, // Reference a named input from the current project // Equivalent to: { "input": "production" } "production", // Reference a named input from all project dependencies // Equivalent to: { "input": "production", "dependencies": true } "^production", ], } ``` The `projects` property can be a string or an array of strings specifying which project(s) to reference the named input from. Using just the string name (e.g., `"production"`) is shorthand for referencing the named input from the current project. Prefixing with `^` (e.g., `"^production"`) references the named input from all project dependencies. {% aside type="note" title="^ with filesets vs named inputs" %} The `^` prefix behaves differently depending on what follows it: - `^{projectRoot}/**/*.ts` — treated as a **dependency fileset**, hashing matching files in each dependency project - `^{workspaceRoot}/tools/**/*` — treated as a **dependency fileset** with workspace-level files - `^production` — treated as a **named input reference**, resolving the `production` named input for each dependency project In short, if the value after `^` starts with `{projectRoot}` or `{workspaceRoot}`, it is a fileset applied to dependencies. Otherwise, it is interpreted as a named input reference. {% /aside %} ## Named inputs Many tasks will utilize the same sets of inputs with minor differences. Nx allows you to define these sets in the `namedInputs` section of the `nx.json` file. These sets of inputs can then be referenced and reused in the `inputs` array of targets. You can think of these as variables which can be used in the `inputs` array. Named Inputs can be defined for the entire workspace in `nx.json` or for a specific project in `project.json`/`package.json`. ### Workspace level named inputs Named inputs defined in `nx.json` can be used by any project in the workspace in the `inputs` array. The following example shows how to define named inputs for the entire workspace in `nx.json`. ```jsonc {% meta="{3-5}" %} // nx.json { "namedInputs": { "default": ["{projectRoot}/**/*"], // Default Inputs }, } ``` ### Project level named inputs Named inputs defined in `package.json` or `project.json` define the value of the named input for tasks belonging to that specific project. Naming a set of inputs with the same name as a set of inputs defined for the workspace in `nx.json` will override the meaning of that set for the project. {% tabs %} {% tabitem label="project.json" %} ```jsonc {% meta="{3-7}" %} // project.json { "namedInputs": { "default": ["{projectRoot}/**/*", "sharedGlobals"], // Default Inputs "production": ["default", "!{projectRoot}/jest.config.ts"], // Production Inputs "sharedGlobals": [] // Shared Global Inputs }, "targets": { ... } } ``` {% /tabitem %} {% tabitem label="Project Level (package.json)" %} ```jsonc {% meta="{4-8}" %} // package.json { "dependencies": { ... }, "namedInputs": { "default": ["{projectRoot}/**/*", "sharedGlobals"], // Default Inputs "production": ["default", "!{projectRoot}/jest.config.ts"], // Production Inputs "sharedGlobals": [] // Shared Global Inputs }, } ``` {% /tabitem %} {% /tabs %} ### Named input conventions By default, Nx Workspaces are generated with the following named inputs: ```jsonc // nx.json { "namedInputs": { "default": ["{projectRoot}/**/*", "sharedGlobals"], // Default Inputs "production": ["default", "!{projectRoot}/jest.config.ts"], // Production Inputs "sharedGlobals": [], // Shared Global Inputs }, } ``` The above inputs follow a convention that Nx recommends for named inputs. Nx generates these by default but you can also introduce your own conventions. #### Default inputs The `default` inputs include all files in a project as well as any shared global inputs which should be considered for all tasks. Defaulting to the set of everything that matters ensures that Nx will always re-run tasks when necessary by default. #### Shared global inputs The `sharedGlobals` inputs include things that Nx should always look at when determining computation hashes. For instance, this could be the OS where the command is being run or the version of Node. #### Production inputs The `production` inputs only include the set of files in a project which will affect behavior of the project in production. For instance, the `main.ts` of an application will be compiled as the main application logic and will affect the end user experience while files such as `.eslintrc.json` only affect the tools used by the developers and have no direct impact on the end user experience. In general, it is best to define the `production` inputs as the `default` inputs (everything) excluding the specific files which are known not to affect end user experience. This makes it so that by default, all files are considered to affect end user behavior unless excluded from the `production` fileset. ### Using named inputs on project dependencies It is common for most tasks to consider a set of inputs for the project it belongs to and a set of inputs for dependencies of the project. For instance, running the tests of a project should consider all files of the project being tested but only production files of its dependencies. The following `inputs` configuration uses named inputs in two different ways: ```jsonc {% meta="{6}" %} // project.json { "name": "myreactapp", "targets": { "test": { "inputs": ["default", "^production", "{projectRoot}/jest.config.js"], }, }, } ``` 1. `default` tells Nx to consider all files within the project root of `myreactapp` when running the `test` task. 2. `^production` is prefixed with `^` and applies the to projects which `myreactapp` depends on. 3. You can add other modifications to the inputs set directly without defining them as named inputs. All of the above inputs are taken into consideration when the following command is run: ```shell nx test myreactapp ``` {% cardgrid %} {% linkcard title="nx.json reference" description="namedInputs can be defined in nx.json" href="/docs/reference/nx-json#inputs-namedinputs" /%} {% linkcard title="Project configuration reference" description="inputs and namedInputs can be defined in project configuration" href="/docs/reference/project-configuration#inputs-and-named-inputs" /%} {% linkcard title="Configure Inputs for Task Caching" description="This recipes walks you through a few examples of how to configure inputs and namedInputs" href="/docs/kb/configure-inputs" /%} {% /cardgrid %} --- ## Java For setup and guides, see [Nx with Java](/docs/technologies/java/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Java" /%} --- ## Jest For setup and guides, see [Nx with Jest](/docs/technologies/test-tools/jest/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Jest" /%} --- ## Module Federation For setup and guides, see [Nx with Module Federation](/docs/technologies/module-federation/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Module Federation" /%} --- ## Node For setup and guides, see [Nx with Node.js](/docs/technologies/node/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Node" /%} --- ## Nx Cloud References {% index_page_cards path="reference/nx-cloud" /%} --- ## Cloud Commands The Nx Cloud command line interface provides various commands to connect and interact with Nx Cloud, manage distributed task execution, and handle authentication. Below is a reference for the commands you run directly and their options. ## Available commands ### `nx-cloud login` Provision a local personal access token to access Nx Cloud features. This will open your browser to the Nx Cloud application and after signing in will generate a personal access token and save it in a configuration file locally called `nxcloud.ini`. This command is the same as running `nx login`. **Usage:** ```shell npx nx-cloud login [nxCloudUrl] ``` #### Options | Option | Type | Description | Default | | ------------ | ------- | ------------------------------------------------------------------ | ---------------------- | | `nxCloudUrl` | string | The URL of the Nx Cloud instance to connect to | `https://cloud.nx.app` | | `--status` | boolean | Report the current login status and exit without opening a browser | | With `--status`, the command reports whether a personal access token is set for the resolved Nx Cloud URL. When `nx.json` also contains an `nxCloudId`, the token is checked against Nx Cloud, and an expired or revoked token is reported as invalid. #### Configuration file location {% tabs %} {% tabitem label="macOS & Linux" %} We look for this file at the following locations: - `$XDG_CONFIG_HOME/nxcloud/nxcloud.ini` - `$HOME/config/nxcloud/nxcloud.ini` - `$HOME/.nxcloud.ini` If we don't find an existing config file, we create one at `$HOME/config/nxcloud/nxcloud.ini` {% /tabitem %} {% tabitem label="Windows" %} We look for this file within the `%LOCALAPPDATA%/nxcloud` directory and if it does not exist, we will create a new `nxcloud.ini` file there. {% /tabitem %} {% /tabs %} The format of this file is as follows: ```ini [https://cloud\.nx\.app] personalAccessToken=SOME_ACCESS_TOKEN ``` If you have access to multiple instances of the Nx Cloud application (e.g. self-hosted Enterprise and our managed instance), each instance will be saved to this file under its URL. ### `nx-cloud logout` Revoke a personal access token from your local environment. This will remove the personal access token from the locally initialized configuration file and also invalidate the token from the Nx Cloud application. You will be prompted to remove a single token or all tokens from your local environment. This command is the same as running `nx logout`. **Usage:** ```shell npx nx-cloud logout ``` ### `nx-cloud configure` To provision more than one personal access token for multiple contexts (e.g. home and work machines) you can use the personal access tokens page under your Nx Cloud profile. To save a personal access token to your local `nxcloud.ini` file without needing to edit the file yourself call `nx-cloud configure`. **Usage:** ```shell npx nx-cloud configure ``` #### Options | Option | Type | Description | Default | | ------------------------- | ------ | -------------------------------------- | ---------------------- | | `--personal-access-token` | string | The personal access token to configure | | | `--nx-cloud-url` | string | The URL of the Nx Cloud instance | `https://cloud.nx.app` | #### Examples ```shell npx nx-cloud configure --personal-access-token=SOME_ACCESS_TOKEN ``` To configure multiple tokens for different instances of the Nx Cloud app: ```shell npx nx-cloud configure --personal-access-token=SOME_ACCESS_TOKEN --nx-cloud-url=https://nx-cloud.my-domain.app ``` ### `nx-cloud convert-to-nx-cloud-id` When logging into Nx Cloud with a [Personal Access Token](/docs/kb/personal-access-tokens), your `nx.json` file needs to include the `nxCloudId` property, which acts as a unique identifier for your workspace. If you have been using the previous `nxCloudAccessToken` to connect, simply run `npx nx-cloud convert-to-nx-cloud-id` to automatically update your configuration to use `nxCloudId`. **Usage:** ```shell npx nx-cloud convert-to-nx-cloud-id ``` ### `nx-cloud onboard` Connect a workspace to Nx Cloud. This command supports an interactive onboarding wizard for humans and JSON-based subcommands for automation and AI agents. Authentication is required before using this command. Either run `nx login` first or configure a personal access token with `nx-cloud configure`. **Usage:** ```shell npx nx-cloud onboard [subcommand] [options] ``` - Without flags, this starts the interactive onboarding wizard. - With `--no-interactive`, it runs an automation-friendly flow. - With `--no-interactive` and explicit workspace creation flags such as `--repo`, `--template`, or `--detect-repo`, it routes to `workspace create`. - With `--no-interactive` and no explicit creation flags, it routes to `connect-workspace` and defaults to JSON output. #### Options | Option | Type | Description | Default | | ------------------ | ------- | -------------------------------------------------------------------- | ------- | | `--json` | boolean | Output JSON for scripting | `false` | | `--token` | string | Use a specific Nx Cloud access token for this invocation | | | `--no-interactive` | boolean | Disable interactive prompts | `false` | | `--workspace-name` | string | Alias for `--name` | | | `--name` | string | Workspace or organization name, depending on the selected flow | | | `--org` | string | Organization ID to use | | | `--detect-repo` | boolean | Auto-detect the current Git repository | `false` | | `--write-config` | boolean | Write the returned `nxCloudId` to `nx.json` after workspace creation | `false` | #### Examples ```shell npx nx-cloud onboard npx nx-cloud onboard --no-interactive --json npx nx-cloud onboard --no-interactive --org=org_123 --detect-repo --write-config ``` #### Subcommands ##### `nx-cloud onboard status` Show onboarding status for the authenticated user, including organizations and GitHub connection state. **Usage:** ```shell npx nx-cloud onboard status ``` | Option | Type | Description | Default | | -------- | ------- | ------------------------- | ------- | | `--json` | boolean | Output JSON for scripting | `false` | ##### `nx-cloud onboard connect-workspace` One-shot command intended for agents and automated flows. It: 1. Detects the current repository from git remotes. 2. Selects or creates an organization. 3. Checks GitHub connection status. 4. Creates an Nx Cloud workspace from the repository. 5. Writes `nxCloudId` to `nx.json` when possible. If GitHub authentication is required, JSON mode returns an `actionRequired` payload instead of failing the command. **Usage:** ```shell npx nx-cloud onboard connect-workspace ``` | Option | Type | Description | Default | | -------- | ------- | ------------------------- | ------- | | `--json` | boolean | Output JSON for scripting | `false` | | `--org` | string | Organization ID to use | | | `--name` | string | Workspace name override | | ```shell npx nx-cloud onboard connect-workspace --json npx nx-cloud onboard connect-workspace --org=org_123 --name=my-workspace ``` ##### `nx-cloud onboard connect github` Start GitHub device-flow authentication. - In interactive mode, this prints instructions, attempts to open the browser, and polls until authorization completes. - In JSON mode, this returns the device-flow payload and leaves polling to the caller. **Usage:** ```shell npx nx-cloud onboard connect github ``` | Option | Type | Description | Default | | -------- | ------- | ------------------------- | ------- | | `--json` | boolean | Output JSON for scripting | `false` | ##### `nx-cloud onboard connect github poll` Poll for completion of a GitHub device-flow authentication started with `connect github`. **Usage:** ```shell npx nx-cloud onboard connect github poll --device-code ``` | Option | Type | Description | Default | | --------------- | ------- | --------------------------------- | ------- | | `--device-code` | string | Device code returned by `connect` | | | `--json` | boolean | Output JSON for scripting | `false` | ##### `nx-cloud onboard orgs list` List organizations available to the authenticated user. **Usage:** ```shell npx nx-cloud onboard orgs list ``` | Option | Type | Description | Default | | -------- | ------- | ------------------------- | ------- | | `--json` | boolean | Output JSON for scripting | `false` | ##### `nx-cloud onboard orgs create` Create a new organization. **Usage:** ```shell npx nx-cloud onboard orgs create ``` | Option | Type | Description | Default | | -------- | ------- | ------------------------- | ------- | | `--json` | boolean | Output JSON for scripting | `false` | ##### `nx-cloud onboard repos list` List repositories available to an organization. **Usage:** ```shell npx nx-cloud onboard repos list --org ``` | Option | Type | Description | Default | | ------------ | ------- | ---------------------------------- | ------- | | `--org` | string | Organization ID | | | `--search` | string | Filter repositories by search term | | | `--page` | number | Page number to fetch | | | `--per-page` | number | Number of repositories per page | | | `--json` | boolean | Output JSON for scripting | `false` | ##### `nx-cloud onboard templates list` List available Nx Cloud workspace templates. **Usage:** ```shell npx nx-cloud onboard templates list ``` | Option | Type | Description | Default | | -------- | ------- | ------------------------- | ------- | | `--json` | boolean | Output JSON for scripting | `false` | ##### `nx-cloud onboard vcs status` Show GitHub App connection status for an organization. **Usage:** ```shell npx nx-cloud onboard vcs status --org ``` | Option | Type | Description | Default | | -------- | ------- | ------------------------- | ------- | | `--org` | string | Organization ID | | | `--json` | boolean | Output JSON for scripting | `false` | ##### `nx-cloud onboard workspace create` Create a workspace either from an existing repository or from a template. Use exactly one of: - `--repo` with `--repo-full-name` - `--template` - `--detect-repo` When `--write-config` is provided, the command attempts to add the returned `nxCloudId` to `nx.json`. **Usage:** ```shell npx nx-cloud onboard workspace create --org [options] ``` | Option | Type | Description | Default | | ----------------------- | ------- | -------------------------------------------------------------------- | ------- | | `--org` | string | Organization ID | | | `--name` | string | Workspace name | | | `--workspace-name` | string | Alias for `--name` | | | `--repo` | string | Repository ID for an existing repository | | | `--repo-full-name` | string | Repository full name in `owner/repo` format | | | `--default-branch` | string | Default branch for an existing repository | `main` | | `--template` | string | Template ID for template-based workspace creation | | | `--github-organization` | string | GitHub organization for template-based workspace creation | | | `--repo-name` | string | Repository name override for template-based workspace creation | | | `--private` | boolean | Mark the created workspace repository as private | `true` | | `--installation-id` | string | GitHub App installation ID override for existing repositories | | | `--detect-repo` | boolean | Auto-detect the current repository and populate repository arguments | `false` | | `--write-config` | boolean | Write the returned `nxCloudId` to `nx.json` | `false` | | `--json` | boolean | Output JSON for scripting | `false` | ```shell npx nx-cloud onboard workspace create --org=org_123 --repo=repo_456 --repo-full-name=acme/web --name=web npx nx-cloud onboard workspace create --org=org_123 --detect-repo --write-config npx nx-cloud onboard workspace create --org=org_123 --template=tmpl_react --name=demo ``` #### Automation notes - `--json` is supported across all onboarding subcommands intended for scripting. - `connect-workspace` is the best entry point for AI agents because it can detect the repository, create an organization when needed, and return actionable JSON when GitHub authorization is required. - `connect github --json` starts device flow, and `connect github poll --device-code ... --json` can be used to poll for completion. ### `nx-cloud start-nx-agents` Invoke `npx nx-cloud start-nx-agents` at the beginning of your main job to provision the [Nx Agents](/docs/features/ci-features/distribute-task-execution) that run your distributed tasks. Nx Cloud reads your CI configuration from a [`.nx/ci-config.yaml`](/docs/reference/nx-cloud/ci-config) file. Every Nx Cloud command reads that file, so your configuration applies to the run whichever command starts it. `start-nx-agents` takes no configuration flags. **Usage:** ```shell npx nx-cloud start-nx-agents ``` ```yaml # .nx/ci-config.yaml dte: distribute-on: 5 linux-large lifecycle: stop-after: - build - test - e2e ``` {% aside type="caution" title="start-nx-agents and start-ci-run cannot be combined" %} `start-ci-run` does not read `.nx/ci-config.yaml`. If a config file is present, `start-ci-run` exits with an error and points you to `start-nx-agents`. Pick one path for your workspace: the config file with `start-nx-agents`, or CLI flags with `start-ci-run`. {% /aside %} To move an existing `start-ci-run` setup to the config file, see [Migrate from start-ci-run to start-nx-agents](/docs/kb/migrate-to-ci-config), which maps each flag to its `.nx/ci-config.yaml` counterpart. ### `nx-cloud start-ci-run` `start-ci-run` configures a CI run through CLI flags. If you prefer to keep your configuration in a versioned file, use [`start-nx-agents`](#nx-cloud-start-nx-agents) with a [`.nx/ci-config.yaml`](/docs/reference/nx-cloud/ci-config) file instead. At the beginning of your main job, invoke `npx nx-cloud start-ci-run`. This tells Nx Cloud that the following series of command correspond to the same CI run. This command is the same as running `nx start-ci-run`. {% aside type="caution" title="Run start-ci-run before any other Nx command" %} Run `npx nx-cloud start-ci-run` (or the orchestrator job that runs it) before any other `nx` command, including across separate or downstream pipelines that share the same CI Pipeline Execution (CIPE). The first `nx` command that contacts Nx Cloud creates the CIPE and locks in its settings for the whole run, including the access token's scope, the distribution configuration, your [assignment rules](/docs/kb/assignment-rules), and the stop-agents conditions. If another `nx` command reaches Nx Cloud first, the CIPE is created with default settings and your `start-ci-run` flags are ignored for that run. The result is assignment rules or stop conditions that appear to have no effect. {% /aside %} **Usage:** ```shell npx nx-cloud start-ci-run ``` {% aside type="caution" title="Do not run start-ci-run locally" %} `start-ci-run` generates a temporary marker file that can cause a local Nx repo to behave as if it is a part of a CI run. This can cause strange behavior like Nx commands timing out or throwing unexpected errors. To discourage this from happening, this command will run a check to see if it is running in a CI environment. You can bypass this check with `npx nx-cloud start-ci-run --force`. If you accidentally run this command locally, remove all generated marker files with `npx nx-cloud cleanup`. {% /aside %} #### Options | Option | Type | Description | Default | | ------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | `--distribute-on` | string | Configure the number of agents, [launch templates](/docs/kb/launch-templates) and [assignment rules](/docs/kb/assignment-rules) for distributed execution | | | `--assignment-rules` | string | Path to the [assignment rules configuration](/docs/kb/assignment-rules#using-assignment-rules-when-you-bring-your-own-compute) for manual distribution. | | | `--no-distribution` | boolean | Disable distribution for this run | | | `--require-explicit-completion` | boolean | Disable automatic completion monitoring and require explicit `nx complete-ci-run` | `false` | | `--stop-agents-after` | string | Comma-separated list of targets after which agents should terminate | | | `--stop-agents-on-failure` | boolean | Terminate all agents when a command fails. This flag does not interrupt the command itself. To cancel the command on failure use [nxBail](/docs/reference/nx-commands) | `true` | | `--use-dte-by-default` | boolean | Configure Nx to distribute all commands by default | `true` | | `--with-env-vars` | string | Comma-separated list of environment variables to pass to Nx Agents | | | `--fix-tasks` | string | Comma-separated glob patterns for tasks to enable [AI self-healing](/docs/features/ci-features/self-healing-ci). Supports negation with `!` prefix | | | `--auto-apply-fixes` | string | Comma-separated glob patterns for [self-healing](/docs/features/ci-features/self-healing-ci) tasks to auto-apply to PRs without manual review | | | `--force` | boolean | Bypass CI environment check. Run [`nx-cloud cleanup`](#nx-cloud-cleanup) if accidentally ran locally. | | #### Detailed option usage ##### `--assignment-rules` When `--distribute-on` points at a configuration file that already embeds assignment rules, the `--assignment-rules` file replaces them. ##### `--no-distribution` Turn distribution off for the whole CI pipeline execution. This also stops `start-ci-run` from writing the marker file that makes subsequent `nx` commands run as distributed executions, so use it for pipelines that only want Nx Cloud caching and [self-healing CI](/docs/features/ci-features/self-healing-ci). Pass either `--distribute-on` or `--no-distribution`. With neither, the command warns that the distribution configuration is ambiguous. ##### `--distribute-on` By default, `start-ci-run` is intended for use with [Nx Agents](/docs/features/ci-features/distribute-task-execution) and expects `--distribute-on` to be configured. It will output a warning if this flag is not set. If you are running a distributed execution with a legacy setup without Nx Agents, you can pass `--distribute-on=manual` to disable this warning. This command tells Nx Cloud how many agents to use (and what launch templates to use) to distribute tasks. E.g., `npx nx-cloud start-ci-run --distribute-on="8 linux-medium-js"` will distribute CI using 8 agents that are initialized using the `linux-medium-js` launch template. You can also [define the configuration in a file](/docs/features/ci-features/dynamic-agents) and reference it as follows: `npx nx-cloud start-ci-run --distribute-on=".nx/workflows/dynamic-changesets.yaml"`. ```yaml // .nx/workflows/dynamic-changesets.yaml distribute-on: small-changeset: 3 linux-medium-js medium-changeset: 6 linux-medium-js large-changeset: 10 linux-medium-js ``` ##### --stop-agents-after You can tell Nx Cloud to terminate agents after it sees a certain target or group of targets: `npx nx-cloud start-ci-run --stop-agents-after=build,test,e2e`. {% aside type="tip" title="Be specific!" %} For the best results, always tell Nx Cloud about all targets that should be expected during your pipeline. This will prevent your CI Pipeline Execution from ending prematurely, cutting off any unfinished work. {% /aside %} Whether you are running commands serially or in parallel (through use of the `&` operand or discrete jobs), you should include one or more targets from each command. {% tabs %} {% tabitem label="Serial command execution" %} #### Incorrect example: ```yaml - run: npx nx-cloud start-ci-run --stop-agents-after=build - run: nx affected -t build - run: nx affected -t lint - run: nx affected -t test ``` Nx Cloud will only look for the presence of a `build` target before determining your pipeline can be shut down. Since both `lint` and `test` will only run after `build` is complete, your pipeline would end before all intended work is executed. #### Corrected example: ```yaml - run: npx nx-cloud start-ci-run --stop-agents-after=lint,test,build - run: nx affected -t lint - run: nx affected -t test - run: nx affected -t build ``` {% /tabitem %} {% tabitem label="Parallel command execution" %} #### Incorrect example: ```yaml - run: npx nx-cloud start-ci-run --stop-agents-after=test - run: nx affected -t build & nx affected -t lint & nx affected -t test ``` At first glance, this example may appear correct. However, since all the commands are executed in parallel, it could be possible that your `test` command results in full cache hits and finishes faster than the `build` and `lint` commands. In this case, Nx Cloud will stop your pipeline, leaving work incomplete for both the `build` and `lint` commands. #### Corrected example: ```yaml - run: npx nx-cloud start-ci-run --stop-agents-after=lint,test,build - run: nx affected -t build & nx affected -t lint & nx affected -t test ``` {% /tabitem %} {% /tabs %} #### Advanced shutdown targeting In advanced pipeline setups, it is possible that you want to run multiple commands with the same target, but with distinct configurations. An example of this may be doing static translations for different locales during your builds. ```yaml - run: npx nx-cloud start-ci-run --stop-agents-after=build - run: nx affected -t build --configuration=locale-en - run: nx affected -t build --configuration=locale-es ``` In this case, the `build` target will be executed twice, once with the `locale-en` configuration and once with the `locale-es` configuration. However, Nx Cloud is only looking for the `build` target to assess whether the CI Pipeline Execution can be marked complete. To address this, you can pass an optional `configuration` to make your `target`s more specific. ```yaml - run: npx nx-cloud start-ci-run --stop-agents-after=build:locale-en,build:locale-es - run: nx affected -t build --configuration=locale-en - run: nx affected -t build --configuration=locale-es ``` ##### --with-env-vars (Nx Agents only) By default, invoking `npx nx-cloud start-ci-run` will take all environment variables prefixed with `NX_` and send them over to Nx Agents. This means that your access token, verbose logging configuration and other Nx-related environment variables will be the same on your main CI jobs and the Nx Agent machines. If you want to pass other environment variables from the main job to Nx Agents, you can do it as follows: `--with-env-vars="VAR1,VAR2"`. This will set `VAR1` and `VAR2` on Nx Agents to the same values set on the main job before any steps run. > Note: none of the values passed to Nx Agents are stored by Nx Cloud. ##### --fix-tasks Enable [AI-powered self-healing](/docs/features/ci-features/self-healing-ci) for specific tasks. When a task fails, Nx Cloud will attempt to analyze the error and generate a fix. By default, all tasks are enabled for self-healing. You can use glob patterns to limit which tasks are eligible. Examples: ```shell # Enable for lint and format tasks only npx nx-cloud start-ci-run --fix-tasks="*lint*,*format*" --no-distribution # Enable for all tasks except deploy and test npx nx-cloud start-ci-run --fix-tasks="!*deploy*,!*test*" --no-distribution ``` The patterns support: - `*` wildcard to match any characters - `!` prefix to exclude tasks (negation) - Multiple comma-separated patterns ##### --auto-apply-fixes Automatically apply [AI-generated fixes](/docs/features/ci-features/self-healing-ci) to pull requests for specific tasks without requiring manual review. Fixes are only applied if the verification phase passes. Examples: ```shell # Auto-apply fixes for linting tasks only npx nx-cloud start-ci-run --auto-apply-fixes="*lint*" --no-distribution # Auto-apply fixes for both lint and format tasks npx nx-cloud start-ci-run --auto-apply-fixes="*lint*,*format*" --no-distribution ``` #### Enabling/Disabling distribution Invoking `npx nx-cloud start-ci-run` will tell Nx to distribute by default. You can enable/disable distribution for individual commands as follows: {% tabs %} {% tabitem label="Nx >= 18" %} Explicitly enable distribution: ```shell nx affected -t build --agents ``` Explicitly disable distribution: ```shell nx affected -t build --no-agents ``` {% /tabitem %} {% tabitem label="Nx >= 14.7" %} Explicitly enable distribution: ```shell nx affected -t build --dte ``` Explicitly disable distribution: ```shell nx affected -t build --no-dte ``` {% /tabitem %} {% /tabs %} ### `nx-cloud start-agent` Starts an agent process when you [bring your own compute](/docs/kb/bring-your-own-compute). The agent waits for Nx Cloud to assign tasks that the main CI job distributed, either through `start-nx-agents` with `dte.distribute-on: manual` in `.nx/ci-config.yaml`, or through `start-ci-run --distribute-on=manual`. To have Nx Cloud provision and manage the agents for you, see [Nx Agents](/docs/features/ci-features/distribute-task-execution). This command is the same as running `nx start-agent`. **Usage:** ```shell npx nx-cloud start-agent ``` #### Options | Option | Type | Description | Default | | ----------- | ------ | ----------------------------------------------------------------------------- | ------- | | `--targets` | string | Comma-separated target names. The agent only picks up tasks for these targets | | Without `--targets`, the agent accepts any task assigned to it. Use it to dedicate a pool of agents to a subset of your pipeline, for example `--targets=e2e`. #### Environment variables | Variable | Description | | -------------------------------------------------- | -------------------------------------------------------------------------------------------------- | | `NX_AGENT_NAME` | Name the agent for logs and the Nx Cloud UI. Defaults to the CircleCI job name, then a random name | | `NX_AGENT_INSTANCE_NAME` | Distinguish individual instances that share an agent name | | `NX_AGENT_LAUNCH_TEMPLATE` | Report the [launch template](/docs/kb/launch-templates) the agent runs, used by assignment rules | | `NX_VERBOSE_LOGGING` or `NX_CLOUD_VERBOSE_LOGGING` | Set either to `true` to turn on verbose logging. `start-agent` has no `--verbose` flag | ```shell NX_AGENT_NAME=agent-1 npx nx-cloud start-agent ``` For complete examples across different CI providers, see the [bring your own compute guide](/docs/kb/bring-your-own-compute). ### `nx-cloud stop-all-agents` Same as `nx-cloud complete-ci-run`, `nx stop-all-agents` and `nx complete-ci-run`. This command tells Nx Cloud to terminate all agents associated with this CI pipeline execution. Invoking this command is not needed anymore. New versions of Nx Cloud can track when the main job terminates and terminate associated agents automatically. **Usage:** ```shell npx nx-cloud stop-all-agents ``` ### `nx-cloud complete-ci-run` Explicitly complete a CI run when automatic completion monitoring is turned off, either with `lifecycle.heartbeat: false` in `.nx/ci-config.yaml`, or with `--require-explicit-completion` on `start-ci-run`. **Usage:** ```shell npx nx-cloud complete-ci-run ``` ### `nx-cloud cleanup` Remove temporary marker files created by `start-ci-run` or `start-nx-agents` if either is accidentally run locally. **Usage:** ```shell npx nx-cloud cleanup ``` ### `nx-cloud fix-ci` Run [self-healing CI](/docs/features/ci-features/self-healing-ci) for the current CI pipeline execution. The command asks Nx Cloud which failures need fixing, runs the AI agent against them, and reports the resulting fix back to the pipeline. When no failing task qualifies, it prints a note and exits without doing any work. This command is the same as running `nx fix-ci`. It is not supported on Windows. **Usage:** ```shell npx nx-cloud fix-ci ``` Configure which tasks are eligible and which fixes apply automatically through `start-ci-run --fix-tasks` and `--auto-apply-fixes`, or through the Nx Cloud UI. See [self-healing CI](/docs/features/ci-features/self-healing-ci) for the full configuration. ### `nx-cloud apply-locally` Apply a self-healing fix to your working copy instead of merging it from the pull request. Copy the fix identifier from the Nx Cloud UI or the pull request comment, then run the command from the root of your workspace. Nx Cloud downloads the diff, checks out the branch the fix was generated for, and applies it there. If that branch is checked out in another worktree, the fix is applied in that worktree. The command refuses to run when you have uncommitted changes, and it stops when your branch has diverged from the commit the fix was based on. **Usage:** ```shell npx nx-cloud apply-locally ``` #### Options | Option | Type | Description | Default | | ------------------ | ------- | -------------------------------------------------------------------- | ------- | | `` | string | The fix identifier, in `-` form, for example `abc123-def456` | | | `--no-interactive` | boolean | Skip the prompts and take the default answer for each one | | For the walkthrough with screenshots, see [applying locally for fine-tuning](/docs/features/ci-features/self-healing-ci#applying-locally-for-fine-tuning). ### `nx-cloud conformance` Run the [conformance](/docs/enterprise/conformance) rules configured for your workspace, including the rules published to your Nx Cloud organization, and run the fix generator for any rule that has one. Rules configured in Nx Cloud take precedence over the same rule in `nx.json`, and the command warns when it drops a local entry for that reason. Requires `@nx/conformance` version 1.3.3 or later in the workspace. **Usage:** ```shell npx nx-cloud conformance ``` #### Options | Option | Type | Description | Default | | --------------- | ------- | ---------------------------------------------------------------------------------- | ------- | | `--verbose` | boolean | Print additional details while running | `false` | | `--printConfig` | boolean | Print the resolved conformance configuration and exit. `--print-config` also works | `false` | ### `nx-cloud conformance:check` Same as [`nx-cloud conformance`](#nx-cloud-conformance), except that fix generators do not run and nothing is written to disk. Run this one in CI. **Usage:** ```shell npx nx-cloud conformance:check ``` #### Options | Option | Type | Description | Default | | --------------- | ------- | ---------------------------------------------------------------------------------- | ------- | | `--verbose` | boolean | Print additional details while running | `false` | | `--printConfig` | boolean | Print the resolved conformance configuration and exit. `--print-config` also works | `false` | ### `nx-cloud publish-conformance-rule` Package one conformance rule directory into an archive and publish it to your Nx Cloud organization, where it becomes available to every connected workspace. The directory must contain the rule implementation and a `schema.json`. Your workspace needs an `nxCloudId` in `nx.json` plus a personal access token. Run `nx-cloud convert-to-nx-cloud-id` if you are still on an access token, then authenticate with `nx login` locally or `nx-cloud configure --personal-access-token=` in CI. **Usage:** ```shell npx nx-cloud publish-conformance-rule ``` #### Options | Option | Type | Description | Default | | ------------------ | ------- | ---------------------------------------------------------- | ------- | | `` | string | Path to the rule directory, relative to the workspace root | | | `--verbose` | boolean | Print additional details while packaging and uploading | `false` | See [publish conformance rules to Nx Cloud](/docs/enterprise/publish-conformance-rules-to-nx-cloud) for the rule layout and the full workflow. ### `nx-cloud publish-conformance-rules` Publish every rule directory inside a parent directory, one after another. Each entry in the parent directory has to be a directory. The command errors and points you at [`publish-conformance-rule`](#nx-cloud-publish-conformance-rule) when it finds a file. **Usage:** ```shell npx nx-cloud publish-conformance-rules ``` #### Options | Option | Type | Description | Default | | -------------------------- | ------- | -------------------------------------------------------------------------------------------- | ------- | | `` | string | Path to the directory holding the rule directories, relative to the workspace root | | | `--verbose` | boolean | Print additional details while packaging and uploading, and print the stack trace on failure | `false` | ### `nx-cloud validate` Check a [launch template](/docs/kb/launch-templates) file against the Nx Cloud schema and report every error it finds. The command exits with a non-zero status when the file is invalid, so it works as a CI guard against merging a broken template. **Usage:** ```shell npx nx-cloud validate --workflow-file= ``` #### Options | Option | Type | Description | Default | | ----------------- | ------- | ---------------------------------------------------------------------------------------------- | ------- | | `--workflow-file` | string | Path to the file to validate. Required | | | `--step-file` | boolean | Validate a single [custom step](/docs/kb/custom-steps) file instead of a whole launch template | `false` | #### Examples ```shell npx nx-cloud validate --workflow-file=./.nx/workflows/agents.yaml npx nx-cloud validate --workflow-file=./.nx/workflows/custom-steps.yaml --step-file ``` See [validating launch templates](/docs/kb/launch-templates#validating-launch-templates). #### Subcommands ##### `nx-cloud validate sandbox-violations` Replay a downloaded [sandbox report](/docs/features/ci-features/sandboxing) against your current `nx.json`. For every task in the report, the command prints which unexpected reads and writes your latest input and output declarations now cover, and which ones remain unexplained. It accepts a single per-task report, an array of reports, or the `index.json` written next to them, in which case every referenced report is loaded. Exits `0` when every task is reconciled and `1` when at least one task still has unexpected reads or writes. Requires Nx 22.6.0 or later. **Usage:** ```shell npx nx-cloud validate sandbox-violations ``` | Option | Type | Description | Default | | --------------- | ------- | --------------------------------------------------------------------- | ------- | | `` | string | Path to a per-task report, an array of reports, or an `index.json` | | | `--json` | boolean | Emit a machine-readable summary on stdout instead of the printed view | `false` | ```shell npx nx-cloud get sandbox-reports --branch --since 1d npx nx-cloud validate sandbox-violations .nx/workspace-data/sandbox-reports//index.json --json ``` For the fix workflow, see [fix sandbox violations](/docs/kb/fix-sandbox-violations). ### `nx-cloud get sandbox-reports` Download sandbox reports for tasks that ran on a branch. Reports capture unexpected file reads and writes detected by the Nx Cloud task sandbox and are written to disk as JSON files for inspection or post-processing. **Usage:** ```shell npx nx-cloud get sandbox-reports [options] ``` #### Options | Option | Type | Description | Default | | ----------------------- | ------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | | `--branch` | string | Branch to query | current git branch | | `--fallback-branch` | string | For each task without a report on `--branch`, fall back to this branch's latest report | | | `--since` | string | Time window. Units: `h`, `d`, `w`, `y` (e.g. `1h`, `7d`, `4w`, `1y`) | `7d` | | `--output`, `-o` | string | Output directory | `./.nx/workspace-data/sandbox-reports//` | | `--include-clean` | boolean | Include reports with zero unexpected reads/writes | `false` | | `--latest-reports-only` | boolean | Fetch only the latest report per task. Pass `--no-latest-reports-only` to fetch every report in the window | `true` | | `--concurrency` | number | Parallel downloads | `20` | | `--force` | boolean | Overwrite existing files instead of skipping them | `false` | | `--json` | boolean | Print a machine-readable summary on stdout | `false` | | `--help`, `-h` | boolean | Show command help and exit | | #### Examples Download reports for a feature branch over the last hour: ```shell npx nx-cloud get sandbox-reports --branch 11249 --since 1h ``` Fall back to `main` for any task that has no report on the feature branch: ```shell npx nx-cloud get sandbox-reports --branch my-feature --fallback-branch main ``` ### `nx-cloud record` Run any command, stream its output as usual, and upload that output and its exit code to Nx Cloud. The recorded command shows up in the Runs view as a `record-command` target, which puts the output of non-Nx pipeline steps next to your task results. `nx record` exits with the same code the wrapped command exited with, so it is safe to drop in front of an existing step. **Usage:** ```shell npx nx-cloud record -- [args...] ``` #### Examples ```shell npx nx-cloud record -- echo "hello world" npx nx-cloud record -- nx format:check ``` Everything after `--` belongs to the wrapped command. See [recording non-Nx commands](/docs/guides/nx-cloud/record-commands). ### `nx-cloud decrypt-artifact` Decrypt an artifact that Nx Cloud stored with your workspace [encryption key](/docs/guides/nx-cloud/encryption). Point it at a single encrypted file or at a directory, in which case every file underneath is decrypted into a mirrored output tree. The key is read from `NX_CLOUD_ENCRYPTION_KEY`. Without that variable, the nearest `nx.json` is searched for `nxCloudEncryptionKey`. **Usage:** ```shell npx nx-cloud decrypt-artifact [--output-path ] ``` #### Options | Option | Type | Description | Default | | --------------------- | ------ | ----------------------------------------------------------------------------- | --------- | | `` | string | Path to the encrypted file or directory | | | `--output-path`, `-o` | string | Output directory. A relative path resolves from the current working directory | see below | For a single file, the output lands in the current directory, named after the input with the `.enc` or `.encrypted` wrapper removed and `-decrypted` appended. A `.tar.gz` or `.zip` extension is restored from the decrypted content when the name carries no extension of its own. For a directory, the output goes to `-decrypted` in the current directory, and each file inside keeps its own name minus the wrapper extension. {% aside type="caution" title="Relative paths and pnpm or yarn" %} Under `pnpm nx-cloud` and `yarn nx-cloud`, relative paths resolve from the workspace root rather than your current directory. Use `npx nx-cloud`, `./node_modules/.bin/nx-cloud`, or an absolute path. {% /aside %} ## Getting help You can get help for any command by adding the `--help` flag: ```shell npx nx-cloud --help ``` --- ## CI Configuration File Nx Cloud reads CI configuration from a `.nx/ci-config.yaml` file at the root of your workspace. The file controls how a CI Pipeline Execution runs: task distribution, agent lifecycle, self-healing, and the environment forwarded to agents. Every Nx Cloud command reads the file, so the configuration applies to the run whichever command starts it. To provision Nx Agents, run [`nx-cloud start-nx-agents`](/docs/reference/nx-cloud-cli#nx-cloud-start-nx-agents). ```yaml # .nx/ci-config.yaml dte: distribute-on: 5 linux-large lifecycle: stop-after: - build - test - e2e ``` Start the run by invoking the command once, early in your main job: ```shell npx nx-cloud start-nx-agents ``` The command takes no configuration flags. Everything lives in the file, so the same configuration is versioned with your workspace and can be overridden per environment. {% aside type="caution" title="start-ci-run and .nx/ci-config.yaml cannot be combined" %} `nx-cloud start-ci-run` does not read `.nx/ci-config.yaml`. If a config file is present, `start-ci-run` exits with an error and directs you to `start-nx-agents`. Choose one path: either the config file with `start-nx-agents`, or CLI flags with `start-ci-run`. For a mapping between the two, see the [Nx Cloud CLI reference](/docs/reference/nx-cloud-cli). {% /aside %} ## File location Nx Cloud reads the file from `.nx/ci-config.yaml`. A `.nx/ci-config.yml` extension also works. ## Top-level structure The file has five top-level sections. All are optional, and each falls back to its defaults when omitted. | Section | Purpose | | ----------- | ------------------------------------------------------------------- | | `lifecycle` | Stop conditions and stall detection for the run. | | `ai` | Self-healing CI configuration. | | `dte` | Distributed task execution: how tasks are distributed to Nx Agents. | | `nx-agents` | Configuration for the Nx Agents themselves. | | `overrides` | Per-environment overrides keyed by `NX_CI_EXECUTION_ENV`. | For complete pipelines that pair this file with a provider workflow, see [Setting up CI for Nx Cloud](/docs/kb/setup-ci). ## `lifecycle` Stop conditions and stall detection for the run group. ```yaml # .nx/ci-config.yaml lifecycle: fail-after: 1 heartbeat: true stop-after: - build - test - e2e ``` ### `lifecycle.fail-after` Number of task failures allowed before the run group is marked failed. `0` means never fail, so the run always finishes. `1` stops on the first failure. Type: `integer`, minimum `0`. Default: `1`. ### `lifecycle.heartbeat` When `true`, Nx Cloud monitors the run for stalled progress and marks it failed if the main job stops reporting in for too long. Type: `boolean`. Default: `true`. ### `lifecycle.stop-after` Targets that mark this CI run complete once each one has finished. List every target you expect in the pipeline so the run does not end before all work is done. Type: array of strings. ```yaml # .nx/ci-config.yaml lifecycle: stop-after: - lint - test - build ``` ## `ai` Self-healing CI configuration. See [self-healing CI](/docs/features/ci-features/self-healing-ci) for how the fixes are generated and applied. ```yaml # .nx/ci-config.yaml ai: fix-tasks-patterns: - '*lint*' - '*test*' auto-apply-patterns: - '*lint*' ``` ### `ai.fix-tasks-patterns` Glob patterns of task IDs that self-healing CI is allowed to attempt fixes for. Type: array of strings. ### `ai.auto-apply-patterns` Glob patterns of task IDs whose fixes are applied automatically without explicit approval. Type: array of strings. ## `dte` Distributed task execution: how tasks are distributed to Nx Agents. ```yaml # .nx/ci-config.yaml dte: enabled-by-default: true distribute-on: 5 linux-large assignment-rules: .nx/assignment-rules.yaml ``` ### `dte.enabled-by-default` When `true`, tasks in this run group are distributed across agents automatically. Type: `boolean`. Default: `true`. ### `dte.distribute-on` How many agents to use and which [launch templates](/docs/kb/launch-templates) to run them on. Accepts a single launch-template string, or a map of changeset sizes to launch-template strings for [dynamic agent allocation](/docs/features/ci-features/dynamic-agents). A launch-template string is one or more ` ` segments separated by commas. ```yaml # .nx/ci-config.yaml dte: distribute-on: 5 linux-large ``` ```yaml # .nx/ci-config.yaml dte: distribute-on: small-changeset: 3 linux-medium medium-changeset: 6 linux-large large-changeset: 10 linux-large ``` The changeset map can also live in its own file. Set `distribute-on` to that file's path and Nx Cloud reads the map from it. ```yaml # .nx/ci-config.yaml dte: distribute-on: .nx/workflows/distribution-config.yaml ``` To bring your own compute instead of using Nx Agents, set `distribute-on: manual`. See [bring your own compute](/docs/kb/bring-your-own-compute). ### `dte.assignment-rules` Rules that pin matching tasks to specific agent types. Provide the rules inline, or a path to a YAML file containing them. See [assignment rules](/docs/kb/assignment-rules) for the rule format. ```yaml # .nx/ci-config.yaml dte: assignment-rules: .nx/assignment-rules.yaml ``` ```yaml # .nx/ci-config.yaml dte: assignment-rules: - projects: - '*' targets: - build run-on: - agent: linux-large-A parallelism: 2 ``` Each inline rule matches tasks by `projects`, `targets`, and `configurations` globs, and routes them to the agents listed under `run-on`. `run-on` is required on every rule. Each `run-on` entry needs an `agent`, with optional `parallelism`. ## `nx-agents` Configuration for the Nx Agents. ```yaml # .nx/ci-config.yaml nx-agents: with-env-vars: - CHROMATIC_BRANCH - CHROMATIC_SHA ``` ### `nx-agents.with-env-vars` Environment variables forwarded from the main job to every Nx Agent. Variables prefixed with `NX_` are forwarded automatically, so list only the additional ones you need. Type: array of strings. ## `overrides` Per-environment overrides keyed by [`NX_CI_EXECUTION_ENV`](/docs/reference/environment-variables) value. Each override is a partial config that takes precedence over the base sections when its key matches the active environment. This has no `start-ci-run` flag equivalent. It is specific to the config file. ```yaml # .nx/ci-config.yaml dte: distribute-on: 5 linux-large assignment-rules: - targets: - e2e-ci run-on: - agent: linux-large parallelism: 1 overrides: main: dte: assignment-rules: - targets: - e2e-ci run-on: - agent: linux-extra-large parallelism: 1 ``` In this example every environment distributes across `linux-large` agents and routes `e2e-ci` tasks to a `linux-large` agent. When `NX_CI_EXECUTION_ENV` is `main`, the override replaces `assignment-rules`, routing `e2e-ci` to a `linux-extra-large` agent instead. Overrides apply per key. An override replaces the base value for the keys it sets, so an array like `assignment-rules` is replaced as a whole rather than merged with the base rules. Keys the override does not set keep their base values, so `distribute-on` remains `5 linux-large` on `main`. An override only replaces the sections you set. Sections you leave out fall back to the base config, and the base config falls back to defaults. --- ## Nx Cloud Credit Pricing Reference Credits are the Nx Cloud currency that enables usage-based pricing. This system allows for transparent cost tracking and flexible billing based on actual consumption of resources. ## What are credits? Credits represent the computational resources consumed during CI/CD operations. They account for: - CPU usage - Memory consumption - Setup and provisioning costs - Storage operations - Task execution time - AI computation ## Credit pricing Each plan includes a monthly credit allowance. Beyond it, the Team plan bills $5.50 per 10,000 credits, and Enterprise plans get volume discounts. See [plans and pricing](https://nx.dev/pricing) for allowances, and [Orchestration & CI with Nx Cloud](/docs/features/ci-features) for what happens when they run out. ### Non-compute pricing - **CI Pipeline Execution: 500 credits per execution**. A CI Pipeline Execution is a CI run or a Workflow run (depending on your CI provider). For instance, running a PR or running CI on the main branch are CI Pipeline Executions. - **Resource Usage Report: 10 credits per report**. With Nx Agents, each agent generates its own report. For non-distributed runs, all reports generated on the same machine count as a single 10 credit charge. See [resource usage](/docs/features/ci-features/resource-usage) for how to turn collection on or off. - **AI-Powered Self-Healing CI: 6555 credits per 1M input tokens, 32700 credits per 1M output tokens**. Credits are consumed by the fix-ci command. ### Agents resource classes #### Docker / Linux AMD64 | Resource Class | Specifications | Credits/min | | -------------- | -------------- | ----------- | | Small | 1 vCPU | 5 | | Medium | 2 vCPU | 10 | | Large | 4 vCPU | 20 | | Extra large | 8 vCPU | 40 | #### Docker / Linux ARM64 | Resource Class | Specifications | Credits/min | | -------------- | -------------- | ----------- | | Medium | 2 vCPU | 13 | | Large | 4 vCPU | 26 | | Extra large | 8 vCPU | 52 | _Note: Linux resource classes have memory available in an approximate 1:4 ratio per vCPU core._ #### Docker / Windows | Resource Class | Specifications | Credits/min | | -------------- | --------------- | ----------- | | Medium | 3 vCPU, 6GB RAM | 40 | --- _Note: Prices do not include applicable taxes. Credit pricing and allocations subject to change._ --- ## Enterprise Release Notes ### 2026.01 ##### Self-healing CI - Feat: Azure DevOps support for Self-Healing CI, bringing it to feature parity with GitHub and GitLab for automated fix proposals - Automated fix application: Nx Cloud can now automatically create commits on Azure DevOps pull requests to apply AI-generated fixes - Interactive PR comments: Self-healing proposals appear as comments on Azure DevOps PRs with actions to apply, reject, or revert fixes - Feat: Self-Healing CI fixes are now generated for draft PRs/MRs by default (configurable via workspace settings) - Feat: Self-Healing CI now supports Bitbucket workspaces - Enable Self-Healing CI for PRs - Configure PR comment notifications - Set up automatic flaky task reruns - Define eligible task patterns and auto-apply rules - Feat: Enable Self-Healing CI directly from the Nx Cloud UI with a single-click setup wizard - Automatically detects your CI provider (GitHub Actions, GitLab CI, or Azure DevOps) - Adds the `nx fix-ci` step to your CI configuration - Creates a pull request with the changes for you to review and merge - Feat: Add support for triggering a CI rerun when Self-Healing CI determines that failures are caused by environment issues - Feat: Allow users to configure branch patterns to ignore for AI fix generation - Feat: Configure Self-Healing CI functionality, including task filtering and auto-apply fixes logic, from the Self-Healing settings tab under Workspace settings - Feat: Display AI provider status banner on Self-Healing page when Claude API is experiencing issues - Feat: Self-healing now uses affected project information to prevent misclassifying persistent failures as flaky tasks - Feat: Updated Self-Healing CI UI within the CIPE screen for a richer, more detailed experience - Improved messaging and support to show why a fix was not started in the cloud UI - Feat: Add `--no-interactive` flag to `nx-cloud apply-locally` command to improve compatibility with AI agents - Fix: Self-Healing CI now uses the workspace's package manager (pnpm, yarn, bun) instead of hardcoded npx for task verification - Fix: Update classification prompts to determine environmental vs flaky tasks better - Fix: Update git operations during Self-Healing CI to be more resilient whenever there are additional commits, or branch becomes outdated - Fix: Self-healing now correctly resolves the merge commit SHA when run as part of a GitLab Merged Results Pipeline - Fix: Limit the amount of consecutive flaky reruns that Self-Healing CI will trigger - Fix: In cases where auto-apply does not proceed (e.g., Nx Cloud was the previous CIPE author), a consistent message is now shown across the PR/MR review comment and the Nx Cloud UI - Fix: Improved clarity of messages when Self-Healing CI times out or is cancelled part way through - Fix: For suggested fix diffs consisting only of a trailing newline removal, the full diff now correctly renders in the UI - Fix: Fixed an issue which prevented auto application of fix suggestions for configured tasks - Fix: Ensure Self-Healing CI accurately prints information about the tasks that were run for verification - Fix: Fixes an issue where Self-Healing CI could fail to fetch git SHAs when GitHub App tokens expire during long-running CI jobs - Fix: No longer adds the warning about an in-flight CIPE to review comments for fixes which have already had action taken on them - Fix: Avoid overly verbose explanations inline in review comments when issues are classified as `environment_state` ##### CI pipeline executions - Feat: AI fix status badges and filtering for CI Pipeline Executions - Badges show: Generating Fix, Fix Ready, and Fix Applied - Filter the CIPE list by fix status (Fix Ready, Fix Applied, or Fix Rejected) - Click on any fix badge to navigate directly to the self-healing page - Feat: New Configuration tab on the CI Pipeline Execution page to help verify settings and debug issues - Shows: explicit completion, VCS context, cache status, distribution setup, auto-stop conditions, and self-healing CI settings ![CIPE configuration view](../../../../assets/nx-cloud/cipe-configuration-view.png) - Feat: Display CIPE credit usage summary under CIPE Analysis area including compute credits and AI credits - Feat: Branch filter allows fuzzy matching by opt-in via a toggle - Feat: Run tasks list filter supports sort by retries - Fix: Add loading feedback when navigating between different CIPEs - Fix: Author filter now allows searching by text input with enter - Fix: Adjust cache status multi-select filter in Tasks list to use logical OR instead of AND - Fix: Defer loading of the run list with a loading animation - Fix: When using `nx affected:test` rather than `nx affected -t test`, the `--stop-agents-after` condition is now correctly matched ##### Analytics & reporting - Feat: Workspace analytics now features a collapsible sidebar navigation ![Analytics sidebar layout](../../../../assets/nx-cloud/analytics-sidebar-layout.avif) - Range selectors are now in a dropdown ![New range selector dropdown](../../../../assets/nx-cloud/analytics-range-selector.avif) - Custom range utilizes a date-range calendar ![New date range picker](../../../../assets/nx-cloud/analytics-date-range-picker.avif) - Feat: Remove non-cacheable tasks from flaky task analytics - Feat: Added ability to filter by status in "recent executions" within flaky task analytics drawer - Fix: Fixed issue that was errantly filtering out too much data from the flaky task view - Fix: Improved performance of flaky task analytics queries - Fix: Average task duration in flaky tasks analytics drawer is now fixed to 2 decimal places - Fix: Do not show AI Credits if it's 0 - Fix: Fix enterprise usage periods for mid-month license starts — usage data now correctly starts from the actual license start date ##### Onboarding & workspace setup - Feat: New `/welcome` page to help orient new users to Nx Cloud ![Welcome splash page](../../../../assets/nx-cloud/welcome-splash.avif) - Feat: New landing page for connecting workspaces, allowing users to navigate to the right flow from their repo URL - Feat: When creating a workspace, you will now see a description of a template before you clone it, including technologies used and Nx features demoed ![Template info sheet](../../../../assets/nx-cloud/template-info.avif) - Feat: Allow members of GitHub organizations to connect existing repos to Nx Cloud - Feat: Made the "default branch" workspace setting available for all plans - Feat: Quickly configure recommended access control settings from the "Access control" workspace settings page - Feat: Updated the "Connect your GitHub/GitLab user account" prompt with improved styling and doc links - Feat: Add a prompt to remind users to set up notification settings - Fix: Minor style and description adjustments for the manual onboarding flow - Fix: Manual onboarding flow shows `NX_CLOUD_API` variable for instances other than Managed US - Fix: The GitHub integration configuration view now allows authenticating through a Personal Access Token or by connecting your GitHub user account - Fix: Fix onboarding redirect for users without their GitHub account connected ##### Developer experience - Feat: Add a "Jump to top" button to agent and task logs - Feat: Add tip to pull request message to run `generate ci-workflow` - Feat: Refreshed application error views with more friendly and helpful error messages and illustrations - Feat: Updated the permission error illustration - Fix: A reasonable fallback image will be shown when a user avatar cannot be rendered - Fix: Agent utilization visualization correctly visualizes agent restarts - Fix: Compare tasks diff view correctly displays `.` in paths (applies to new hash details data) - Fix: Fixed npm dependency hashing in the Compare Tasks view to include package names - Fix: Updated a few outdated links to the docs ##### Workspace data & caching - Feat: Fine-grained control over workspace data caching - `NX_CLOUD_SKIP_WORKSPACE_DATA_CACHE_UPLOAD` — skip the upload step - `NX_CLOUD_SKIP_WORKSPACE_DATA_CACHE_DOWNLOAD` — skip the download step - Existing `NX_CLOUD_SKIP_WORKSPACE_DATA_CACHE=true` takes precedence and skips both actions ##### Task distribution & hooks - Fix: Resolves an issue where 'target group' name metadata was only persisted on DTE tasks that had been retried, causing inconsistent viewing on Task Analytics and Time Saved screens - Fix: Post task execution hook no longer errors when distributed tasks are skipped due to upstream dependency failures - Fix: Unified task timing data between DTE and local execution modes when using the post task execution hook (startTime and endTime fields now attached directly to `context.TaskResult.task`) - Fix: Fixed a bug where a distributed task execution could slow or hang when processing a completed graph - Fix: Fix non-Nx Agents detection for resource usage metrics upload prompt ### 2025.10 ##### Self-healing CI / AI fixes - See [self-healing CI guide](/docs/guides/nx-cloud/enable-ai-features#self-healing-ci-for-enterprise-on-prem-installations) for enablement steps. - Feat: Automatically detect and fix failing CI tasks with AI-generated solutions - Feat: New `nx-cloud self-heal apply-locally` command to test AI fixes before applying them in CI - Feat: Revert previously applied AI fixes or automatically apply verified ones - Feat: GitHub review comments now surface suggested AI-generated fixes - Feat: Improved verification flow, prompt wording, and commit awareness to increase fix quality ##### Graph & visualization - Feat: Workspace graph now surfaces implicit dependencies and Nx version data - Feat: New circular dependency analysis view - Feat: Graph toolbar persists state and adds refined controls - Feat: Edge interactions open context menus with additional details ##### Analytics & reporting - Feat: Time to Green (TTG) reports compare hypothetical versus observed time savings - Feat: Context switch tracking chart highlights time saved by reducing interruptions ##### Custom workflows - Feat: Configure working directories per workflow step - Feat: Apply environment variables across custom workflows - Feat: Improved launch template management UI ##### Onboarding - Feat: Revamped end-to-end UI-only onboarding flow for GitHub workspaces - For this to work, please enable the "Repository Administration Write" permission in your GitHub, to allow NxCloud to create new repositories ##### Enterprise features - Feat: SCIM support for Okta, PingIdentity and Azure AD ##### Conformance - Feat: Better commands and validation when publishing conformance rules - Feat: Improved GitLab section mapping to owners files ##### Developer experience - Feat: Better reconciliation and visibility for restarted agents (view which tasks caused an agent to restart and previous logs for restarted agents) - Feat: Comprehensive dark mode refinements across the application ##### Deployment - Chore: Released Helm chart `1.0.0` for Nx Cloud - Review the [migration guide](https://github.com/nrwl/nx-cloud-helm/blob/main/charts/nx-cloud/MIGRATION.md) before upgrading ### 2025.07.7 - Fix: Resolve zod dependency issues caused by Docker images being built without package-lock file - Fix: Normalize S3 bucket configuration to support assumedRole authentication for workspace data and conformance rule buckets - Feat: CIPE summary table updated to display CIPE link outside the table for improved readability ### 2025.07.4 - 2025.07.6 - Note: These versions had issues and should be skipped. Please use 2025.07.7 or later. ### 2025.07.3 - Fix: use short link when generating DTE summary URLs for CI Pipeline Executions ### 2025.07.1 - Fix: auth redirect loop when using admin login - Fix: improvement to the flaky task retry mechanism - Fix: enable run hooks on DTE ### 2025.07 ##### Breaking change This upgrade includes a breaking change to the `nx-cloud` cluster: instead of a message queue, the `nx-api` pod needs a valid Valkey (Redis) connection string. 1. Install Valkey: 1. You can either use the Bitnami chart: https://github.com/bitnami/charts/tree/main/bitnami/valkey 2. Or for a simpler deployment, you can use the Valkey docker image directly: https://hub.docker.com/r/valkey/valkey/ 3. Or you can install it as a system service: https://valkey.io/topics/installation/ 2. Upgrade to the latest Helm chart `0.16.3` 3. Apply the following values ```yaml enableMessageQueue: false nxApi: # add these env vars to the nx-api deployment: env: - name: VALKEY_CLIENT_PROVIDER value: 'redisson' - name: VALKEY_PASSWORD valueFrom: # remember to apply this secret to your cluster secretKeyRef: name: valkey-secrets key: VALKEY_PASSWORD - name: VALKEY_PORT value: '6379' - name: VALKEY_PRIMARY_ADDRESS value: 'valkey' - name: VALKEY_USE_SENTINEL value: 'false' - name: VALKEY_USERNAME value: 'default' - name: NX_CLOUD_CONFORMANCE_RULES_BUCKET value: local-cluster-file-server # use this exact value if you are using the file server, otherwise point it to an S3/Azure/Google bucket # it will use the same role-based auth mechanism you already configured for the NxCloud cache # you can also use the same bucket name that you use for the cache (rules will just be stored in a sub-folder) ``` ##### Updates - Feat: Conformance, Workspace Graph, and Custom Workflows availability - Feat: Nx 21 [continuous tasks](https://nx.dev/blog/nx-21-continuous-tasks) support - Feat: Download artifacts button - When you view a task that just ran in CI on the NxCloud UI, there is now a button to download any artifacts that task produced directly from your browser - This is especially useful if you want to view screenshots/videos of failed e2e tests - Feat: [Self-healing CI](/docs/features/ci-features/self-healing-ci) - Speak to your assigned DPE about testing this - You will need an Anthropic API key and access to Claude's servers - Feat: Dark Mode UI setting - Various fixes and stability improvements to DTE, agent visualization, and other areas of the app ### 2025.06.3 - Fix: add timeouts to GitLab requests - the defaults are now 5 and 10 seconds for connect and read - these should help prevent issues with certain unstable GitLab environments ### 2025.06.2 - Fix: Terminal outputs not loading in the browser in restricted environments - Requires an update to the latest current nx Helm chart version 0.16.3 ### 2025.06.1 - Fix: GitHub connection issue on nx-api startup when Nx Agents are active ### 2025.06 - Feat: Define your own custom resource classes (CPU, RAM etc.) for use with Nx Agents - See [configuration details](https://github.com/nrwl/nx-cloud-helm/blob/main/EXTERNAL-RESOURCE-CLASSES.md) - Feat: Flaky task retry configuration - Configure in the workspace settings how Nx Cloud should handle flaky tasks - Feat: Full GitLab integration - Allows automatic members sync to your Nx Cloud workspace - Feat: Agent logs timestamps and full-screen mode - Feat: Assignment rules updates - Parallelism configuration - Target globs - See [here](/docs/kb/assignment-rules#how-to-define-an-assignment-rule) for examples - Feat: Parallel agent steps and step groups (docs [here](/docs/kb/launch-templates#launchtemplatestemplatenamegroupname)) - Feat: Reusable agent launch template snippets via yaml anchors - See example [here](/docs/kb/launch-templates#full-example) - Specifically how `common-init-steps: &common-init-steps` is defined - Feat: Individual GitHub commit statuses for each run group - This is configurable in your workspace settings - See [here](/docs/kb/setup-ci#github-status-checks) for branch settings configuration - You might also need [to update your GitHub app permissions](/docs/kb/github-app-permissions) - Misc: CIPE list is sortable by duration - Fix: early DTE job termination improvements - Fix: run details page performance improvements ### 2025.03.3 - Feat: provide prebuilt Java cert store to NxAPI - Full details [here](https://github.com/nrwl/nx-cloud-helm/blob/main/PROXY-GUIDE.md#pre-built-java-cacerts) ### 2025.03.2 - Feat: Nx Agents "bundled executors" - up until now, the "executor" binaries that run on each Nx Agent (and know how to parse your agents.yaml and run each step) had to be downloaded separately from an external bucket - this made the on-prem upgrade process more difficult, as it required a separate step to download the executor and then upload it in the correct folder on an internally available repository - now, the executors are available as Docker images that can be imported alongside all your other NxCloud images - to get started: - when upgrading to this version, make sure you also pull in the executor image `nxprivatecloud/nx-cloud-workflow-executor:2025.03.2`([link](https://hub.docker.com/repository/docker/nxprivatecloud/nx-cloud-workflow-executor/tags/2025.03.2/sha256-a42835a3126f21178af87f02b68d68fec1ff0654d37a57855a762c01e7795a6b)) - as part of your controller [args](https://github.com/nrwl/nx-cloud-helm/blob/main/charts/nx-agents/values.yaml#L76) pass this option: ``` args: # pass the internal image registry where the pods can pull the executor images from # for example: image-registry: us-east1-docker.pkg.dev/nxcloudoperations/nx-cloud-enterprise-public image-registry= # you can REMOVE the below option, as it's not needed anymore # kube-unix-init-container-name=... ``` - you no longer need to upload the executor binary separately to a bucket - now whenever you start your agent pods, they will load the above image and copy the executor from there ### 2025.03.1 - Fix: use custom "github URL" (if defined) when checking out the repo on Nx Agents ### 2025.03 ##### Assignment rules Assignment rules allow you to control which tasks can run on which agents. Save on agent costs by provisioning different sizes of agents to suit the individual needs of your tasks. You can ensure resource intensive targets like `e2e-ci` and `build` have what they need by using larger agents. Lighter tasks like `lint` and `test` can run on smaller agents. Assignment rules are defined in yaml files within your workspace's `.nx/workflows` directory. You can use assignment rules with DTE-agents or with dynamic Nx Agents. Note that additional configuration is required when using DTE agents. Read the full docs [here](/docs/kb/assignment-rules#assignment-rules-beta). Once you start using assignment rules, you'll be able to see all your configured "rules" in your CIPE "Analysis page". ##### DTE/Agent utilization visualization Speaking about the CIPE "Analysis" page, the agent utilization graph has been completely revamped. The new agent utilization visualization allows you to see when agents were actively executing tasks, and gaps when agents were idle. You can use this tool to optimize how work gets distributed, and modify your commands and dependencies to remove idle time. Tasks that hang will be highlighted in yellow, helping you debug OOM issues. If you’re using Nx Agents, you’ll also see set up steps on the visualization. ##### Workspace data caching Before an Nx command is run, Nx will generate some metadata that it will use when evaluating tasks (i.e project graph) and store that data in the workspace-data folder. This short process is relatively quick for small repos and only needs to be performed upon the first call to Nx. However, for larger repos and cases where Nx is frequently generating this information from scratch, it becomes a large time sink. In CI, workspace-data needs to be generated each time a new pipeline is run and each agent needs to generate its own identical copy. You can now set-up NxCloud to cache the default branch's workspace data and allow pipeline agents to retrieve it from the cache rather than regenerate this metadata each time. To enable it, you need to set this env variable on the nx-api deployment: - `NX_CLOUD_WORKSPACE_ARTIFACTS_STORAGE_BUCKET=` - it will then use the same authentication mechanism you've set up for your main cache bucket - if you do not use a cloud provider bucket such as S3, you can set this variable to `NX_CLOUD_WORKSPACE_ARTIFACTS_STORAGE_BUCKET=file-server` and it will use your local cluster file server ##### Misc items - a new version of the AMQ image was released with the latest security patches and fixes - node modules caching fixes on Nx Agents - previously, we were always recommending caching the `node_modules` folder itself in your Nx Agents yaml configs - this does not work with `npm ci`, as it always deletes the local `node_modules` folder before starting the installation. Instead, NPM recommends caching the `$HOME/.npm` directory. - Yarn and PNPM also have their own dedicated folders they recommend for caching - Part of this release, we now fixed caching folders in the `$HOME` directory, so all the below options should work: - `~/.npm` - `~/.cache/yarn` - `.pnpm-store` (note PNPM on Nx Agents does not store its cache folder in the \$HOME dir) - Please refer to the [custom launch templates docs](/docs/kb/launch-templates#full-example) for how you can setup your caching under these new recommendations - Nx Agents `$HOME` directory mounting - previously, when your Nx Agents pods were starting up, we were mounting as a k8s volume just the folder in which you checkout your repo: `$HOME/workspace` - however, a lot of dependencies and third-party apps use `$HOME` folder to deposit a lot of files (Rust, NPM cache folders etc.) - this caused agents to fight for available space on the node itself, causing very hard to debug issues if the space requirements were too big - part of this release, we now mount the whole `$HOME` directory as a volume, ensuring each agent gets a predictable storage size allocated - this also enables Nx Agents to run in more restricted environments (such as OpenShift), where read-only file systems are enforced (due to mountable volumes being writeable) - to enable this: - ensure you use (or are extending from) one of our pre-built agents base images - this is the image you set in your `image:` portion of your `agents.yaml` - (you are most likely using one of our images, so you can probably skip this step) - if you had to import the above image into your own internal registry, ensure it is part of a repository called `nx-agents-base-images` - Example (see the `nx-agents-base-images` part in this path): `image: 'us-east1-docker.pkg.dev/nxcloudoperations/nx-cloud-enterprise-public/nx-agents-base-images:ubuntu22.04-node20.11-v12'` - enable the `--copy-home-dir-init-container` flag [on the `controller.deployment.args` section in your Nx Agents `helm-config.yaml`](https://github.com/nrwl/nx-cloud-helm/blob/main/charts/nx-agents/values.yaml#L69) - increase restart amount for agents - if any of your agents go down (either because one of their init steps fails, due to networking issues for example) or they run out of memory, we now try to restart them up to `N` times, where `N` is the number of agents you have - this should result in more pipeline stability, though it is worth to still monitor the failed steps to ensure any persistent issues get addressed - addresses various potential race conditions in the NxCloud runner when restoring items from the cache (this was mainly noticeable on very large workspaces) - various UI issues with the "compare tasks diff" have now been addressed - this is the tool used to diagnose why a cache hit did not occur and what the differences are between two given hashes ### 2025.01.4 - Misc: adds new custom Nx Agents resource classes ### 2025.01.3 - Misc: adds new custom Nx Agents resource classes ### 2025.01.2 - Fix: issue with decoding certain branch names in the URL (fixes loading certain run pages) ### 2025.01.1 - Fix: adds data migrations for older organizations ### 2025.01 ##### Affected project graph The affected project graph for pull requests has now made it to the on-prem release! [Read the full announcement here](https://nx.dev/blog/ci-affected-graph). ##### DTE improvements - There have been a lot of performance improvements to the DTE algorithm and how tasks get sorted to ensure optimal distribution - Improved early agent shutdown: we now look at more parameters to decide whether we can shutdown a DTE agent earlier - Project graph integrity checks - both the main job and the agents require the exact same project graph for the DTE algorithm to run correctly - differences can appear, for example, if the agents or main job restore an older cached version of the project graph (instead of re-calculating the current one) - it can also happen if the main job and agents run off of different commits (maybe your main CI job does a `git merge` with `main` and your agents do not) - we now explicitly check if the agents and main job run on the same exact commit hash and also if they use the same project graph: otherwise we fail the DTE early - `stop-agents-after` now supports target configs - if you are running two affected commands at different points in your main job, each triggering the same target but under different configurations - `nx affected -t build:config1` - `nx affected -t build:config2` - you can configure agents to wait for both of them to complete before ending the DTE - `--stop-agents-after=build:config1,build:config2,lint,test` ##### Nx Agents improvements Previously, if an agent ran out of memory or crashed in the middle of its run, the logs would be lost. Now, we have a dedicated long-running "log uploader" that can upload logs even if the main agent container crashed. To enable, you will need to configure the following env var on your workflow controller: ```yaml - name: LOG_UPLOADER_IMAGE value: 'us-east1-docker.pkg.dev/nxcloudoperations/nx-cloud/nx-cloud-workflow-log-uploader' ``` We now also make much fewer requests to GitHub (or your other VCS providers) during a CIPE start, so you should see improved Nx Agents startup times. ##### PR comments look refresh The PR comment containing status updates about your CI execution has had make-over, showing a more clear breakdown of your runs, their duration and the status: ![new PR comment](../../../../assets/nx-cloud/new_github_comment.png) ##### Misc items - while we do our best to infer your commit message to display on the CIPE page, if it ever doesn't look right, you can manually override in your CI pipeline by setting `NX_CLOUD_COMMIT_MESSAGE` - improved workspace analytics controls - we now print more information on the main CI job summary table, such as a direct link to the associated CIPE - there is now a workspace level setting enabling or disabling flaky task retrying ### 2024.10.3 - Feat: Support NO_PROXY env var on pods ### 2024.10.2 - Fix: AWS S3 bucket connections when using STS role-based authentication ### 2024.10.1 - Fix: GitHub and external bucket connection issues when using a proxy ### 2024.10 This is a big release so let's go through the highlights first. There is also an important "Breaking changes" section at the end. ##### New version structure We have changed our version structure to a more simplified tag: `YEAR.MM.PATCH_NUMBER` The goal is to make it easier to spot how old/recent your existing NxCloud version is and compare it against newer deployments. ##### DTE summary table on main agent When distributing with DTE, up until now, we have been replaying all your tasks logs "as they come in" from the DTE agents back onto your main job. This is not that useful on big workspaces, with large task affected task graphs as it can be hard to follow all the outputs from all the agents streaming back concurrently. This release contains the new "CI Table Log View" summary, and you can [read all about it here](https://nx.dev/blog/improved-ci-log-with-nx-cloud). If you prefer the old logs style, you can always disable the feature via your workspace's settings screen. ##### Personal access tokens Up until now, for developers to get access to read (and maybe write) to the cache you always needed an access token to be made available locally: either committed to the repo via `nx.json` or made available as an env variable via a `.local.env` file. This flow was secure enough as is, even if you never rotated your access tokens, as someone would still need continuous hourly access to your source code if they wanted to retrieve any of the latest cached artifacts. But given you already manage developer access to your NxCloud workspace via the web app, we wanted to tie local cache access to that mechanism as well. This release contains the new ["Personal Access Tokens"](https://nx.dev/blog/personal-access-tokens) feature that now asks developers to login locally before they can use the cache. If they are a member of the workspace, they get a local token stored on their machine that will be used to access the cache. The moment they get removed as a member from your workspace, they won't be able to read from the cache anymore. Please read the full announcement post here, as it contains details on how to migrate your team to using [Personal Access Tokens](https://nx.dev/blog/personal-access-tokens). ##### GitHub app integration If you are using GitHub, setting up a custom GitHub app for your org is the best way to take advantage of all the latest "GitHub-specific" features we offer. Please see instructions [here](/docs/enterprise/single-tenant/custom-github-app) on setting up an app. You will then need to make sure you set up your VCS integration again through your workspace's settings screen, and use the above app you created. As part of this, you will also get the "GitHub membership management" feature, where everybody who is a collaborator of your GitHub repo will also get "read" access to your NxCloud workspace, without you having to explicitly invite them. ##### Misc items - Improved docker agents support - We fixed a few issues related to running docker builds in Nx Agents - Big DTE performance improvements - Azure file storage for Nx Agents - Auth session length has been extended to 7 days by default - Use NX_CLOUD_SESSION_MAX_AGE to configure this to a different value - Various SAML fixes and improvements - One highlight is that users can now login from Okta directly (while before they had to initiate login through NxCloud web app) ##### Breaking changes Most workspaces will not be affected by this, but if you have these values configured in your `helm-config.yaml`: - `github.pr.[...]` - or `gitlab.mr.[...]` they will stop working with this release (see [this](https://github.com/nrwl/nx-cloud-helm/pull/141/files) for details on what was removed). Please go to your workspace settings and you should be able to configure all the above values when you setup a VCS integration. _Terminal outputs_ in the web app will now be fully served from storage bucket (either S3/Gcloud/Azure, or your internal file-server). This means your NxCloud cluster needs to have an open/healthy connection to the bucket. You can test this by ssh'ing into the `nx-cloud-frontend` pod and trying to `wget` one of your bucket artefacts. Any proxy or firewall constraints will need to be handled. Additionally, if your bucket is hosted at a self-signed https URL, any fetch calls from the frontend pod to your bucket will fail. If you think any of this applies to you, please contact your DPE to discuss options. ### 2406.29.1.patch1 - Fix an issue with specifying custom AWS credentials in Minio instances - Fix an issue with removing pending invites ### 2406.29.1 ##### Full terminal outputs in the web app Due to storage constraints in Mongo, long terminal outputs were sometimes truncated when viewed in the UI. With this update we are now loading all terminal outputs directly from the storage bucket, removing the need to keep them in Mongo. You should now be able to view full, complete logs in the UI regardless of how large the output is. ##### OpenShift fixes for agents - the latest messagequeue image is now OpenShift ready - to use, just update to the latest Helm version `0.15.6` and make sure you are not passing in an explicit tag for the messagequeue - then use version `2406.29.1` for NxCloud. This should use the latest, OpenShift enabled messagequeue image - when running Agents on OpenShift, they run as a specific user with ID 1000 - to override this, make sure to set `NX_CLOUD_RUN_UNIX_PODS_AS_USER: ` and `NX_CLOUD_RUN_UNIX_PODS_AS_GROUP: ` on the [workflow controller env vars](https://github.com/nrwl/nx-cloud-helm/blob/main/charts/nx-agents/values.yaml#L63) ##### Full bitbucket data center (on-prem) We now have full support for BitBucket Data Center (self-hosted): - VCS integration for posting comments with live updates about your CI runs - full agents integration - more info about each one of your commits on the NxCloud web app - you can even [set-up auth with BitBucket Data Center](/docs/enterprise/single-tenant/auth-bitbucket-data-center#bitbucket-data-center-auth) ##### Misc - easier workspace setup experience for new customers - the CIPE visualisation has been updated (elapsed task time) - general web app performance improvements ##### Breaking changes If you are using DTE, you will now need to pass the `--distribute-on="manual"` flag to your `npx nx start-ci-run` commands. ### 2405.02.15 ##### Easy membership management via GitHub A few months ago, we introduced a new feature to our managed SASS NxCloud product: easy membership management via GitHub. If you create a new workspace on [https://cloud.nx.app/](https://cloud.nx.app/) right now you will be guided through how to connect it to your GitHub repository. Now everyone that has access to your GitHub repository will also get access to your NxCloud workspace. If anyone loses access to GitHub (maybe they leave the company), they will also lose access to NxCloud. This makes membership management easy and straightforward, as you don't have to manually invite users anymore. Of course, setting up this connection also gives you NxCloud run status updates directly on your PRs - a feature we've had for a long time. This feature has now been release for on-prem set-ups as well. To benefit from it, you'll need to create your own Github App with permissions to access your repository. Your on-prem NxCloud instance will then use this app to pull membership info from Github and check user permissions. You can find [the full setup instructions here](/docs/enterprise/single-tenant/custom-github-app). ##### DTE v2 enabled by default After testing the improved task distribution algorithm (DTE v2) for the past few months, we are now enabling it by default for all customers. Expect quicker CI run times when using DTE, and better utilization of your agents with less idle time. ##### Nx Agents and breaking changes If you are using Nx Agents, this release will contain a breaking change to the workflow controller. Before upgrading to this version, you'll need to follow the new [Agents Guide](https://github.com/nrwl/nx-cloud-helm/blob/main/agents-guide/AGENTS-GUIDE.md) and deploy an instance of Valkey that your controller can connect to. The reason we need Valkey is that the workflow controller now persistently stores information about your workflows for up to 8 hours, and these changes will be persisted regardless of the availability of the workflow controller pod, making your in-progress workflows more resilient to rolling kubernetes updates, and will fix some previous issues with agent statuses not syncing to the UI. If you are not using Nx Agents, this does not affect you and you can upgrade to this version straight away. ##### UI improvements - If you are using the new Crystal plugins in Nx 18, we've now added a "technologies label" to each task, so you can quickly see which tasks are Playwright based, Cypress, React etc. - We've added toast notifications in the app. You'll see them confirming some of your actions, such as saving workspace changes. ##### Misc fixes - We've fixed various bugs around the task distribution algorithm and Nx Agents. CIPEs using distribution should feel more stable and faster. - We fixed a few issues relating to the GitLab and BitBucket integrations. ### 2404.05.9 ##### DTE algorithm v2 experimental flag For the past 2 months, we've been re-writing our entire task distribution algorithm. The aim was to allocate tasks more efficiently to agents, reduce total time spent by agents downloading artefacts and reduce idle agent time waiting for tasks. While the features is still in its beta stage, initial tests do show a big improvement in overall CI completion time (but this varies on a case by case basis). If you are already using DTE or Nx Agents, you can enable this experimental feature by adding the following env var to your main job (the job where you invoke `npx start-ci-run`): ```yaml NX_CLOUD_DTE_V2: 'true' ``` ##### Nx Agents on-prem availability Since the previous release, we've been testing various options for deploying Nx Agents on-premise. We now have a dedicate Helm chart dedicated to setting up an Nx Agents cluster on your infrastructure: 1. ⚠️ Please reach out to your DPE first so we can start an Nx Agents trial and discuss any limitations and requirements up-front 2. You can view the example `values.yaml` [here](https://github.com/nrwl/nx-cloud-helm/blob/main/charts/nx-agents/values.yaml) 3. Once we had a chance to look at your existing CI compute requirements, you deploy the chart via `helm install nx-cloud nx-cloud/nx-agents --values=helm-values.yml` ##### Audit logger As an Enterprise installation admin, you can now view audit logs over NxCloud workspace events by visiting `https:///audit-log`. This includes events such as when a workspace was created, when a new VCS integration was set-up and so on. ##### UI improvements - Organizations can now be created directly from the "Connect a workspace" screen - Full screen terminal outputs for your tasks ### 2402.27.3.patch3,4,5,6 - Feat: allows customising the base image for the agent init-container (in case it is self-hosted internally in the company) - Feat: adds more logging to debug authorization errors for Github, Gitlab and SAML - Fix: fixes an issue with using custom launch templates on GitLab ### 2402.27.3.patch3 - Feat: allows disabling the automated pod watcher which doesn't behave as expected in some k8s engines ### 2402.27.3.patch2 - Feat: allows volume class to be customised for Agents ### 2402.27.3.patch1 - Fixes an issue with the aggregator creating empty organisations during the first migration ### 2402.27.3 With this version you can take advantage of most features announced during our recent [launch week](/blog/launch-nx-week-recap). ##### Nx Agents This release contains everything needed to run [Nx Agents](/docs/features/ci-features/distribute-task-execution) on-prem. While the on-prem configuration is still experimental, we are actively running Nx Agents trials at the moment, and if you'd like to take part please reach out to your DPE. If you already running DTE, there are a few advantages to upgrading to Agents: - simplified CI config: you will need to maintain just a single, main CI job config. NxCloud will create needed CI agents for you as needed. - [dynamic agent allocation based on PR size](/docs/features/ci-features/dynamic-agents): instead of always launching all your agents NxCloud will now launch different number of agents dynamically based on your PR size - access to [Spot instances](https://aws.amazon.com/ec2/spot/): if you are running your clusters on any of the popular cloud providers (AWS, Google Cloud, Azure etc.), you can now use their Spot instances for running your CI job. This is possible due to NxCloud's distribution model, which allows work on a reclaimed node to be re-distributed to the remaining agents. We will shortly make available a new Helm chart that will allow you to deploy a separate Agents cluster to launch workflows: [https://github.com/nrwl/nx-cloud-helm](https://github.com/nrwl/nx-cloud-helm). ![agents_screen](../../../../assets/nx-cloud/agents.webp) ##### Task Atomizer and task retries If you combine this release + upgrade to the latest Nx 18, you will have access to both the [task atomizer](/docs/features/ci-features/split-e2e-tasks) (which allows your e2e to be distributed among agents PER FILE, instead of previously per project) and the [flaky task retry functionality](/docs/features/ci-features/flaky-tasks). ##### CIPE page improvements Along with all the UI changes to support agents (following their logs and track how tasks get distributed,details of which you'll find demoed on [this page](/docs/features/ci-features/distribute-task-execution)) this release also brings all the new improvements to the CI pipeline execution page, including the commit info panel at the top: ![cipe_top_half_screen](../../../../assets/nx-cloud/cipe_top.webp) ### 2312.11.7.patch1 - re-enable path style access for s3 buckets - fix an aggregator migration issue with old CIPE data ### 2312.11.7 ##### Helm package compatibility When upgrading to this version and anything above it, you will need to use Helm version 0.12.0+: | Chart Version | Compatible Images | | :-----------: | :--------------------------------: | | <= `0.10.11` | `2306.01.2.patch4` **and earlier** | | >= `0.11.0` | `2308.22.7` **and later** | | >= `0.12.0` | `2312.11.7` **and later** | ##### New UI features and improvements On the UI, we replaced the runs overview with the new CI Pipeline Executions (CIPE for short) screen: ![cipe_screen](../../../../assets/nx-cloud/cipe_screen.webp) This screen organises your runs more logically, according to each invocation of your CI pipeline. It provides more data around the committer name and commit message and a full analysis of your CIPE once it is completed. And if you need to run your tasks on multiple environments, you can now switch between them on this page and view the results separately. You can play around with an example on the [Nx Repo](https://staging.nx.app/orgs/62d013d4d26f260059f7765e/workspaces/62d013ea0852fe0a2df74438/overview) There is also a new Analytics screen for your workspaces, to which we'll keep adding new features to better help you optimise your CI pipelines: ![analytics_screen](../../../../assets/nx-cloud/analytics_screen.webp) Here you can see: - historical trends of CIPE Average duration - historical trends of CIPE average daily count - average daily time saved by DTE Other improvements: - better overall UI performance (navigating feel much snappier now) - improved terminal output rendering - members can now be invited as admins directly ##### The light runner Nx Cloud works by using a local Node runner that wraps your Nx tasks and sends information about them to the Nx Cloud API. This is how it knows whether to pull something from the remote cache or run it. Because they work together, sometimes changes to the API required updates to this local runner. This led to workspaces that did not update their local runner version in `package.json` sometimes running into compatibility issues. We overhauled this mechanism, and the runner is now bundled as part of the API itself, ensuring you get sent the correct runner code when you first start running Nx commands in your workspace. This ensures you will always have the correct local runner version that is compatible with your on-prem Nx Cloud installation. We've been testing this out on our Public Nx Cloud instance and it is now available for on-prem installations as well. To enable the light runner feature, make sure you: 1. remove `useLightClient: false` from your `nx.json` (if you had it) 2. If you are on Nx version > 17, you can remove any `nx-cloud` or `@nrwl/nx-cloud` package in your `package.json` and it should just work 3. If you are on Nx version < 17, upgrade to `nx-cloud@16.5.2` or `@nrwl/nx-cloud@16.5.2`. ##### Nx Agents This release is also the first one to support ["Nx Agents"](/docs/features/ci-features/distribute-task-execution). While currently experimental and disabled by default for on-prem users, we are looking for more on-prem workspaces to try it out with so please reach out to your DPE contact or to [cloud-suppport@nrwl.io](mailto:cloud-support@nrwl.io) if you are interested in helping us shape this according to your needs! ##### Breaking changes - MongoDB migration As a reminder, we now only support MongoDB 6+. If you are running an older version please refer to the upgrade instructions [here](/docs/reference/nx-cloud/release-notes#breaking-changes). ### 2308.22.7.patch7 - Allows the frontend container to be ran with `runAsNonRoot: true` ### 2308.22.7.patch6 - Fixes a UI issue on the branch when running a task in a DTE context ### 2308.22.7.patch5 - Fixes a UI issue when navigating to branches containing slashes ### 2308.22.7.patch4 - Updates the frontend image to remove some vulnerability issues ### 2308.22.7.patch3 - Fixes a compatibility issue with the latest `nx-cloud` release ### 2308.22.7.patch2 - Fix: github member invites ### 2308.22.7.patch1 - Feature: self-signed certificate support for aggregator - This is needed if you are using self-signed certificate for your external Mongo instance - See [here](https://github.com/nrwl/nx-cloud-helm/blob/main/PROXY-GUIDE.md#supporting-self-signed-ssl-certificates) for usage details. - Fix: aggregator issue when creating text Mongo indexes ### 2308.22.7 In our last big release, we announced a completely new UI, rebuilt from the ground up in React. In this release, the frontend team has continued that effort and wrapped the React app with the [Remix](https://remix.run/) framework. This is the same technology that powers our public https://cloud.nx.app/ product. It's faster, it handles resource caching better, and should allow the frontend team to ship features quicker than ever before. ##### Helm package compatibility When upgrading to this version and anything above it, you will need to use Helm version 0.11.1: | Chart Version | Compatible Images | | :-----------: | :--------------------------------: | | <= `0.10.11` | `2306.01.2.patch4` **and earlier** | | >= `0.11.0` | `2308.22.7` **and later** | ##### VCS proxy support - For the GitHub/Bitbucket/Gitlab integrations to work, Nx Cloud needs to make HTTP calls to GitHub/GitLab to post comments - If are behind a proxy however, these requests might fail - If you are using our [Helm chart](https://github.com/nrwl/nx-cloud-helm/), you can now configure this option to unblock the vcs integration and allow it to work with your proxy: ```yaml vcsHttpsProxy: '' ``` ##### Misc updates - UI enhancements of the run details screen - UI enhancements of the task details screen - fixes and better error handling for the DTE screen - failed runs are now sorted at the top - web app performance improvements for large workspaces - more structured NxAPI pod logs (allows for better debugging) ##### Bug fixes - Fixed an issue with applying licenses on orgs owned by non-installation admin accounts ##### Breaking changes - MongoDB migration In the last big release we announced [the deprecation of Mongo 4.2](/docs/reference/nx-cloud/release-notes#breaking-changes) With this release, we have now stopped supporting Mongo 4.2 completely. Please upgrade Mongo to version 6 before installing this new image. You will find instructions [here](/docs/reference/nx-cloud/release-notes#breaking-changes). ### 2306.01.2.patch4 - Fixes an issue with new licenses expiring sooner than original end date ### 2306.01.2.patch3 - Fixes an issue with multiple admin organizations being created on new installations - Fixes an issue where Enterprise licenses could not be applied on some new orgs ### 2306.01.2.patch2 - Fixes an issue with the `single-image` container where the aggregation would block the API from starting up ### 2306.01.2.patch1 - Fixes an issue where admin users were not being created on new installations. ### 2306.01.2 This is one of our biggest Nx Cloud On-Prem releases. It also marks a change in our release process which will be explained at the end. ##### Brand new uI A few months ago we announced a complete re-design of the Nx Cloud UI! It's faster, easier to use and pleasant to look at! We're now bringing this to On-Prem users as well: You can read more about it in our [announcement blog post](https://nx.dev/blog/nx-cloud-3-0-faster-more-efficient-modernized). ##### Pricing updates While before we provided you with a separate coupon for each workspace, we have now changed to "organization-wide licenses": you receive a single coupon for your whole organization, that gives you unlimited access for the agreed number of workspaces. You are then free to delete, create and re-shuffle your workspaces as often as you want without requiring new coupons for us (as long as you stay within your limit of workspaces). You will see some updates in the UI to reflect this, however, **you don't need to do anything once you update your images!** We'll automatically migrate you to this, based on your current number of enabled workspaces! ##### Proxy updates One of the features of Nx Cloud is its integrations with your repository hosting solution. When you open up a Pull Request, you can configure Nx Cloud to post a comment to it once your CI has finished running, with a summary of all the tasks that succeeded and failed on that code change, and a link to your branch on Nx Cloud so you can further analyse your run. Your developers save time, and allows them to skip digging through long CI logs. Before, if you had a self-hosted instance of GitHub, Gitlab or Bitbucket, calls from Nx Cloud to your code-hosting provider would fail, because they'd be using a self-signed certificate, which Nx Cloud wouldn't recognise. [We now support self-signed SVN certificates, via a simple k8s configMap.](https://github.com/nrwl/nx-cloud-helm/blob/main/PROXY-GUIDE.md#supporting-self-signed-ssl-certificates) [We've also made updates to the runner, to support any internal proxies you might have within your intranet.](https://github.com/nrwl/nx-cloud-helm/blob/main/PROXY-GUIDE.md#supporting-self-signed-ssl-certificates) ##### DTE performance We completely re-wrote our Task Distribution engine, which should result in much fewer errors due to agent timeouts, increased performance and more deterministic task distribution. We've also added a new internal task queueing system, which should further improve the performance of DTE. While this is an implementation detail which will be automatically enabled in future releases, you can test it out today by setting [`enableMessageQueue: true`](https://github.com/nrwl/nx-cloud-helm/blob/main/charts/nx-cloud/values.yaml#L18) in your Helm config. You can read more about the recent DTE improvements in our [Nx Cloud 3.0 blog post](https://nx.dev/blog/nx-cloud-3-0-faster-more-efficient-modernized). ##### Misc updates - We have fixed issues related to OpenShift deployments. [Special thanks to minijus](https://github.com/nrwl/nx-cloud-helm/pull/32) for his work on the Helm charts and helping us test the changes. ##### Breaking changes Nx Cloud uses MongoDB internally as its data store. While we've always used Mongo 4.2, in the latest release we started targeting Mongo 6.0. It's a much lighter process, with improved performance, and quicker reads and writes. While you can still upgrade to this new image even if you are on Mongo 4.2 (nothing will break), **we strongly recommend you upgrade your Database to Mongo 6.0 to make sure nothing breaks in the future.** [We wrote a full guide on how you can approach the upgrade here](https://github.com/nrwl/nx-cloud-helm/blob/main/MONGO-OPERATOR-GUIDE.md#upgrading-to-mongo-6). If you need assistance, please get in touch at [cloud-support@nrwl.io](mailto:cloud-support@nrwl.io). ###### Migration from community edition to enterprise On May 16th, 2023 we announced our plans to sunset the Community Edition of Nx Cloud On-Prem to align with our new pricing plans. If you are on the Community Edition, please follow these steps to migrate: 1. Use this image: `2306.01.2.patch3` 2. Switch to private Enterprise by setting `NX_CLOUD_MODE=private-enterprise` (or `mode: 'private-enterprise'` if using Helm). 3. Reach out to us at [cloud-support@nrwl.io](mailto:cloud-support@nrwl.io). You will get a FREE, unlimited-use coupon for the next 3 months so you can trial Nx Enterprise. ##### New release process With this update, we are also changing our release process: 1. We'll start adding release notes with every new version published 2. We switch to using [calver](https://calver.org/) versioning for our images 3. We stopped publishing the `latest` tag. 4. We will be emailing Enterprise admins with every new release. If you do not get these emails, please send us an email at [cloud-support@nrwl.io](mailto:cloud-support@nrwl.io) to get added Any questions at all or to report issues with the new release [please get in touch!](mailto:cloud-support@nrwl.io) ### 13-02-2023T23-45-24 - Feat: Targettable agents for DTE. You can now ask specific agents to pick up specific tasks (via `--targets - Fix: DTE fixes for 404 not found artefacts errors - Fix: issue when using GitHub integration with self-hosted GitHub instances ### 26-01-2023T21-22-48 - Misc: Fixes to the Gitlab integration ### 05-01-2023T17-53-45 - Misc: This release contains small bug fixes and UI improvements. ### 14-12-2022T19-43-44 - Feat: IAM Role Auth. We have now deprecated "aws_access_key_id" and "aws_access_key_secret" in favor of service accounts and IAM roles for accessing AWS resources. See the [new guide here](https://github.com/nrwl/nx-cloud-helm/blob/main/aws-guide/AWS-GUIDE.md) for details. ### 13-10-2022T16-45-30 - Misc: This release mostly contains improvements that apply to the Public SASS version of Nx Cloud. No significant changes for the On-Prem version. ### 13-10-2022T16-45-30 - Feat: Private Cloud now runs completely as Kubernetes cluster. See the [Helm example repo](https://github.com/nrwl/nx-cloud-helm) for more details ### 05-08-2022T15-42-20 - Fix: issue with retrieving hashes during reads - Feat: added route to display container version at `/version` - Misc: forward api errors to stderr so k8s clusters can process them better ### 02-08-2022T16-11-36 - Note: The version naming scheme for the containers was changed to better track date/time of releases and to support embedding of the version inside the web UI - Feat: view the container version under the `/errors` route - Feat: BitBucket login (note: does not support self-hosted instances of BitBucket Server) - Feat: New system-ui font scheme - Fix: branch screen sorting performance improvements ### 2.4.11 - Fixes an intermittent container start-up issue when running a self-contained Mongo instance - Fixes an issue with the self-hosted file-server where it would fail to create the initial directories ### 2.4.10 - Fix an issue with the admin password not being set correctly ### 2.4.9 - Align all Nx Cloud images to this version. No new fixes or features included. ### 2.4.8 {% callout type="caution" title="IMPORTANT" %} The default container mode has changed from `COMMUNITY` to `ENTERPRISE`. If you are running a Community version of the container, you will need to make sure the `NX_CLOUD_MODE=private-community` is explicitly set (otherwise your container will fail to start-up). {% /callout %} - Fix: Web app performance improvements - Fix: issue with GitHub logged in admins not being able to download logs - Fix: issue with billing page when multiple access tokens were attached to the same org - Fix: multiple Mongo DBs used to be created if a default DB was not provided in the connection string. Now it always defaults to the provided `NX_CLOUD_MONGO_DB_NAME` ### 2.4.7 - Misc: performance improvements to DB indexes - Misc: improvements to hash differ to use regex - Misc: export more collections for debug purposes (workspaces and organizations) ### 2.4.6 - Fix: issue with navigating to organizations/workspaces in the web app ### 2.4.5 - Feat: filters to branch and run list pages - Fix: improved `MD5` cache artifact archiving - Misc: various UI and UX improvements to the Nx Cloud dashboards ### 2.4.4 - Fix: Missing artefact retrieval error when using read-tokens - Fix: Performance improvements to the branch page and run groups sorting - Fix: better handling of artefact `.tar` archiving ### 2.4.3 - Feat: Billing page messaging improvements - Fix: runs sorting on branch page ### 2.4.2 - Feat: DTE post-run report - Feat: Hash Detail tool flow improvements ### 2.4.1 - Feat: Admins can now easily export debug info for error investigation - Fix: branch screen run group sorting ### 2.4.0 - Feat: [GitLab Auth Support](https://nx.app/docs/private-cloud-gitlab-auth)/private-cloud-gitlab-auth - Feat: Hash diffing tool improvements - Feat: show message on branch page if workspace is unclaimed - Fix: Agent out of memory warning - Feat: cache inner runs - Fix: include correct GitHub workflows path - Fix: default to most recent run group on branch page - Fix: handle DTEs with no tasks - Fix: await process checkout sessions ### 2.3.1 - Feat: Increase file-server default cached artifact limit. If you are not using an external file storage (such as S3), then the cached assets will now be kept by default from 2 weeks to 4 weeks, increasing the chance of cache hits. - Feat: "Download cache usage" data from the "Time saved" workspace page ### 2.3.0 - Feat: GitHub Integration - no token is now necessary in "`nx.json`" for the GitHub integration to work (you still need to provide as an env var for caching to work). To connect your workspace to GitHub without an access token in "`nx.json`" just pass in the "`NX_CLOUD_INTEGRATION_DEFAULT_WORKSPACE_ID=`" env var - Misc: better error handling (report less false positives) - Fix: Scheduled tasks locking ### 2.2.16 - Misc: DB performance improvements (old records clean-up aggregator, indexes etc.) ### 2.2.15 - Feat: Add options to control database load - Fix: Better exception handling in the API ### 2.2.14 - Feat: Optimize event processing to increase the throughput of workspaces with a very high number of agents. - Fix: Gracefully recover when stats aggregation fails ### 2.2.13 - Feat: Hash diffing tool enhancements ### 2.2.12 - Feat: DTE visualisation improvements for larger workspaces - Fix: billing page not displaying subscriptions for Private Community ### 2.2.11 - Feat: Better error handling for scheduled tasks - Fix: branch screen not loading ### 2.2.10 - Feat: Various UI improvements to the Nx Cloud screens - Feat: Hash detail diff tool - Feat: GitHub app comment revamp - Feat: DTE visualisation ### 2.2.9 - Fix: DTE bug fixes caused by incorrectly batched tasks ### 2.2.8 - Fix: various DTE bug fixes - Feat: Add `NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT` env var for more explicitly optimising DTEs - Feat: Send GitHub workspace membership invites by email - Fix: improve container start-up time - Feat: If Mongo connection fails during container start-up it keeps retrying up to a max number of times (configurable via `MONGO_MAX_RETRIES`) - Feat: expose "/ping" endpoint (can be useful for K8s readinessProbe) - `curl --fail http://localhost:8081/nx-cloud/ping --header "authorization: your-nx-cloud-access-token"` - Feat: billing estimator (on billing page) - Fix: ignore ending slash on `NX_CLOUD_APP_URL` (in case it's added by mistake) ### 2.2.7 - Feat: `VERBOSE=1` env variable option to output extra information during container initialisation - Feat: `MONGO_REPAIR=1` env variable option to trigger a [Mongo Repair](https://docs.mongodb.com/manual/tutorial/recover-data-following-unexpected-shutdown/) if the container data gets corrupted ### 2.2.3 - Fix: Reset the memory limits to best work on an instance with 8GB of RAM. - Fix: Set the default `NX_CLOUD_MODE` to "community". ### 2.2 - [Nx Cloud 2.2](https://blog.nrwl.io/%EF%B8%8F-nx-cloud-2-2-%EF%B8%8F-b7656ed5ce7c) ### 2.0 - [Overview of Nx Cloud 2.0](https://blog.nrwl.io/introducing-nx-cloud-2-0-f1e5c2002a65) --- ## Nx Console Settings Reference This page provides a complete reference of all configuration settings available for Nx Console. Each setting shows how to configure it in both VSCode and JetBrains IDEs (IntelliJ IDEA, WebStorm, etc.). ## Editor settings {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} VSCode uses JSON files for configuration. Settings can be configured at different scopes: **User Settings** (global across all workspaces): - Apply to all VSCode projects - Location: `~/.config/Code/User/settings.json` (Linux/macOS) or `%APPDATA%\Code\User\settings.json` (Windows) - Access via: `Cmd+,` (macOS) or `Ctrl+,` (Windows/Linux), then click the "Open Settings (JSON)" icon **Workspace Settings** (specific to one workspace): - Apply only to the current workspace - Location: `.vscode/settings.json` in your workspace root - Recommended for team-shared configurations (commit to version control) - Override user settings Learn more about VSCode settings in the [official VSCode documentation](https://code.visualstudio.com/docs/getstarted/settings). {% /tabitem %} {% tabitem label="JetBrains" %} {% aside type="note" title="JetBrains IDE Configuration" %} JetBrains IDEs configure Nx Console through the Settings/Preferences UI panel (**Settings > Tools > Nx Console**) rather than JSON configuration files. Only a subset of VSCode settings are available. {% /aside %} {% /tabitem %} {% /tabs %} ## Common Nx commands Configure which Nx commands appear in the sidebar view. {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} **Setting:** `nxConsole.commonNxCommands` **Default:** `["run", "run-many", "affected", "affected --graph", "list"]` ```json { "nxConsole.commonNxCommands": ["run", "run-many", "affected", "test", "build"] } ``` This setting supports both arbitrary commands and UI-based commands that open dedicated interfaces. {% /tabitem %} {% tabitem label="JetBrains" %} {% aside type="caution" title="Not Available" %} This setting is not available in JetBrains IDEs. Command customization is not currently supported. {% /aside %} {% /tabitem %} {% /tabs %} ## Project viewing style Defines how the Projects view displays entries in the sidebar. {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} **Setting:** `nxConsole.projectViewingStyle` **Options:** `"list"` | `"tree"` | `"automatic"` **Default:** `"automatic"` ```json { "nxConsole.projectViewingStyle": "tree" } ``` **Options explained:** - `"list"`: Shows projects as a flat ordered list - `"tree"`: Displays projects in a folder/tree structure - `"automatic"`: Adaptively switches between list and tree based on workspace structure {% /tabitem %} {% tabitem label="JetBrains" %} **Location:** Settings > Tools > Nx Console > Tool Window Style **Options:** List | Tree | Automatic **Default:** Automatic Navigate to **Settings/Preferences > Tools > Nx Console** and select the desired style from the "Tool Window Style" dropdown. **Options explained:** - **List**: Shows projects as a flat ordered list - **Tree**: Displays projects in a folder/tree structure - **Automatic**: Adaptively switches between list and tree based on workspace structure {% /tabitem %} {% /tabs %} ## Nx workspace path Specifies the relative path to the Nx workspace root directory. {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} **Setting:** `nxConsole.nxWorkspacePath` **Default:** `undefined` ```json { "nxConsole.nxWorkspacePath": "packages/workspace" } ``` Useful when working in a monorepo where the Nx workspace is nested within a larger repository structure. {% /tabitem %} {% tabitem label="JetBrains" %} **Location:** Settings > Tools > Nx Console > Nx Workspace Path **Default:** Empty (uses project root) Navigate to **Settings/Preferences > Tools > Nx Console** and enter the relative path in the "Nx Workspace Path" field. For example: `packages/workspace` {% /tabitem %} {% /tabs %} ## Enable codeLens Controls the visibility of CodeLens features for Nx-specific files. {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} **Setting:** `nxConsole.enableCodeLens` **Default:** `true` ```json { "nxConsole.enableCodeLens": false } ``` CodeLens provides inline actionable commands directly in your editor for files like `project.json` and workspace configuration files. {% /tabitem %} {% tabitem label="JetBrains" %} {% aside type="caution" title="Not Available" %} This setting is not available in JetBrains IDEs. CodeLens is a VSCode-specific feature. JetBrains IDEs use their native intention actions and code inspections instead. {% /aside %} {% /tabitem %} {% /tabs %} ## Enable library imports Configures the TypeScript language server plugin to include configured libraries. {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} **Setting:** `nxConsole.enableLibraryImports` **Default:** `true` ```json { "nxConsole.enableLibraryImports": true } ``` This enables better IntelliSense and autocompletion for library imports in your workspace. {% /tabitem %} {% tabitem label="JetBrains" %} {% aside type="caution" title="Not Available" %} This setting is not available in JetBrains IDEs. TypeScript library imports are handled through JetBrains' native TypeScript language services. {% /aside %} {% /tabitem %} {% /tabs %} ## Generator allowlist Specifies generator names or wildcard patterns to show in the generator picker. {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} **Setting:** `nxConsole.generatorAllowlist` **Default:** `[]` (all generators shown) ```json { "nxConsole.generatorAllowlist": [ "@nx/react:*", "@nx/next:application", "@nx/workspace:library" ] } ``` Use the format `@package:generator` or `@package:*` for wildcards. When this list is populated, only matching generators will be shown. {% /tabitem %} {% tabitem label="JetBrains" %} **Location:** Settings > Tools > Nx Console > Generator Allowlist **Default:** Empty (all generators shown) Navigate to **Settings/Preferences > Tools > Nx Console** and add patterns in the "Generator Allowlist" field, one per line: ```text @nx/react:* @nx/next:application @nx/workspace:library ``` {% /tabitem %} {% /tabs %} ## Generator blocklist Specifies generator names or wildcard patterns to hide from the generator picker. {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} **Setting:** `nxConsole.generatorBlocklist` **Default:** `[]` ```json { "nxConsole.generatorBlocklist": ["@nx/angular:*", "*:stories"] } ``` Useful for removing generators that aren't relevant to your workflow. {% /tabitem %} {% tabitem label="JetBrains" %} **Location:** Settings > Tools > Nx Console > Generator Blocklist **Default:** Empty Navigate to **Settings/Preferences > Tools > Nx Console** and add patterns in the "Generator Blocklist" field, one per line: ```text @nx/angular:* *:stories ``` {% /tabitem %} {% /tabs %} ## Enable dry run on change Enables automatic dry runs when using the Generate command. {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} **Setting:** `nxConsole.enableTaskExecutionDryRunOnChange` **Default:** `true` ```json { "nxConsole.enableTaskExecutionDryRunOnChange": false } ``` When enabled, changing generator options triggers a dry run to preview the changes before applying them. {% /tabitem %} {% tabitem label="JetBrains" %} **Location:** Settings > Tools > Nx Console > Enable Dry Run on Change **Default:** Checked (enabled) Navigate to **Settings/Preferences > Tools > Nx Console** and toggle the "Enable Dry Run on Change" checkbox. {% /tabitem %} {% /tabs %} ## Move generator patterns Controls which collections' move generators should be used based on project path patterns. {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} **Setting:** `nxConsole.moveGeneratorPatterns` **Default:** `{}` ```json { "nxConsole.moveGeneratorPatterns": { "apps/**": "@nx/workspace:move", "libs/**": "@custom/generators:move" } } ``` This is useful when you have multiple generator collections and want to specify which move generator to use for different project types. {% /tabitem %} {% tabitem label="JetBrains" %} {% aside type="caution" title="Not Available" %} This setting is not available in JetBrains IDEs. Move generator pattern customization is not currently supported. {% /aside %} {% /tabitem %} {% /tabs %} ## Nx Cloud notifications Controls which Nx Cloud notifications are displayed. {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} **Setting:** `nxConsole.nxCloudNotifications` **Options:** `"all"` | `"errors"` | `"none"` **Default:** `"all"` ```json { "nxConsole.nxCloudNotifications": "errors" } ``` **Options explained:** - `"all"`: Show all Nx Cloud notifications - `"errors"`: Only show error notifications - `"none"`: Disable all Nx Cloud notifications {% /tabitem %} {% tabitem label="JetBrains" %} **Location:** Settings > Tools > Nx Console > Nx Cloud Notifications **Options:** All | Errors Only | None **Default:** All Navigate to **Settings/Preferences > Tools > Nx Console** and select the desired notification level from the "Nx Cloud Notifications" dropdown. {% /tabitem %} {% /tabs %} ## Show node version on startup Shows a notification with the Node.js version information when the IDE starts. {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} **Setting:** `nxConsole.showNodeVersionOnStartup` **Default:** `false` ```json { "nxConsole.showNodeVersionOnStartup": true } ``` Useful for debugging version-related issues, especially when using nvm or similar Node version managers. {% /tabitem %} {% tabitem label="JetBrains" %} {% aside type="caution" title="Not Available" %} This setting is not available in JetBrains IDEs. Node version management is handled through the IDE's Node.js interpreter settings (**Settings > Languages & Frameworks > Node.js**). {% /aside %} {% /tabitem %} {% /tabs %} ## MCP port Specifies a fixed port for the Nx MCP (Model Context Protocol) server. {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} **Setting:** `nxConsole.mcpPort` **Default:** `undefined` (random port) ```json { "nxConsole.mcpPort": 3333 } ``` Set to `0` to explicitly use a random available port. If not specified, a random port is used by default. {% /tabitem %} {% tabitem label="JetBrains" %} {% aside type="caution" title="Not Available" %} This setting is not available in JetBrains IDEs. MCP port configuration is not currently supported. {% /aside %} {% /tabitem %} {% /tabs %} ## MCP tools filter Filters which MCP tools are enabled using glob patterns with negation support. {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} **Setting:** `nxConsole.mcpToolsFilter` **Default:** `[]` (all tools enabled) ```json { "nxConsole.mcpToolsFilter": ["nx_*", "!nx_cloud_*"] } ``` Use glob patterns to include or exclude specific MCP tools. Prefix with `!` to negate a pattern (exclude matching tools). {% /tabitem %} {% tabitem label="JetBrains" %} {% aside type="caution" title="Not Available" %} This setting is not available in JetBrains IDEs. MCP tools filtering is not currently supported. {% /aside %} {% /tabitem %} {% /tabs %} ## Enable debug logging Activates debug logging in output channels and the Language Server. {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} **Setting:** `nxConsole.enableDebugLogging` **Default:** `false` ```json { "nxConsole.enableDebugLogging": true } ``` Useful for troubleshooting Nx Console issues. When enabled, detailed debug information is written to the output channels. {% /tabitem %} {% tabitem label="JetBrains" %} {% aside type="caution" title="Not Available" %} This setting is not available in JetBrains IDEs. Debug logging configuration is handled through the IDE's native logging system. {% /aside %} {% /tabitem %} {% /tabs %} ## Disable file watching Disables automatic workspace refresh after file changes in the Language Server. {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} **Setting:** `nxConsole.disableFileWatching` **Default:** `false` ```json { "nxConsole.disableFileWatching": true } ``` When enabled, the workspace will not automatically refresh when files change. This can be useful for performance optimization in very large workspaces, but you will need to manually refresh the workspace to see changes. {% /tabitem %} {% tabitem label="JetBrains" %} {% aside type="caution" title="Not Available" %} This setting is not available in JetBrains IDEs. File watching behavior is managed by the IDE's native file system watcher. {% /aside %} {% /tabitem %} {% /tabs %} ## Common configuration scenarios ### Team configuration for specific framework If your team works primarily with React and Next.js: {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} Add to `.vscode/settings.json`: ```json { "nxConsole.generatorAllowlist": [ "@nx/react:*", "@nx/next:*", "@nx/workspace:library" ], "nxConsole.generatorBlocklist": [ "*:stories", "*:cypress-component-configuration" ], "nxConsole.projectViewingStyle": "tree", "nxConsole.enableTaskExecutionDryRunOnChange": true, "nxConsole.nxCloudNotifications": "errors" } ``` {% /tabitem %} {% tabitem label="JetBrains" %} Navigate to **Settings > Tools > Nx Console** and configure: **Generator Allowlist:** ```text @nx/react:* @nx/next:* @nx/workspace:library ``` **Generator Blocklist:** ```text *:stories *:cypress-component-configuration ``` **Nx Cloud Notifications:** Errors Only **Enable Dry Run on Change:** Checked {% /tabitem %} {% /tabs %} ### Nested workspace setup For monorepos where Nx is nested within a larger repository: {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} ```json { "nxConsole.nxWorkspacePath": "packages/my-workspace" } ``` {% /tabitem %} {% tabitem label="JetBrains" %} **Nx Workspace Path:** `packages/my-workspace` {% /tabitem %} {% /tabs %} ### Minimal noise configuration To reduce notifications and automatic behaviors: {% tabs syncKey="ide" %} {% tabitem label="VSCode" %} ```json { "nxConsole.nxCloudNotifications": "errors", "nxConsole.enableTaskExecutionDryRunOnChange": false, "nxConsole.enableCodeLens": false } ``` {% /tabitem %} {% tabitem label="JetBrains" %} **Nx Cloud Notifications:** Errors Only **Enable Dry Run on Change:** Unchecked {% /tabitem %} {% /tabs %} ## See also - [Nx Console Overview](/docs/guides/nx-console) - [Nx Console Generate Command](/docs/kb/console-generate-command) - [Nx Console Run Command](/docs/kb/console-run-command) - [Nx Console Troubleshooting](/docs/kb/nx-console-troubleshooting) --- ## Nx Daemon The Nx Daemon is a background process that watches workspace files and keeps project graph data in memory. Each workspace starts its own daemon process, so multiple workspaces can use different Nx versions without sharing daemon state. ## Why Nx uses a daemon Nx needs the project graph before it can run targets or calculate affected projects. Rebuilding the entire graph for every command becomes expensive as a workspace grows. The daemon watches for file changes and updates the graph incrementally. Because it already knows which files changed and retains graph state in memory, subsequent Nx commands can request an up-to-date graph without starting the analysis from scratch. {% aside type="note" title="Local communication" %} On macOS and Linux, Nx communicates with the daemon through a Unix socket. On Windows, it uses a named pipe. {% /aside %} The daemon shuts down after 3 hours without requests or file changes. Run `nx reset` to stop it manually and clear other Nx workspace state. ## Disable the daemon The daemon is enabled by default on local development machines. Disable it for a workspace by setting `useDaemonProcess` in `nx.json`: ```jsonc title="nx.json" { "useDaemonProcess": false, } ``` Set `NX_DAEMON=false` to disable it for one command or environment. The environment variable takes precedence over `nx.json`. ```shell NX_DAEMON=false nx show projects ``` Nx disables the daemon by default in CI and containers, where its persistent state or file watching isn't useful. Set `NX_DAEMON=true` to opt in when a supported long-running environment can reuse the process. In an AI agent sandbox the daemon runs, but the sandbox has to allow the unix sockets it creates. See [Configure agent sandboxes for Nx](/docs/kb/nx-sandbox-unix-sockets). Nx can't run the daemon in WebAssembly environments, so `NX_DAEMON=true` has no effect there. ## Inspect daemon logs Run `nx daemon` to print the daemon process ID and log file path. Open the file in your IDE or stream it from another terminal: ```shell nx daemon tail -f ``` ## Change the socket directory On macOS and Linux, Nx tries three locations for the daemon socket and for the forked process and plugin worker sockets, and uses the first one it can establish: `/tmp/.nx//sockets`, then `~/.nx/sockets`, then a directory inside the workspace. Beneath the first two, Nx creates a directory only your user can reach, named per run for the daemon and forked process sockets, and per workspace for the plugin worker sockets. The workspace location is used directly rather than getting a directory beneath it, so sockets there are per workspace whichever kind they are. For what each location requires, see [Fix Nx commands failing in an AI agent sandbox](/docs/kb/nx-sandbox-unix-sockets). Setting `NX_SOCKET_DIR` does not add a fourth entry to that list. It replaces it. The value becomes the socket directory itself rather than a root to create one under, and it gets no fallback through the other locations: if Nx cannot use it you get the workspace directory and a warning, and if it names a directory Nx refuses outright you get an error. The stable `/tmp/.nx` and `~/.nx` prefixes are what an agentic coding sandbox allowlists. The workspace location is the last resort and is different in kind: it sits outside those prefixes, and its length grows with the depth of your checkout, so it's the one most likely to exceed the socket path length limit. Nx warns once per command when it lands there. Windows named pipes are not files, so there is no shared directory to separate users in and no chain to walk. Nx places them directly in a per-run directory under `%TMP%`, which is already scoped to one account. Override the location like this: ```shell NX_SOCKET_DIR= nx daemon ``` This is primarily a workaround for file-permission issues with the default location, such as an environment that restricts access to it or a socket path that exceeds the OS length limit. Because the value is used as the socket directory itself, it has to name a directory only your user can reach. Nx rejects the system temp directory and its own container and cache roots. Name a directory beneath an owner-only root instead. `NX_SOCKET_DIR` takes precedence over the older `NX_DAEMON_SOCKET_DIR`. {% aside type="caution" title="Never share the socket directory" %} The socket is a remote for code execution: any process that reaches it can run code inside your long-lived daemon. Do not point `NX_SOCKET_DIR` (or `NX_DAEMON_SOCKET_DIR`) at a directory that other users, workspaces, or containers can access, such as a world-writable temp directory. Use a directory that only your user can reach. The daemon also rejects messages stamped with a different workspace root, which detects two workspaces accidentally sharing a socket directory. {% /aside %} ## Daemon behavior in containers Nx automatically disables the daemon in Docker containers and CI environments. The daemon's performance benefits come from maintaining state between commands and watching for file changes. In ephemeral environments where each command runs in a fresh container, this state cannot be reused, so the overhead of starting a background process provides no benefit. A persistent development container can benefit from the daemon, but check these constraints before enabling it: - Volume mounts can change file metadata in ways that affect file watching. - Container restarts invalidate the daemon socket. - Host and container permission differences can block socket access. Give each container its own daemon. Mounting one socket directory into several containers lets any of them run code in the others' daemons, as described in [Change the socket directory](#change-the-socket-directory). {% aside type="note" title="When to enable the daemon" %} Enable the daemon only in a container that persists across multiple Nx commands. Keep the default for short-lived or frequently restarted containers. {% /aside %} --- ## nx.json Reference The `nx.json` file configures the Nx CLI and project defaults. The full [machine readable schema](https://github.com/nrwl/nx/blob/master/packages/nx/schemas/nx-schema.json) is available on GitHub. The following is an expanded example showing all options. Your `nx.json` will likely be much shorter. ```json // nx.json { "plugins": [ { "plugin": "@nx/eslint/plugin", "options": { "targetName": "lint" } } ], "parallel": 4, "cacheDirectory": "tmp/my-nx-cache", "defaultBase": "main", "namedInputs": { "default": ["{projectRoot}/**/*"], "production": ["!{projectRoot}/**/*.spec.tsx"] }, "targetDefaults": { "@nx/js:tsc": { "inputs": ["production", "^production"], "dependsOn": ["^build"], "options": { "main": "{projectRoot}/src/index.ts" }, "cache": true }, "test": [ { "cache": true, "inputs": ["default", "^production"] }, { "filter": { "executor": "@nx/jest:jest" }, "inputs": ["...", "{workspaceRoot}/jest.preset.js"], "outputs": ["{workspaceRoot}/coverage/{projectRoot}"] } ] }, "release": { "version": { "conventionalCommits": true }, "changelog": { "git": { "commit": true, "tag": true }, "workspaceChangelog": { "createRelease": "github" }, "projectChangelogs": true } }, "sync": { "globalGenerators": ["my-plugin:my-sync-generator"] }, "migrate": { "createCommits": true }, "generators": { "@nx/js:library": { "buildable": true } }, "extends": "nx/presets/npm.json", "tui": { "enabled": true, "autoExit": true }, "conformance": { "rules": [ { "rule": "@nx/conformance/enforce-module-boundaries", "status": "evaluated" }, { "rule": "./tools/local-conformance-rules/check-project.ts", "projects": ["*"] } ] }, "analytics": true } ``` ## Plugins Nx plugins improve the experience of using different tools with Nx. One key feature of plugins is that they can [automatically configure the way Nx runs tasks](/docs/concepts/mental-model#inferred-tasks) for a tool based on that tool's configuration. In order for a plugin to configure tasks for Nx, it needs to be registered in the `plugins` array. If a plugin has no options, it can be listed as a string. Otherwise, it should be listed as an object with a `plugin` property and an `options` property. Every plugin behaves differently, so consult the plugin's own documentation for information about what it does. You can browse the [plugin registry](/docs/plugin-registry) for available plugins. To learn about creating your own plugin read about [extending Nx](/docs/kb/add-language-support). ```json // nx.json { "plugins": [ "@my-org/graph-plugin", { "plugin": "@nx/eslint/plugin", "options": { "targetName": "lint" } } ] } ``` ### Plugin order Plugins are processed in the order that they appear in the `plugins` array. If multiple plugins create a task with the same name, the plugin listed last wins. For example, if a project has both a `vite.config.ts` file and a `webpack.config.js` file, both the `@nx/vite` plugin and the `@nx/webpack` plugin will try to create a `build` task. The `build` task that is executed belongs to the plugin listed lower in the `plugins` array. When the two targets are compatible (they run the same executor and command), their configuration is merged property by property with the later plugin's values winning. When they are incompatible, the later target replaces the earlier one entirely. ### Scope plugins to specific projects Plugins use config files to infer tasks for projects. You can specify which config files are processed by Nx plugins using the `include` and `exclude` properties in the plugin configuration object. ```jsonc // nx.json { "plugins": [ { "plugin": "@nx/jest/plugin", "include": ["packages/**/*"], // include any projects in the packages folder "exclude": ["**/*-e2e/**/*"], // exclude any projects in a *-e2e folder }, ], } ``` The `include` and `exclude` properties are each file glob patterns that are used to include or exclude the configuration file that the plugin is interpreting. In the example provided, the `@nx/jest/plugin` plugin will only infer tasks for projects where the `jest.config.ts` file path matches the `packages/**/*` glob but does not match the `**/*-e2e/**/*` glob. #### Using negation patterns You can use negation patterns (patterns starting with `!`) to create more precise include/exclude rules. Patterns are processed in order, with later patterns overriding earlier ones. **Example: Excluding all e2e projects except one** ```jsonc // nx.json { "plugins": [ { "plugin": "@nx/jest/plugin", "exclude": ["**/*-e2e/**/*", "!**/toolkit-workspace-e2e/**/*"], }, ], } ``` This will exclude all e2e projects except `toolkit-workspace-e2e`. **Example: Including packages except legacy ones** ```jsonc // nx.json { "plugins": [ { "plugin": "@nx/vite/plugin", "include": ["packages/**/*", "!packages/legacy/**/*"], }, ], } ``` **How negation patterns work:** - Patterns are processed in order from first to last - A pattern starting with `!` removes files from the match set - A pattern without `!` adds files to the match set - The last matching pattern determines if a file is included - If the first pattern is a negation, all files are matched initially ## Plugins config `pluginsConfig` holds options for a plugin that apply workspace-wide, rather than to a single registered plugin entry. It is keyed by plugin package name. ```json // nx.json { "pluginsConfig": { "@nx/js": { "projectsAffectedByDependencyUpdates": "auto" } } } ``` ### projectsAffectedByDependencyUpdates Controls which projects Nx marks as affected when the package manager lock file changes. | Value | Description | | ---------- | ----------------------------------------------------------------------------------------------------------------------------- | | `"all"` | **(default)** Every project in the workspace is affected. Safest, and the most CI work. | | `"auto"` | Only projects whose resolved dependencies changed. Nx parses the lock file at both revisions and diffs the resolved packages. | | `string[]` | Project names or glob patterns, for example `["app-*", "shared-lib"]`. Only matching projects are affected. | In `"auto"` mode Nx supports `pnpm-lock.yaml`, `pnpm-lock.yml`, `package-lock.json`, `yarn.lock`, `bun.lock`, and `bun.lockb`. Binary Bun lock files are rendered by Bun before diffing, so Bun must be available wherever `nx affected` runs. For how the affected set is computed, see [run only tasks affected by a PR](/docs/features/ci-features/affected). ## Task options The following properties affect the way Nx runs tasks and can be set at the root of `nx.json`. | Property | Description | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | parallel | defines the max number of targets run in parallel | | cacheDirectory | defines where the local cache is stored (defaults to `~/.nx//cache`, shared by every checkout of the workspace) | | nxCloudEncryptionKey | (when connected to Nx Cloud only) defines an encryption key to support end-to-end encryption of your cloud cache. You may also provide an environment variable with the key `NX_CLOUD_ENCRYPTION_KEY` that contains an encryption key as its value. The Nx Cloud task runner normalizes the key length, so any length of key is acceptable | You can configure `parallel` in `nx.json`, but you can also set a `--parallel` flag in the terminal `nx run-many -t test --parallel=5`. To skip the cache for a single run, pass `--skip-nx-cache` (alias `--disableNxCache`) on the command line. There is no root `nx.json` property for it. ## Default base Tells Nx which base branch to use when calculating affected projects. - `defaultBase` defines the default base branch, defaults to `main`. ## Target defaults Target defaults provide a way to configure common options for targets across projects without duplicating the configuration. When Nx builds a project's configuration, it applies applicable defaults for each target as the base for that target's configuration. `targetDefaults` is a map keyed by target name (e.g. `test`), glob which matches target names (e.g. `e2e-ci--*`), or an executor (e.g. `@nx/js:tsc`). Each value is **either** an object (the common case, when no filtering is needed) **or** an ordered array of filtered entries (when a default should only apply to a subset of targets). When all targets matching an entry in the map should have the same defaults, use the plain object form: ```json // nx.json { "targetDefaults": { "build": { "cache": true } } } ``` When some targets matching an entry in the map should have different defaults than others also matching the entry in the map — for example, two plugins both infer `test` targets — use the array form. Each entry may have a `filter` property to scope its configuration to a subset of targets: ```json // nx.json — polyglot example { "targetDefaults": { "test": [ { "cache": true }, { "filter": { "plugin": "@nx/vitest" }, "inputs": ["default", "^production"] }, { "filter": { "projects": ["tag:dotnet"] }, "options": { "configuration": "Release" } } ] } } ``` | Field | Type | Description | | ----------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | key | `string` | The map key: a target name (e.g. `test`), a glob (e.g. `e2e-ci--*`), or an executor identifier (e.g. `@nx/js:tsc`). | | value | `object` \| `array` | A plain configuration object, or an ordered array of filtered entries. | | `filter.plugin` | `string` | Optional. Restricts the entry to targets inferred by a specific plugin (e.g. `@nx/vitest`). Useful when two plugins expose a target with the same name (e.g. `@nx/vitest:test` and `@nx/jest:test`). | | `filter.projects` | `string` \| `string[]` | Optional. Restricts the entry to matching projects. Accepts project names, globs, directory patterns, tags (`tag:foo`), and negation (`!foo`) — anything `findMatchingProjects` understands. | | `filter.executor` | `string` | Optional. Restricts the entry to targets that resolve to this executor. | | other | any field from [Target Configuration](/docs/reference/project-configuration) | The defaults applied when the entry matches. | Within a key's array, entries apply in document order. **Later entries override prior entries**. An entry with **no `filter`** is a catch-all baseline that applies to every variant of that target; an entry **with a `filter`** applies only where its `plugin`, `projects`, and `executor` match. Because a later entry overrides an earlier one, a trailing catch-all can reset a value set above it. Target defaults apply after targets have been inferred by plugins but before project configuration in `project.json` or `package.json`. {% aside type="caution" title="Beware" %} When a key's value is a plain object (or an array entry has no `filter`), Nx matches by the key alone. Make sure all defaults make sense to all targets that match the key. Anything set in a target default will also override the configuration of [tasks inferred by plugins](/docs/concepts/mental-model#inferred-tasks). {% /aside %} Some common scenarios for this follow. ### inputs & namedInputs Named inputs defined in `nx.json` are merged with the named inputs defined in [project level configuration](/docs/reference/project-configuration). In other words, every project has a set of named inputs, and it's defined as: `{...namedInputsFromNxJson, ...namedInputsFromProjectsProjectJson}`. Defining `inputs` for a given target would replace the set of inputs for that target name defined in `nx.json`. Using pseudocode `inputs = projectJson.targets.build.inputs || nxJsonTargetDefaults.build.inputs`. To merge with the base inputs instead of replacing them, use the [spread token](#spread-token). You can also define and redefine named inputs. This enables one key use case, where your `nx.json` can define things like this (which applies to every project): ```json // nx.json { "targetDefaults": { "test": { "inputs": ["default", "^production"] } } } ``` And projects can define their `production` inputs, without having to redefine the inputs for the `test` target. ```json // project.json { "namedInputs": { "production": ["default", "!{projectRoot}/**/*.test.js"] } } ``` In this case Nx will use the right `production` input for each project. {% cardgrid %} {% linkcard title="Inputs and Named Inputs Reference" description="Learn about all the possible settings for `inputs` and `namedInputs`" href="/docs/reference/inputs" /%} {% linkcard title="Configure Inputs for Task Caching" description="This recipes walks you through a few examples of how to configure `inputs` and `namedInputs`" href="/docs/kb/configure-inputs" /%} {% /cardgrid %} ### Task pipelines Targets can depend on other targets. A common scenario is having to build dependencies of a project first before building the project. The `dependsOn` property in `project.json` can be used to define the list of dependencies of an individual target. Often the same `dependsOn` configuration has to be defined for every project in the repo, and that's when defining `targetDefaults` in `nx.json` is helpful. ```json // nx.json { "targetDefaults": { "build": { "dependsOn": ["^build"] } } } ``` The configuration above is identical to adding `{"dependsOn": ["^build"]}` to every `build` target of every project. {% cardgrid %} {% linkcard title="Project Configuration reference" description="For full documentation of the `dependsOn` property, see the project configuration reference" href="/docs/reference/project-configuration#dependson" /%} {% linkcard title="What is a Task Pipeline" description="This guide describes how to think about task pipelines" href="/docs/concepts/task-pipeline-configuration" /%} {% /cardgrid %} ### Outputs Another target default you can configure is `outputs`: ```json // nx.json { "targetDefaults": { "build": { "outputs": ["{projectRoot}/custom-dist"] } } } ``` When defining any options or configurations inside of a target default, you may use the `{workspaceRoot}` and `{projectRoot}` tokens. This is useful for defining options whose values are paths. ```json // nx.json { "targetDefaults": { "@nx/js:tsc": { "options": { "main": "{projectRoot}/src/index.ts" }, "configurations": { "prod": { "tsconfig": "{projectRoot}/tsconfig.prod.json" } }, "inputs": ["prod"], "outputs": ["{workspaceRoot}/{projectRoot}"] }, "build": { "inputs": ["prod"], "outputs": ["{workspaceRoot}/{projectRoot}"], "cache": true } } } ``` #### Target default priority Both keys above match a `build` target that uses `@nx/js:tsc`. However, only the most specific defaults, the executor-keyed defaults, will apply. The name-keyed defaults will not apply so specify the full config on the executor key. `cache` is the one exception. When an executor key shadows a name key that sets `"cache": true`, Nx reads that value back so the target stays cacheable, and warns that relying on it is deprecated. Nx skips continuous targets and long-running ones such as `serve`, `dev` and `start`, because caching them is invalid. Set `cache` on the executor key instead. An executor key applies to every target that resolves through it, so exclude any continuous target first. Nx rejects a target that is both cacheable and continuous. {% cardgrid %} {% linkcard title="Configure Outputs for Task Caching" description="This recipe walks you through how to set outputs" href="/docs/kb/configure-outputs" /%} {% /cardgrid %} ### Cache Caching is configured by specifying `"cache": true` in a target's configuration. This will tell Nx that it's ok to cache the results of a given target. For instance, if you have a target that runs tests, you can specify `"cache": true` in the target default configuration for `test` and Nx will cache the results of running tests. ```json // nx.json { "targetDefaults": { "test": { "cache": true } } } ``` {% aside type="caution" title="Per Project Caching + Distribution" %} If you are using distributed task execution and disable caching for a given target, you will not be able to use distributed task execution for that target. This is because distributed task execution requires caching to be enabled. This means that the target you have disabled caching for, and any targets which depend on that target will fail the pipeline if you try to run them with Nx Agents enabled. {% /aside %} ### Executor/command options You can configure options specific to a target's executor. As an example, if your repo has projects using the `@nx/js:tsc` executor, you can provide some default options as follows: ```json // nx.json { "targetDefaults": { "@nx/js:tsc": { "options": { "generateExportsField": true } } } } ``` You can also provide defaults for [inferred targets](/docs/concepts/mental-model#inferred-tasks) or targets running a command using the `nx:run-commands` executor. As an example, if your repo has projects where **all the `build` targets** run the same `vite build` command, you can provide some default options as follows: ```json // nx.json { "targetDefaults": { "build": { "options": { "assetsInlineLimit": 2048, "assetsDir": "static/assets" } } } } ``` If two plugins expose a target with the same name (e.g. both `@nx/vitest` and `@nx/jest` infer `test`), use the `plugin` filter to scope per-plugin defaults: ```json // nx.json { "targetDefaults": { "test": [ { "filter": { "plugin": "@nx/vitest" }, "inputs": ["default", "^production"] }, { "filter": { "plugin": "@nx/jest/plugin" }, "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"] } ] } } ``` Or use the `projects` filter — which accepts the same patterns as the `--projects` CLI flag (names, globs, `tag:foo`, `!exclude`) — to scope a default to targets on a subset of projects: ```json // nx.json { "targetDefaults": { "build": [ { "filter": { "projects": ["tag:dotnet"] }, "options": { "configuration": "Release" } }, { "filter": { "projects": ["apps/*", "!apps/legacy"] }, "cache": true } ] } } ``` {% aside type="caution" title="Be careful" %} If multiple targets with the same name run different commands (or use different executors), do not set options in `targetDefaults`. Different commands would accept different options, and the target defaults will apply to all targets with the same name regardless of the command they run. If you were to provide options in `targetDefaults` for them, the commands that don't expect those options could throw an error. {% /aside %} For more details on how to pass args to the underlying command see the [Pass Args to Commands recipe](/docs/kb/pass-args-to-commands). ### Task atomizer configuration Task Atomizer plugins create several targets with a similar pattern. For example, the `@nx/cypress` plugin creates a top level `e2e-ci` target and a target for each test file that looks like `e2e-ci--test/my/test.spec.ts`. To avoid having to write a target default for each of these targets, you can use a glob pattern in the target default key. ```json // nx.json { "targetDefaults": { "e2e-ci--**/**": { "options": { "headless": true } } } } ``` {% aside type="note" title="Pattern Matching" %} Nx uses glob patterns to match against the target name. This means that the `**/**` pattern above is required because tests can be nested within a directory which would make the target name contain a `/`. If your target name does not contain a `/`, you can use a simpler pattern like `e2e-ci-*`. {% /aside %} ### Spread token By default, a target default replaces the corresponding value from a matching [inferred task](/docs/concepts/mental-model#inferred-tasks) (and from any lower-priority configuration source) rather than merging with it. The spread token (`"..."`) lets a target default contribute to the base value instead of overwriting it. **In arrays**, `"..."` is substituted with the items from the base array at that position. For example, to add an input to every `build` target while keeping the inputs its plugin already infers: ```json // nx.json { "targetDefaults": { "build": { "inputs": ["...", "{workspaceRoot}/babel.config.json"] } } } ``` If the inferred `build` target has `inputs: ["default", "^production"]`, the result is `["default", "^production", "{workspaceRoot}/babel.config.json"]`. **In objects**, a key of `"..."` set to `true` spreads base properties at that position, which is useful for adding an option without discarding the ones a plugin already set. Keys defined after `"..."` override base values, and keys defined before `"..."` can be overridden by base values. ```json // nx.json { "targetDefaults": { "build": { "options": { "...": true, "assetsDir": "static/assets" } } } } ``` The spread token behaves the same way in `nx.json` target defaults as it does in `project.json`. For the full list of levels where it applies (target root, `options`, `configurations`, and nested values) and important cautions, see the [Spread token reference](/docs/reference/project-configuration#spread-token). ## Release The `release` property in `nx.json` configures the `nx release` command. It is an optional property, as `nx release` is capable of working with zero config, but when present it is used to configure the versioning, changelog, and publishing phases of the release process. For more information on how `nx release` works, see [manage releases](/docs/features/manage-releases). The full list of configuration options available for `"release"` can be found here: [https://github.com/nrwl/nx/blob/master/packages/nx/src/config/nx-json.ts](https://github.com/nrwl/nx/blob/master/packages/nx/src/config/nx-json.ts) under `NxReleaseConfiguration`. ### Projects If you want to limit the projects that `nx release` targets, you can use the `projects` property in `nx.json` to do so. This property is either a string, or an array of strings. The strings can be project names, glob patterns, directories, tag references or anything else that is supported by the `--projects` filter you may know from other commands such as `nx run`. ```jsonc // nx.json { "release": { // Here we are configuring nx release to target all projects // except the one called "ignore-me" "projects": ["*", "!ignore-me"], }, } ``` ### Projects relationship The `projectsRelationship` property tells Nx whether to release projects independently or together. By default Nx will release all your projects together in lock step, which is an equivalent of `"projectRelationships": "fixed"`. If you want to release projects independently, you can set `"projectsRelationship": "independent"`. ```jsonc // nx.json { "release": { // Here we are configuring nx release to release projects // independently, as opposed to the default of "fixed" "projectsRelationship": "independent", }, } ``` ### Release tag The `releaseTag` property controls how git tags are parsed and generated. By default tags conform to semantic version and are validated as such. {% aside type="note" title="Nx 23 Changes" %} The legacy flat properties (`releaseTagPattern`, `releaseTagPatternRequireSemver`, `releaseTagPatternStrictPreid`, `releaseTagPatternPreferDockerVersion`, `releaseTagPatternCheckAllBranchesWhen`) were removed in Nx 23. Use the nested `releaseTag` object below. An automated migration in Nx 22 moved configuration to the new shape. {% /aside %} #### Configuration options | Property | Type | Default | Description | | ------------------------ | ------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **pattern** | string | `v{version}` for fixed, `{projectName}@{version}` for independent | The git tag pattern to use. Supports interpolation of `{version}`, `{projectName}`, and `{releaseGroupName}` | | **requireSemver** | boolean | `true` | Whether to require that all tags match semantic versioning | | **strictPreid** | boolean | `true` | Whether to ensure prerelease IDs are consistent across packages | | **preferDockerVersion** | boolean | `false` | Whether to prefer Docker-compatible version format in git tags | | **checkAllBranchesWhen** | boolean \| string[] | undefined | Controls whether to check all branches or only merged branches when resolving current versions from git tags. `true` = always check all branches, `false` = only check the current branch, `string[]` = check all branches when the current branch matches any of the provided names or glob patterns | #### Branch resolution for git tags The `checkAllBranchesWhen` option controls how Nx resolves existing git tags to determine the current version of your projects. By default, Nx checks for matching tags on the current branch. If no tags are found, it falls back to checking all branches. | Value | Behavior | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `true` | Always check all branches for the latest matching tag | | `false` | Only check the current branch (no fallback to all branches) | | `string[]` | Check all branches when the current branch matches any of the provided names or [glob patterns](https://github.com/isaacs/minimatch). Otherwise, use the default behavior | This option is useful when release tags may exist on multiple branches. Setting `checkAllBranchesWhen` to `true` or to a list of branch patterns ensures Nx finds the latest tag regardless of which branch it was created on. #### Tag pattern syntax The tag pattern supports interpolating the following values: - `{version}` - The version currently being released - `{projectName}` - The name of the project being released (for independent releases) - `{releaseGroupName}` - The name of the release group being released (when using release groups) Example patterns and their results: - `v{version}` → `v1.0.0` (default for fixed releases) - `{projectName}@{version}` → `my-lib@1.0.0` (default for independent releases) - `{releaseGroupName}/{projectName}/{version}` → `backend/api/1.0.0` - `release/{version}` → `release/1.0.0` #### Example configuration ```jsonc // nx.json { "release": { "releaseTag": { "pattern": "{releaseGroupName}-v{version}", "requireSemver": true, "strictPreid": true, "preferDockerVersion": false, "checkAllBranchesWhen": ["main", "release/*"], }, }, } ``` ### Version The `version` property configures the versioning phase of the release process. It is used to determine the next version of your projects, and update any projects that depend on them to use the new version. {% aside type="note" title="Breaking Changes in Nx v22" %} In Nx v22, the legacy versioning implementation was removed entirely. This had been deprecated since Nx 21. An automated migration was provided in Nx v21 to update your configuration to the new format when running `nx migrate`. For Nx 22, this automated migration was re-added to allow updating your configuration by running `nx migrate`. {% /aside %} Behind the scenes, ecosystem-specific `version` logic is powered by a `VersionActions` implementation. Out of the box Nx wires up the most widely applicable `VersionActions` implementation for you, which is `@nx/js/src/release/version-actions` provided by the `@nx/js` plugin. {% aside type="note" title="Programmatic API (Nx 22+)" %} For advanced use cases, the `ReleaseClient` programmatic API allows you to trigger releases from custom scripts. In Nx 22+, you can optionally ignore the `nx.json` release configuration entirely when using the API, which is useful for ad-hoc scripts that only work with a subset of the repository. {% /aside %} Core version options are therefore directly at the top level of the `version` property (as of Nx v21), while ecosystem-specific options are available through `versionActionsOptions`: ```jsonc // nx.json { "release": { "version": { // Core options "conventionalCommits": true, "manifestRootsToUpdate": ["dist/packages/{projectName}"], // Ecosystem-specific options "versionActionsOptions": { "skipLockFileUpdate": true, }, }, }, } ``` Some important changes in Nx 22: - `preserveMatchingDependencyRanges` now defaults to `true` (previously `false`) - When `true`, dependency version ranges are preserved if they already satisfy the new version being released - Example: If releasing version `1.2.0` and a dependent has range `^1.0.0`, it remains `^1.0.0` instead of being updated to `1.2.0` - Set to `false` explicitly if you need exact versions in all dependents - `updateDependents` now defaults to `"always"` (previously `"auto"`) - `"always"`: Update dependents wherever they exist in the graph, regardless of filter selection - `"auto"`: Update dependents only if they're within the selected projects or release groups - `"never"`: Never update dependents - `releaseTag.strictPreid` now defaults to `true` - Fixed Release Group Release Tag Pattern now defaults to `{releaseGroupName}-v{version}` Some important changes in Nx 23: - `version.adjustSemverBumpsForZeroMajorVersion` now defaults to `true`. For 0.x versions, breaking changes bump the minor and new features bump the patch (instead of major/minor respectively). Set to `false` to preserve the prior behavior of treating all bumps the same regardless of major version. - The deprecated `releaseTag*` flat properties (`releaseTagPattern`, `releaseTagPatternRequireSemver`, `releaseTagPatternStrictPreid`, `releaseTagPatternPreferDockerVersion`, `releaseTagPatternCheckAllBranchesWhen`) were removed in favor of the nested `releaseTag` object introduced in Nx 22. #### Propagating `--preid` to dependent bumps When versioning with `--preid`, the implicit patch bumps given to dependents (and to other projects in a fixed release group) are stable patches by default. That means `project-b` being bumped to `2.0.1-rc.0` still bumps its dependent `project-a` to `1.0.1`, not `1.0.1-rc.0`. Set `version.applyPreidToDependents` to `true` if you want the `--preid` value to flow through to those side-effectful bumps instead. It can also be set per release group under `release.groups..version.applyPreidToDependents`. See [Update Dependents](/docs/guides/nx-release/update-dependents#propagating-preid-to-dependent-bumps) for the full walkthrough and guidance on when to enable it. ### Changelog The `changelog` property configures the changelog phase of the release process. It is used to generate a changelog for your projects, and commit it to your repository. There are two types of possible changelog that can be generated: - **Workspace Changelog**: A changelog that contains all changes across all projects in your workspace. This is not applicable when releasing projects independently. - **Project Changelogs**: A changelog that contains all changes for a given project. The `changelog` property is used to configure both of these changelogs. #### Workspace changelog The `changelog.workspaceChangelog` property configures the workspace changelog. It is used to determine if and how the workspace changelog is generated. ```jsonc // nx.json { "release": { "changelog": { // This disables the workspace changelog "workspaceChangelog": false, }, }, } ``` ```jsonc // nx.json { "release": { "changelog": { "workspaceChangelog": { // This will create a GitHub release containing the workspace // changelog contents "createRelease": "github", // This will disable creating a workspace CHANGELOG.md file "file": false, }, }, }, } ``` #### Project changelogs The `changelog.projectChangelogs` property configures the project changelogs. It is used to determine if and how the project changelogs are generated. ```jsonc // nx.json { "release": { "changelog": { // This enables project changelogs with the default options "projectChangelogs": true, }, }, } ``` ```jsonc // nx.json { "release": { "changelog": { "projectChangelogs": { // This will create one GitHub release per project containing // the project changelog contents "createRelease": "github", // This will disable creating any project level CHANGELOG.md // files "file": false, }, }, }, } ``` #### Replace existing contents {% badge text="Nx 22+" /%} By default, changelog entries are prepended to existing changelog files. To completely replace the entire changelog file contents instead, use the `replaceExistingContents` option: ```jsonc // nx.json { "release": { "changelog": { "workspaceChangelog": { // Replaces the entire CHANGELOG.md file with the new content "replaceExistingContents": true, }, }, }, } ``` This option is available for both `workspaceChangelog` and `projectChangelogs`. It's useful when you want to regenerate the entire changelog from scratch rather than maintaining a cumulative history. ### Git The `git` property configures the automated git operations that take place as part of the release process. ```jsonc // nx.json { "release": { "git": { // This will enable committing any changes (e.g. package.json // updates, CHANGELOG.md files) to git "commit": true, // This will enable create a git for the overall release, or // one tag per project for independent project releases "tag": false, }, }, } ``` ### Docker {% badge text="experimental" /%} {% aside type="caution" title="Experimental Feature" %} Docker support in Nx is currently experimental and may undergo breaking changes without following semantic versioning. {% /aside %} The `docker` property configures Docker image versioning and publishing when using `nx release`. This enables calendar-based versioning schemes and automated Docker registry publishing as part of your release workflow. ```jsonc // nx.json { "release": { // Simple configuration - enables Docker with defaults "docker": true, }, } ``` ```jsonc // nx.json { "release": { "docker": { // Run this command before versioning Docker images "preVersionCommand": "npx nx run-many -t docker:build", // Define versioning schemes for different environments "versionSchemes": { "production": "{currentDate|YYMM.DD}.{shortCommitSha}", "hotfix": "{currentDate|YYMM.DD}.{shortCommitSha}-hotfix", "staging": "{currentDate|YYMM.DD}-staging", "development": "{currentDate|YYMM.DD}-dev-{shortCommitSha}", }, // Skip Docker versioning for these projects to prevent versioning with other tools like NPM // Only set this if you are not using {versionActionsVersion} in your docker version scheme "skipVersionActions": ["api"], // Default Docker repository name (can be overridden per project) "repositoryName": "myorg", // Docker registry URL "registryUrl": "docker.io", }, }, } ``` Read more about it in the [Release Docker Images guide](/docs/kb/release-docker-images). #### Docker plugin options When configuring the `@nx/docker` plugin in `nx.json`, you can customize the behavior of Docker build and run targets. One important option is `skipDefaultTag`: ```jsonc // nx.json { "plugins": [ { "plugin": "@nx/docker/plugin", "options": { "buildTarget": { "name": "docker:build", // Skip the default --tag argument "skipDefaultTag": true, // You must provide your own tags via args "args": ["--tag myorg/myapp:latest", "--tag myorg/myapp:1.0.0"], }, }, }, ], } ``` **Important:** Setting `skipDefaultTag: true` opts out of Nx Release support for that project. When you explicitly manage your own Docker tags via the `args` property, the automatic versioning and publishing features of `nx release` will not work for those Docker projects. Only use `skipDefaultTag: true` if you are not using `nx release` to manage your Docker image versions. If you want to use `nx release` with Docker projects, leave `skipDefaultTag` as `false` (the default) or omit it entirely. #### Version scheme syntax Docker version schemes support calendar-based patterns using the following placeholders: - `{projectName}` - The name of the project being released - `{currentDate}` - Current date in ISO format (e.g., 2025-01-30T14:30:00Z) - `{currentDate|FORMAT}` - Current date with custom format using these tokens: - `YYYY` - 4-digit year (e.g., 2025) - `YY` - 2-digit year (e.g., 25) - `MM` - 2-digit month (01-12) - `DD` - 2-digit day (01-31) - `HH` - 2-digit hour in 24-hour format (00-23) - `mm` - 2-digit minutes (00-59) - `ss` - 2-digit seconds (00-59) - `{shortCommitSha}` - First 7 characters of the current commit SHA - `{commitSha}` - Full commit SHA - `{versionActionsVersion}` - The version generated during the version actions such as "1.2.3" Example patterns: - `{currentDate|YYMM.DD}.{shortCommitSha}` - Results in: 2501.30.a1b2c3d - `{projectName}-{currentDate|YYYY.MM.DD}` - Results in: api-2025.01.30 - `{currentDate|YY.MM.DD.HHmm}-{commitSha}` - Results in: 25.01.30.1430-abcdef1234567890 - `{projectName}-{versionActionsVersion}` - Results in: api-1.2.3 #### Group-level Docker configuration You can configure [Docker release](/docs/kb/release-docker-images) settings at the release group level: ```jsonc // nx.json { "release": { "groups": { "backend": { "projects": ["api"], "projectsRelationship": "independent", "docker": { // Only set this to true if you are not using {versionActionsVersion} in your docker version scheme "skipVersionActions": true // Run a script before tagging Docker version "groupPreVersionCommand": "echo Preparing backend release", }, "changelog": { "projectChangelogs": true } } } } } ``` This allows you to have other releases such as [NPM](/docs/kb/release-npm-packages) or [Rust crates](/docs/kb/publish-rust-crates) in the same workspace. ## Sync These are global configuration options for the [`nx sync`](/docs/reference/nx-commands#nx-sync) command. The `nx sync` command runs all global and task-specific [sync generators](/docs/concepts/sync-generators) to ensure that your files are in the correct state to run tasks or start the CI process. ```jsonc // nx.json { "sync": { "applyChanges": true, "globalGenerators": ["my-plugin:my-sync-generator"], "generatorOptions": { "my-plugin:my-sync-generator": { "verbose": true, }, }, "disabledTaskSyncGenerators": ["other-plugin:problematic-generator"], }, } ``` | Property | Description | | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **applyChanges** | Whether to automatically apply task sync generator changes when running tasks. If not set, the user will be prompted. If set to `true`, the user will not be prompted and the changes will be applied. If set to `false`, the user will not be prompted and the changes will not be applied. | | **globalGenerators** | Sync generators that are only run when the `nx sync` command is executed. These are not associated with a particular task. | | **generatorOptions** | Options to be passed to sync generators | | **disabledTaskSyncGenerators** | Globally disable specific task sync generators | ## Migrate These are workspace-wide defaults for the [`nx migrate`](/docs/reference/nx-commands#nx-migrate) command. Most options mirror an `nx migrate` flag, so you can set a value once instead of passing the flag on every run. A flag passed on the command line always takes precedence over the value in `nx.json`. `include`, `multiMajorMode`, and `useRegistryResolution` apply when generating migrations (`nx migrate `); the rest apply when running them (`nx migrate --run-migrations`). ```jsonc // nx.json { "migrate": { "createCommits": true, "commitPrefix": "chore(repo): apply nx migration ", "multiMajorMode": "direct", "agentic": "claude-code", }, } ``` | Property | Description | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **createCommits** | Whether to automatically create a git commit after each migration runs. Equivalent to the `--create-commits` flag. Defaults to `false`. | | **commitPrefix** | Commit message prefix applied to each migration commit when commits are enabled. Equivalent to the `--commit-prefix` flag. Defaults to `"chore: [nx migration] "`. | | **include** | Restricts which packages to migrate, for target packages that support optional updates: `required` (the target package and the related packages it ships with), `optional` (the optional dependency updates those packages recommend), or `all`. Equivalent to the `--include` flag. Defaults to `all`. | | **multiMajorMode** | How to handle a migration that crosses more than one major version: `direct` (migrate straight to the target) or `gradual` (migrate to the smallest recommended step). Equivalent to the `--multi-major-mode` flag. The `NX_MULTI_MAJOR_MODE` environment variable takes precedence over this value. | | **useRegistryResolution** | Whether `nx migrate` resolves package versions via the npm registry (faster) instead of a package-manager install. Set to `false` to always resolve versions through your package manager. The `NX_MIGRATE_USE_REGISTRY_RESOLUTION` and the deprecated `NX_MIGRATE_SKIP_REGISTRY_FETCH` (removed in Nx 24) environment variables take precedence over this. Defaults to `true`. | | **agentic** | Default for the agentic flow used by `nx migrate --run-migrations`: `false` (never use it), `true` (use it and resolve the installed agent), or an agent id (`"claude-code"`, `"codex"`, `"opencode"`) to always use that agent. Equivalent to the `--agentic` flag. | | **validate** | Whether to run agent-driven validation after generator-only migrations when the agentic flow is enabled. Equivalent to the `--validate` flag. Defaults to `true` when the agentic flow is enabled. | ## Generators Default generator options can be configured in `nx.json`. For instance, the following tells Nx to always pass `--buildable=true` when creating new libraries with the `@nx/js` plugin. ```json // nx.json { "generators": { "@nx/js:library": { "buildable": true } } } ``` ## Extends Some presets use the `extends` property to hide some default options in a separate json file. The json file specified in the `extends` property is located in your `node_modules` folder. The Nx preset files are specified in [the `nx` package](https://github.com/nrwl/nx/tree/master/packages/nx/presets). ## Nx Cloud There are also options for [Nx Cloud](https://cloud.nx.app?utm_source=nx-dev&utm_medium=website&utm_campaign=nx-json) that are set in the `nx.json` file. For instance, you connect to the Nx Cloud service using an `nxCloudId` like this: ```json // nx.json { "nxCloudId": "SOMEID" } ``` If connecting to a different Nx Cloud instance, you can define the `nxCloudUrl`: > The default is `https://cloud.nx.app` ```json // nx.json "nxCloudUrl": "https://nx.custom-company-domain.net" ``` For more details on configuring Nx Cloud, see the [Nx Cloud Configuration Options page](/docs/reference/nx-cloud-cli). ## Max cache size The `maxCacheSize` property in `nx.json` allows you to set a limit on the size of the local cache. If it is not set, Nx defaults to a maximum size of 10% of the size of the disk where the cache is stored, up to a maximum of 10GB. This means that if your disk is 100GB, the maximum cache size will be 10GB. If the cache exceeds the specified size, Nx removes the least recently used cache entries until the total size is below 90% of the specified limit. You can also override this value using the `NX_MAX_CACHE_SIZE` environment variable, which accepts the same units and takes precedence over the `maxCacheSize` option in `nx.json`. This behavior can be opted out by setting `maxCacheSize` to `0`. Valid values for `maxCacheSize` can be specified in bytes, kilobytes (KB), megabytes (MB), or gigabytes (GB). For example, any of the following would be valid values: | Value | Description | | -------- | ---------------------------------------------------------------- | | `819200` | 819200 bytes (800 KB) | | `100MB` | 100 megabytes (100 \* 1024 \* 1024 bytes) | | `1GB` | 1 gigabyte (1024 \* 1024 \* 1024 bytes) | | `0` | No limit on the local cache size (disables the cache size limit) | ```json // nx.json { "maxCacheSize": "0" // No limit on the local cache size } ``` ```json // nx.json { "maxCacheSize": "10GB" // Set the maximum cache size to 10 gigabytes } ``` Regardless of the `maxCacheSize` setting, Nx will remove cache entries that have not been accessed in the last 7 days. ## TUI The `tui` property in `nx.json` configures the [Terminal UI](/docs/kb/terminal-ui). It allows you to enable or disable the TUI and configure its behavior. ```json // nx.json { "tui": { // Enable the Nx TUI "enabled": true, // Automatically exit the TUI when completed // Use a number to specify the seconds to keep the TUI open for after completion "autoExit": true } } ``` ## Conformance The `conformance` property in `nx.json` configures the usage of [Nx Conformance](/docs/enterprise/conformance). ```jsonc // nx.json { "conformance": { "outputPath": "dist/conformance-result.json", "rules": [ { "rule": "@nx/conformance/enforce-module-boundaries", "status": "evaluated", "explanation": "This rule ensures proper module boundaries are maintained across the workspace", }, { "rule": "./tools/local-conformance-rules/check-project.ts", "projects": ["*"], "options": { "strictMode": true, }, }, { "rule": "nx-cloud://my-custom-rule", "projects": [ "frontend-*", { "matcher": "backend-legacy", "explanation": "Legacy backend project is exempt until migration is complete", }, ], "status": "enforced", }, ], }, } ``` ### Configuration options | Property | Type | Description | | -------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | **outputPath** | `string \| false` | Optional path to write the conformance results to. Defaults to `"dist/conformance-result.json"`. Set to `false` to disable writing results to a file. | | **rules** | `Array` | List of conformance rules to apply to your workspace. | ### Rule configuration Each rule in the `rules` array can have the following properties: | Property | Type | Required | Description | | --------------- | ----------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **rule** | `string` | Yes | Relative path to a local rule implementation, node_module path, or nx-cloud rule ID for Nx Cloud Enterprise (e.g., `"nx-cloud://my-rule"`). | | **options** | `Record` | No | Rule-specific configuration options. The available options depend on the specific rule being used. | | **projects** | `Array` | No | Projects to apply the rule to. See [Projects](#projects-conformance) section below. | | **explanation** | `string` | No | Optional explanation to communicate to colleagues why this rule is enabled and important to the workspace. | | **status** | `"enforced" \| "evaluated" \| "disabled"` | No | Override the default status of the rule (defaults to `"enforced"`). If set to `"evaluated"`, violations are reported but don't cause failures. If set to `"disabled"`, the rule is not evaluated at all. | ### Projects (Conformance) If you want to limit the projects that a conformance rule applies to, you can use the `projects` property. This property accepts an array of strings which can be project names, glob patterns, directories, tag references or anything else that is supported by the `--projects` filter you may know from other commands such as `nx run`. By default, the implied value is `["*"]` (all projects). You can also use objects with `matcher` and `explanation` properties to document why specific projects are included or excluded from the rule. ```jsonc { "rule": "my-rule", // Here we are configuring the rule to apply to all frontend projects // except the one called "frontend-legacy" "projects": ["frontend-*", "!frontend-legacy"], } ``` ```jsonc { "rule": "my-rule", "projects": [ "frontend-*", { "matcher": "!frontend-legacy", "explanation": "Exempt until migration to new architecture is complete", }, ], } ``` ### Rule status values - **enforced** (default): Rule violations will cause the conformance check to fail (exit code 1) - **evaluated**: Rule violations are reported but don't cause failures (exit code 0) - **disabled**: Rule is not evaluated at all ## Analytics The `analytics` property controls whether Nx collects usage telemetry. When not set, Nx prompts you on first run in an interactive terminal. ```json // nx.json { "analytics": true } ``` For more information, see [telemetry](/docs/reference/telemetry). --- ## Nx MCP Server Reference The Nx MCP server is a [Model Context Protocol](https://modelcontextprotocol.io/introduction) implementation that connects AI agents to systems they can't easily reach on their own: Nx Cloud CI pipelines, self-healing fixes, task monitoring, and up-to-date Nx documentation. {% aside type="tip" title="Quick Setup" %} To configure your Nx workspace for AI agents (MCP, skills, and more), run `npx nx configure-ai-agents`. See the [AI Integration guide](/docs/getting-started/ai-setup) for details. {% /aside %} {% aside type="note" title="MCP + Skills" %} The Nx MCP server works best alongside [agent skills](/docs/features/enhance-ai). Skills handle domain knowledge — workspace exploration, code generation, task execution — while the MCP server provides connectivity to Nx Cloud and task monitoring. For background on this approach, see our blog post on [why we deleted most of our MCP tools](/blog/why-we-deleted-most-of-our-mcp-tools). {% /aside %} For an overview of how Nx gives coding agents workspace context, see [enhance your AI coding agent](/docs/features/enhance-ai). ## Installation There are a few ways to setup the Nx MCP server. ### via .mcp.json {% tabs syncKey="nx-version" %} {% tabitem label="Nx >= 21.4" %} ```json // .mcp.json { "servers": { "nx-mcp": { "type": "stdio", "command": "npx", "args": ["nx", "mcp"] } } } ``` {% /tabitem %} {% tabitem label="Nx < 21.4" %} ```json // .mcp.json { "servers": { "nx-mcp": { "type": "stdio", "command": "npx", "args": ["nx-mcp@latest"] } } } ``` {% /tabitem %} {% /tabs %} ### Via Nx Console extension If you're using Cursor or VS Code, install the [Nx Console extension](/docs/getting-started/editor-setup) which automatically manages the MCP server for you. ## Client-specific setup > If your preferred client is not listed, configure your client to run `npx nx mcp` (Nx >= 21.4) or `npx nx-mcp@latest` (older versions) to start the Nx MCP server. ### Claude Code {% aside type="tip" title="Using an agent sandbox?" %} Claude Code, Codex, and the GitHub Copilot CLI all sandbox the commands they run, and each blocks Unix sockets until you allow them. See [Nx fails in an AI agent sandbox](/docs/kb/nx-sandbox-unix-sockets) for the per-agent setting. {% /aside %} {% tabs syncKey="nx-version" %} {% tabitem label="Nx >= 21.4" %} ```shell claude mcp add nx-mcp npx nx mcp ``` {% /tabitem %} {% tabitem label="Nx < 21.4" %} ```shell claude mcp add nx-mcp npx nx-mcp@latest ``` {% /tabitem %} {% /tabs %} ### VS Code {% tabs syncKey="nx-version" %} {% tabitem label="Nx >= 21.4" %} ```shell code --add-mcp '{"name":"nx-mcp","command":"npx","args":["nx","mcp"]}' ``` {% /tabitem %} {% tabitem label="Nx < 21.4" %} ```shell code --add-mcp '{"name":"nx-mcp","command":"npx","args":["nx-mcp"]}' ``` {% /tabitem %} {% /tabs %} Alternatively, configure it in your VS Code settings or use the Nx Console extension for automatic setup. ### Cursor 1. Install [Nx Console](/docs/getting-started/editor-setup) from the marketplace 2. You'll receive a notification to "Improve Copilot/AI agent with Nx-specific context" 3. Click "Yes" to configure the MCP server If you miss the notification, run the `nx.configureMcpServer` command from the command palette (`Ctrl/Cmd + Shift + P`). ### JetBrains IDEs 1. Install [Nx Console](/docs/getting-started/editor-setup) from the marketplace 2. You'll receive a notification to "Improve Copilot/AI agent with Nx-specific context" 3. Click "Yes" to configure the MCP server If you miss the notification, run the `Nx: Setup MCP Server` command from the command palette (`Ctrl/Cmd + Shift + A`). ## Command-line options The `nx mcp` command (or `npx nx-mcp` for older versions) accepts the following options: | Option | Alias | Description | | ----------------------- | ----- | ----------------------------------------------------------------------------------------------------------------------------------- | | `[workspacePath]` | `-w` | Path to the Nx workspace root. Defaults to the current working directory if not provided. | | `--transport ` | | Transport protocol to use: `stdio` (default), `sse`, or `http` | | `--port ` | `-p` | Port to use for the HTTP/SSE server (default: 9921). Only valid with `--transport sse` or `--transport http`. | | `--tools ` | `-t` | Filter which tools are enabled. Accepts glob patterns including negation (e.g., `"*"`, `"!nx_docs"`, `"ci_*"`) | | `--minimal` | | Hide workspace analysis tools (default: `true`). Use `--no-minimal` to expose all tools. See [Minimal Mode](#minimal-mode-default). | | `--disableTelemetry` | | Disable sending of telemetry data | | `--debugLogs` | | Enable debug logging | | `--help` | | Display help information | ### HTTP transport If you want to host the server instead of communicating via `stdio`, use the `--transport` and `--port` flags: {% tabs syncKey="nx-version" %} {% tabitem label="Nx >= 21.4" %} ```shell npx nx mcp --transport sse --port 9921 ``` {% /tabitem %} {% tabitem label="Nx < 21.4" %} ```shell npx nx-mcp@latest --transport sse --port 9921 ``` {% /tabitem %} {% /tabs %} The HTTP transport supports multiple concurrent connections, allowing different clients to connect simultaneously with independent sessions. ### Filtering tools You can limit which tools are available using the `--tools` option with glob patterns: {% tabs syncKey="nx-version" %} {% tabitem label="Nx >= 21.4" %} ```shell # Enable all tools (default) npx nx mcp --tools "*" # Disable specific tools npx nx mcp --tools "*" "!nx_docs" # Enable only Nx Cloud CI tools npx nx mcp --tools "ci_*" # Enable workspace tools only npx nx mcp --tools "nx_workspace" "nx_project_details" "nx_docs" ``` {% /tabitem %} {% tabitem label="Nx < 21.4" %} ```shell # Enable all tools (default) npx nx-mcp@latest --tools "*" # Disable specific tools npx nx-mcp@latest --tools "*" "!nx_docs" # Enable only Nx Cloud CI tools npx nx-mcp@latest --tools "ci_*" # Enable workspace tools only npx nx-mcp@latest --tools "nx_workspace" "nx_project_details" "nx_docs" ``` {% /tabitem %} {% /tabs %} This is useful when you want to restrict the LLM's capabilities or reduce noise in the tool list. ### Minimal mode (Default) By default, the MCP server runs in **minimal mode** (`--minimal`), which hides workspace analysis and generator tools. These capabilities are now handled more efficiently by [agent skills](/docs/features/enhance-ai), which provide domain knowledge as incrementally-loaded instructions rather than tool-call-based data dumps. This reduces token usage and keeps the tool list focused on what MCP does best: connectivity to Nx Cloud and running processes. To restore all tools (e.g., for clients that don't support skills), pass `--no-minimal`: {% tabs syncKey="nx-version" %} {% tabitem label="Nx >= 21.4" %} ```shell npx nx mcp --no-minimal ``` {% /tabitem %} {% tabitem label="Nx < 21.4" %} ```shell npx nx-mcp@latest --no-minimal ``` {% /tabitem %} {% /tabs %} When the Nx Console extension manages the MCP server in VS Code or Cursor, it automatically enables minimal mode if it detects that agent skills are installed in the workspace. ## Available tools ### Documentation tools | Tool | Description | | --------- | ------------------------------------------------------------------------------------------------- | | `nx_docs` | Returns documentation sections relevant to user queries about Nx configuration and best practices | ### Task monitoring tools | Tool | Description | | ---------------------------------- | ---------------------------------------------------------------- | | `nx_current_running_tasks_details` | Lists currently running Nx TUI processes and their task statuses | | `nx_current_running_task_output` | Returns terminal output for specific running tasks | ### Visualization tools | Tool | Description | | -------------------- | -------------------------------------------------------------------------------------------------------- | | `nx_visualize_graph` | Opens interactive project or task graph visualizations (requires a running IDE instance with Nx Console) | ### Nx Cloud CI tools These tools enable AI agents to interact with CI pipelines and self-healing capabilities: | Tool | Description | | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ci_information` | Retrieves CI pipeline execution information from Nx Cloud for the current branch. Returns pipeline status, failed tasks, and self-healing status. | | `ci_task_output` | Retrieves terminal output for a specific CI task (failed or successful), using a runId from `ci_information` or resolving it from the branch / URL context. | | `update_self_healing_fix` | Apply or reject a self-healing CI fix from Nx Cloud. Records the decision on the suggested fix. | ### Extended tools (--no-minimal) The following tools are hidden by default in minimal mode. They're available when running with `--no-minimal`, which is useful for AI clients that don't support skills or instruction files. #### Workspace tools | Tool | Description | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `nx_workspace` | Returns a readable representation of the project graph and nx.json configuration. Also returns any project graph errors if present. | | `nx_workspace_path` | Returns the path to the Nx workspace root | | `nx_project_details` | Returns complete project configuration in JSON format for a specific project, including targets, dependencies, and metadata | | `nx_available_plugins` | Lists available Nx plugins from the core team and local workspace plugins | #### Generator tools | Tool | Description | | --------------------- | --------------------------------------------------------------------------------------------- | | `nx_generators` | Returns a list of available code generators in your workspace | | `nx_generator_schema` | Returns the detailed JSON schema for a specific Nx generator, including all available options | {% aside type="note" title="Tool Availability" %} When no workspace path is specified, only the `nx_docs` and `nx_available_plugins` tools will be available. Nx Cloud tools require a workspace connected to Nx Cloud. {% /aside %} ## Available resources When connected to an Nx Cloud-enabled workspace, the Nx MCP server automatically exposes recent CI Pipeline Executions (CIPEs) as MCP resources. Resources appear in your AI tool's resource picker, allowing the LLM to access detailed information about CI runs including: - Failed tasks and their error messages - Terminal output from task executions - Affected files in the pipeline run - Build timing and performance data ## Tool usage examples ### Accessing documentation Get accurate guidance without hallucinations: ```text How do I configure Nx release for conventional commits? What are the caching options for tasks? ``` The `nx_docs` tool retrieves relevant, up-to-date documentation based on your query. ### Analyzing CI failures When a CI build fails, your AI assistant can: ```text What failed in my last CI run? Help me fix the build error ``` Using the Nx Cloud tools, the AI can: 1. Access detailed information about the failed build 2. Retrieve terminal output from failed tasks 3. Understand what changed and suggest fixes ### Autonomous CI monitoring With the `ci_information` and `update_self_healing_fix` tools, AI agents can monitor CI pipelines and interact with self-healing. For a ready-to-use implementation, see the [CI monitoring skill](/docs/features/enhance-ai#self-healing-ci-integration) in Claude Code: ```text Monitor CI and fix any failures ``` The AI agent can: 1. Poll CI pipeline status via `ci_information` 2. Receive failure context and self-healing suggestions 3. Apply verified fixes via `update_self_healing_fix` 4. Continue iterating until CI passes ### Workspace exploration and code generation For workspace exploration, code generation, and task execution, agents use [skills](/docs/features/enhance-ai) rather than MCP tools. Skills teach agents how to use the Nx CLI directly — running commands like `nx show projects`, `nx show project myapp`, and `nx generate` — which is more token-efficient than fetching large JSON payloads via MCP. If your AI client doesn't support skills, use `--no-minimal` to expose the full set of workspace and generator tools via MCP. --- ## .nxignore Reference You may optionally add an `.nxignore` file to the root. This file is used to specify files in your workspace that should be completely ignored by Nx. The syntax is the same as a [`.gitignore` file](https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository#_ignoring). **When a file is specified in the `.nxignore` file**, changes to that file are not taken into account in the `affected` calculations. --- ## Code Owners {% index_page_cards path="reference/owners" /%} --- ## @nx/owners - Generators ## `init` Initialize the owners plugin **Usage:** ```shell nx g @nx/owners:init ``` ## `sync-codeowners-file` Sync Nx Owners config to a `CODEOWNERS` file **Usage:** ```shell nx g @nx/owners:sync-codeowners-file ``` --- ## Overview of the Nx Owners Plugin The `@nx/owners` plugin extends the CODEOWNERS functionality to allow you to define code ownership based on projects in addition to the standard file-based definitions. It leverages the [`nx sync`](/docs/concepts/sync-generators) command to compile `owners` configuration settings from `nx.json` and project configuration files into valid CODEOWNERS files for [GitHub](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners), [Bitbucket](https://support.atlassian.com/bitbucket-cloud/docs/set-up-and-use-code-owners/) or [GitLab](https://docs.gitlab.com/ee/user/project/codeowners/). With this plugin, you can specify code ownership using the same project matcher syntax as [`nx run-many`](/docs/reference/nx-commands#nx-run-many). This allows you to easily define rules for multiple projects that may not be located in the same directory. Also, the CODEOWNERS rules will not need to be revisited if a project location is changed or a new project is added. {% aside type="note" title="This plugin requires an active Nx Enterprise license" %} In order to use `@nx/owners`, you need to have an active Nx Enterprise license. If you don't have a license or it has expired, the syncing process will stop working and you'll need to manually maintain your CODEOWNERS file. {% /aside %} ## Set up @nx/owners 1. [Activate your license](/docs/enterprise/activate-license) if you haven't already 2. Install the package ```shell nx add @nx/owners ``` 3. Configure Ownership Configure the `@nx/owners` plugin in the `nx.json` file or in individual project configuration files. Consult the [Owners Configuration Reference](#owners-configuration-reference) section for more details. 4. Configure the [Sync Generator](/docs/concepts/sync-generators) and CI The `nx add @nx/owners` command should have registered the `@nx/owners:sync-codeowners-file` generator as a `globalGenerator` in `nx.json`. You can double check to make sure: ```jsonc // nx.json { "sync": { "globalGenerators": ["@nx/owners:sync-codeowners-file"], }, } ``` Add `nx sync:check` to the beginning of the CI process. ```yaml - name: Ensure the workspace configuration is in sync run: npx nx sync:check ``` It is also often helpful to add `nx sync` as a git push hook or git commit hook. ## Owners configuration reference {% tabs %} {% tabitem label="GitHub" %} ```jsonc // nx.json { // Can be set to true instead of an object to accept all defaults "owners": { // Options are `github`, `bitbucket` or `gitlab`. (Optional) Defaults to `github` "format": "github", // (Optional) Default changes based on format: `.github/CODEOWNERS`, `.bitbucket/CODEOWNERS`, `.gitlab/CODEOWNERS` "outputPath": "CODEOWNERS", // (Optional) "patterns": [ { "description": "A description of the rule", "owners": ["@joelovesrust"], // specify either projects or files, not both // Can be any project specifier that could be used in `nx run-many` // See https://nx.dev/docs/reference/nx-commands#nx-run-many "projects": ["my-rust-app", "rust-*", "tag:rust"], // File globs "files": [".github/workflows/**/*"], }, ], }, } ``` {% /tabitem %} {% tabitem label="Bitbucket" %} ```jsonc // nx.json { // Can be set to true instead of an object to accept all defaults "owners": { // Options are `github`, `bitbucket` or `gitlab`. (Optional) Defaults to `github` "format": "bitbucket", // (Optional) Default changes based on format: `.github/CODEOWNERS`, `.bitbucket/CODEOWNERS`, `.gitlab/CODEOWNERS` "outputPath": "CODEOWNERS", // (Optional) "patterns": [ { "description": "A description of the rule", "owners": ["@joelovesrust"], // specify either projects or files, not both // Can be any project specifier that could be used in `nx run-many` // See https://nx.dev/docs/reference/nx-commands#nx-run-many "projects": ["my-rust-app", "rust-*", "tag:rust"], // File globs "files": [".github/workflows/**/*"], }, ], }, } ``` {% /tabitem %} {% tabitem label="GitLab" %} If you are using GitLab, you can specify CODEOWNERS [sections](https://docs.gitlab.com/ee/user/project/codeowners/#organize-code-owners-by-putting-them-into-sections) which give you a little more control over the PR process. ```jsonc // nx.json { // Can be set to true instead of an object to accept all defaults "owners": { // Options are `github`, `bitbucket` or `gitlab`. (Optional) Defaults to `github` "format": "gitlab", // (Optional) Default changes based on format: `.github/CODEOWNERS`, `.bitbucket/CODEOWNERS`, `.gitlab/CODEOWNERS` "outputPath": "CODEOWNERS", // (Optional) "patterns": [ { "description": "A description of the rule", "owners": ["@joelovesrust"], // Specify either `projects` or `files`, not both // Can be any project specifier that could be used in `nx run-many` // See https://nx.dev/docs/reference/nx-commands#nx-run-many "projects": ["my-rust-app", "rust-*", "tag:rust"], // File globs "files": [".github/workflows/**/*"], }, ], // (Optional) "sections": [ { // Labels the section "name": "My section", // (Optional) The owners to use if a pattern does not specify a set of owners "defaultOwners": ["@cheddar"], // Specify either `numberOfRequiredApprovals` or `optional`, not both // (Optional) Require more than one person to approve the PR "numberOfRequiredApprovals": 2, // (Optional) Do not require any approvals, just notify the owners "optional": true, // Same format as the root patterns "patterns": [], }, ], }, } ``` ```jsonc // path/to/project/project.json { "owners": { // Keys are file globs relative to the root of the project // Owners can be listed as a string array "**/*": ["@ahmed", "@petra"], // Owners can be listed as an object with a description "README.md": { "description": "Jared is very particular about the README file", "owners": ["@jared"] } } }; ``` {% /tabitem %} {% /tabs %} **Examples:** {% tabs %} {% tabitem label="GitHub" %} ```jsonc // nx.json { "owners": { // defaults to "github" "format": "github", // defaults to ".github/CODEOWNERS" "outputPath": "CODEOWNERS", "patterns": [ { "description": "Joe should double check all changes to rust code", "projects": ["tag:rust"], "owners": ["@joelovesrust"], }, { "description": "The Finance team owns these projects", "projects": ["finance-*"], "owners": ["@finance-team"], }, { "description": "Alice, Bob and Cecil work together on these projects", "projects": ["admin", "booking", "cart"], "owners": ["@alice", "@bob", "@cecil"], }, { "description": "CI Workflows", "files": [".github/workflows/**/*"], "owners": ["@devops"], }, ], }, } ``` ```jsonc // packages/my-project/project.json { "owners": { "**/*": ["@ahmed", "@petra"], "package.json": ["@ahmed"], "README.md": { "owners": ["@jared"], "description": "Jared is very particular about the README file" } }, }; ``` {% /tabitem %} {% tabitem label="Bitbucket" %} ```jsonc // nx.json { "owners": { "format": "bitbucket", // defaults to ".bitbucket/CODEOWNERS" "outputPath": "CODEOWNERS", "patterns": [ { "description": "Joe should double check all changes to rust code", "projects": ["tag:rust"], "owners": ["@joelovesrust"], }, { "description": "The Finance team owns these projects", "projects": ["finance-*"], "owners": ["@finance-team"], }, { "description": "Alice, Bob and Cecil work together on these projects", "projects": ["admin", "booking", "cart"], "owners": ["@alice", "@bob", "@cecil"], }, { "description": "CI Workflows", "files": [".github/workflows/**/*"], "owners": ["@devops"], }, ], }, } ``` ```jsonc // packages/my-project/project.json { "owners": { "**/*": ["@ahmed", "@petra"], "package.json": ["@ahmed"], "README.md": { "owners": ["@jared"], "description": "Jared is very particular about the README file" } }, }; ``` {% /tabitem %} {% tabitem label="GitLab" %} ```jsonc // nx.json { "owners": { "format": "gitlab", // defaults to ".gitlab/CODEOWNERS" "outputPath": "CODEOWNERS", "patterns": [ { "description": "Joe should double check all changes to rust code", "projects": ["tag:rust"], "owners": ["@joelovesrust"], }, { "description": "CI Workflows", "files": [".github/workflows/**/*"], "owners": ["@devops"], }, ], "sections": [ { "name": "Finance", "defaultOwners": ["@finance-team"], "numberOfRequiredApprovals": 2, "patterns": [ { "description": "The Finance team owns these projects", "projects": ["finance-*"], }, { "description": "Alice, Bob and Cecil work together on these projects", "projects": ["admin", "booking", "cart"], "owners": ["@alice", "@bob", "@cecil"], }, ], }, ], }, } ``` ```jsonc // packages/my-project/project.json { "owners": { "**/*": ["@ahmed", "@petra"], "package.json": ["@ahmed"], "README.md": { "owners": ["@jared"], "description": "Jared is very particular about the README file" } }, }; ``` {% /tabitem %} {% /tabs %} ## Set a default owner To give the whole repository a fallback owner, use a file wildcard (`files: ["*"]`), not a project wildcard (`projects: ["*"]`). The two behave differently: - `projects: ["*"]` matches every project and expands to one CODEOWNERS entry per project root. This pattern only covers files that live inside a project. Files outside every project root such as a top-level configuration files do not get an owner. - `files: ["*"]` matches all files in the repository and emits a single entry in CODEOWNERS, `* @owner`. You can use this pattern as a repo-wide default owner. List it first and let the more specific patterns below it override since CODEOWNERS applies the last matching rule for a given file. {% aside type="note" title="Order matters" %} CODEOWNERS resolves a file to the **last** pattern that matches it. Put the `files: ["*"]` catch-all at the top of your `patterns` array so the specific rules that follow override it. {% /aside %} ```jsonc // nx.json { "owners": { "format": "github", "outputPath": "CODEOWNERS", "patterns": [ { "description": "Default owner for everything", "files": ["*"], "owners": ["@platform-team"], }, { "description": "The Finance team owns these projects",- `projects: ["*"]` matches every project and expands to one CODEOWNERS entry per project root. This pattern only covers files that live inside a project. Files outside every project root such as a a top-level configuration files do not get an owner. "projects": ["finance-*"], "owners": ["@finance-team"], }, ], }, } ``` Nx generates the following CODEOWNERS file: ```yaml {% title=".github/CODEOWNERS" %} # Default owner for everything * @platform-team # The Finance team owns these projects /packages/finance-ui @finance-team /packages/finance-data @finance-team ``` Every file falls back to `@platform-team`, and the later, more specific rule reassigns the finance projects to `@finance-team`. --- ## Playwright For setup and guides, see [Nx with Playwright](/docs/technologies/test-tools/playwright/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Playwright" /%} --- ## Nx Powerpack End User License Agreement Last Updated: September 18th, 2024 This Nx End User License Agreement (together with your associated Order Information, the "Agreement") governs your use of our Nx Powerpack, a suite of paid extensions for Nx (the "Software"). To make this Agreement easier to read, the terms "Nx," "we," and "us" refers to Narwhal Technologies, Inc., and the term "you" refers to you and any organization that you are acting on behalf of in signing up for a subscription to the Software. If you are an individual acting on behalf of an entity, you represent and warrant that you have the authority to enter into this Agreement on behalf of that entity and to legally bind that entity. If you do not accept the terms of this Agreement, then you must not use the Software. ## 1. Definitions (a) "Licensed Volume" means the limits, volume or other conditions of permitted use for the Software as set forth in the Order Information, including any limits on the number of Authorized Users or number of workspaces. (b) "Nx IP" means the Software, algorithms, technology, databases, tools, know-how or processes used to provide or deliver the Software or any related services, and its documentation ("Documentation"), all improvements, modifications or derivative works of the foregoing (regardless of authorship), and all intellectual property rights ("IPR") in any of the foregoing. (c) "Order Information" means (i) certain terms associated with your subscription to Use the Software, as communicated to you via our pricing page available at https://cloud.nx.app/powerpack/purchase ("Pricing Page"), as may be updated from time to time or (ii) as otherwise set forth in a written order form or purchase order signed by you and Nx ("PO"). ## 2. License (a) License. Subject to the terms and conditions of this Agreement (including receipt of the License Key), Nx hereby grants you a worldwide, non-exclusive, non-transferable (except in compliance with Section 12), non-sublicensable license to download and install the Software on premises owned or controlled by you, and run the Software solely for your internal business purposes (the "Purpose") during the Term in accordance with the Documentation and subject to the Licensed Volume. You have the right to permit your employees or contributors ("Authorized Users") to use the Software on your behalf for the Purpose in accordance with this Agreement; provided, however, that you will remain fully and directly liable to Nx for any and all use of the Software by Authorized Users as if such use was by you yourself under this Agreement. Nothing in this Agreement will operate to grant you any right, title or interest, whether by implication, estoppel or otherwise, in or to the Nx IP, other than as expressly set forth herein. As between Nx and you, Nx will exclusively own all right, title and interest in and to the Nx IP. (b) Use Restrictions. You will not at any time, directly or indirectly, and will not permit any person or entity (collectively, "Person") (including, without limitation, your Authorized Users) to: (i) copy, modify or create derivative works of the Software or Documentation, in whole or in part; (ii) reverse engineer, disassemble, decompile, decode or otherwise attempt to derive or gain improper access to any software component of the Software, in whole or in part; (iii) frame, mirror, sell, resell, rent or lease the use of the Software, License Key or Documentation to any other Person, or otherwise use or allow any Person to use the Software, License Key or Documentation for any purpose other than for your benefit for the Purpose in accordance with this Agreement; (iv) create any script or other automated tool that attempts to create multiple License Keys; (v) use the Software or License Key in any infringing or unlawful manner; or (vi) use the Software, Documentation or any other Confidential Information of Nx for competitive analysis or benchmarking purposes, or to otherwise develop, commercialize, license or sell any product, service or technology that could, directly or indirectly, compete with the Nx IP. (c) Authorized Equipment. You will bear the sole responsibility for obtaining and maintaining the hardware and any computer systems, networks, telecommunications systems, Internet access, third party services or any other materials required to meet the minimum technical and operational requirements required to operate the Software. ## 3. License Key; Fees and Payment To use the Software, you are required to purchase a license key via the Pricing Page or PO ("License Key"). You are liable for any actions or inactions performed under your License Key. You will pay Nx all fees set forth in your Order Information ("Fees") on the payment dates specified in your Order Information. All Fees are non-refundable. Nx reserves the right to change the Fees and Licensed Volume and to institute new Fees and revised limits of the Licensed Volume upon 30 days' prior notice to you. Unless otherwise specified in the Order Information, Fees will be paid by the approved credit card that you designate when you sign up to use the Software. You hereby authorize us to initiate all payment transactions for Fees from your approved credit card when such Fees are due, if applicable. Any and all Fees that are not paid to Nx when due will accrue interest at a rate of 1.5% per month, or the maximum rate permitted by law, whichever is greater. In the event of a conflict between this Agreement and the Order Information, the Order Information will control and govern. All Fees do not include any sales, use, value added or other applicable taxes, payment of which will be your sole responsibility (excluding any taxes based on Nx's net income). ## 4. Confidential Information "Confidential Information" means any information that one party (the "Disclosing Party") provides to the other party (the "Receiving Party") in connection with this Agreement, whether orally or in writing, that is designated as confidential or that reasonably should be considered to be confidential given the nature of the information and/or the circumstances of disclosure. Confidential Information will not include any information that: (i) is or becomes generally known to the public through no fault or breach of this Agreement by the Receiving Party; (ii) is rightfully known by the Receiving Party at the time of disclosure without an obligation of confidentiality; (iii) is independently developed by the Receiving Party without access to or use of any Confidential Information of the Disclosing Party that can be evidenced in writing; or (iv) is rightfully obtained by the Receiving Party from a third-party without restriction on use or disclosure. For clarity, the Software and the Documentation will be deemed Confidential Information of Nx. The Receiving Party will not use or disclose any Confidential Information of the Disclosing Party except as necessary to perform its obligations or exercise its rights under this Agreement. The Receiving Party may disclose Confidential Information of the Disclosing Party only: (A) to those of its employees, contractors, agents and advisors who have a bona fide need to know such Confidential Information to perform under this Agreement and who are bound by written agreements with use and nondisclosure restrictions at least as protective of the Confidential Information as those set forth in this Agreement, or (B) as such disclosure may be required by the order or requirement of a court, administrative agency or other governmental body, subject to the Receiving Party providing to the Disclosing Party reasonable written notice to allow the Disclosing Party to seek a protective order or otherwise contest the disclosure. ## 5. Policies; Support You hereby acknowledge that you have reviewed and agreed to the Nx Privacy Policy at https://cloud.nx.app/privacy. Such policy is hereby incorporated into and is hereby deemed a part of this Agreement, binding upon you and you Authorized Users with respect to your and their use of the Software in connection with this Agreement. As part of your subscription to the Software, Nx will provide reasonable support in connection with the Software in accordance with the support terms set forth in your Order Information. ## 6. Feedback From time-to-time you or your Authorized Users may provide Nx with feedback with regard to the Software. You, on behalf of yourself and your Authorized Users, hereby grant Nx a perpetual, irrevocable, royalty-free and fully-paid up license to use and exploit all such feedback in connection with Nx's business purposes. ## 7. Indemnification (a) Nx Indemnification. Nx will defend and pay all damages finally awarded against you pursuant to a final, valid and binding judgment or order, or a final settlement agreement with respect to any claim, suit or proceeding brought by a third party against you arising from the Software's infringement of such third-party's IPR. The foregoing obligation will not apply if the underlying third-party claim arises from (i) your breach of this Agreement, negligence, willful misconduct or fraud; (ii) modifications to the Software by anyone other than Nx; or (iii) combinations of the Software of with software, data or materials not provided by Nx. If Nx reasonably believes the Software (or any component) could infringe any third party's IPR, Nx may, at its sole option and expense: (A) procure the right for you to continue using the Software (or any infringing component) to make it non-infringing without materially reducing its functionality; or (B) replace the Software (or any infringing component) with a non-infringing alternative that is functionally equivalent in all material respects. If the foregoing remedies are not available to Nx on commercially reasonable terms, then Nx may terminate your use of the Software upon notice to you. (b) Your Indemnification. You will defend and pay all damages finally awarded against Nx pursuant to a final, valid and binding judgment or order or a final settlement agreement with respect to any claim, suit or proceeding brought by a third party against Nx arising from any breach of the restrictions set forth in Section 2(b). (c) Indemnification Procedures. The party seeking defense and indemnity (the "Indemnified Party") will promptly notify the other party (the "Indemnifying Party") of any and all such claims and will reasonably cooperate with the Indemnifying Party with the defense and/or settlement thereof. The Indemnifying Party will have the sole right to conduct the defense of any claim for which the Indemnifying Party is responsible hereunder (provided that the Indemnifying Party may not settle any claim without the Indemnified Party's prior written approval unless the settlement unconditionally releases the Indemnified Party from all liability, does not require any admission by the Indemnified Party, and does not place restrictions upon the Indemnified Party's business). The Indemnified Party may participate in the defense or settlement of any such claim at its own expense and with its own choice of counsel or, if the Indemnifying Party refuses to fulfill its obligation of defense, the Indemnified Party may defend itself and seek reimbursement from the Indemnifying Party. ## 8. Disclaimers THE SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND NX MAKES NO WARRANTIES OR REPRESENTATIONS TO YOU, YOUR AUTHORIZED USERS OR TO ANY OTHER PERSON REGARDING THE SOFTWARE. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, NX HEREBY DISCLAIMS (a) ALL WARRANTIES AND REPRESENTATIONS, WHETHER EXPRESS OR IMPLIED AND (b) ANY WARRANTY THAT USE OF THE SOFTWARE WILL BE ERROR-FREE. ## 9. Limitations of Liability EXCEPT FOR A PARTY'S GROSS NEGLIGENCE, WILLFUL MISCONDUCT OR FRAUD, IN NO EVENT WILL (a) EITHER PARTY BE LIABLE TO THE OTHER PARTY FOR ANY INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE OR CONSEQUENTIAL DAMAGES, LOSS OF INCOME, DATA, PROFITS, REVENUE OR BUSINESS INTERRUPTION, OR THE COST OF SUBSTITUTE SERVICES OR OTHER ECONOMIC LOSS, ARISING OUT OF OR IN CONNECTION WITH THIS AGREEMENT, WHETHER SUCH LIABILITY ARISES FROM ANY CLAIM BASED ON CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, AND WHETHER OR NOT SUCH PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE AND (b) NX'S TOTAL LIABILITY TO YOU, YOUR AUTHORIZED USERS OR ANY OTHER PERSON IN CONNECTION WITH THIS AGREEMENT OR THE PROVISION OF THE SOFTWARE EXCEED THE FEES ACTUALLY PAID BY YOU TO NX IN THE 12 MONTH PERIOD PRECEDING THE ACTION GIVING RISE TO SUCH LIABILITY. ## 10. Term and Termination (a) Term; Termination. The term of this Agreement will begin on the effective date in the Order Information, and will expire at the end of the initial term specified in the Order Information (the "Initial Term"). Following the Initial Term, this Agreement will automatically renew for successive one-month terms (the Initial Term, together with any renewal term, the "Term"), unless Nx or you provides the other with at least twenty (20) days' written notice of its intent not to renew prior to the end of the then-current term. Either party may terminate this Agreement, effective on written notice to the other party, if the other party materially breaches this Agreement, and such breach: (A) is incapable of cure; or (B) being capable of cure, remains uncured thirty (30) days after the non-breaching party provides the breaching party with written notice of such breach. (b) Effect of Termination; Survival. Upon termination of this Agreement, your right to use the Software will immediately terminate and you shall cease using the Software, including for example, by permanently removing the Software dependency. This Section 10(b) and Sections 2(b), 3, 4, 5 (first sentence), 6 - 9 and 12 survive any termination of this Agreement. ## 11. Trademarks You hereby grant Nx a limited, non-exclusive, royalty-free license to use and display your name, designated trademarks and associated logos ("Your Marks") during the Term in connection with Nx's marketing and promotional efforts for its products and services, including by publicly naming you as a customer of Nx. Nx will conform to and observe the trademark standards as you prescribe from time to time. All goodwill generated by Nx's use of Your Marks inures to your benefit. ## 12. Miscellaneous This Agreement, together with the Order Information, is the complete and exclusive agreement between the parties with respect to its subject matter and supersedes all prior or contemporaneous agreements, communications and understandings, both written and oral, with respect to its subject matter, including any prior terms. This Agreement may be amended or modified only by a written document assented by duly authorized representatives of the parties. Nx may perform an audit of your use of the Software once per year in connection with your compliance with this Agreement, including if you are exceeding the Licensed Volume. Nx may provide notices to you by posting them on our website, by providing electronic notification via the Software, or by email to the address associated with your account. You may provide notices to us via email at powerpack-support@nrwl.io. All notices are effective upon posting or when delivered. Except as otherwise set forth herein, either party's failure to enforce any provision of this Agreement will not constitute a waiver of future enforcement of that or any other provision. No waiver of any provision of this Agreement will be effective unless it is in writing and signed by the party granting the waiver. If any provision of this Agreement is held invalid, illegal or unenforceable, that provision will be enforced to the maximum extent permitted by law, and the remaining provisions of this Agreement will remain in full force and effect. This Agreement will be governed by and construed in accordance with the laws of the State of California without giving effect to any principles of conflict of laws that would lead to the application of the laws of another jurisdiction. Any legal action or proceeding arising under this Agreement will be brought exclusively in the federal or state courts located in the Northern District of California and the parties irrevocably consent to the personal jurisdiction and venue therein. Nx may freely assign its rights and obligations under this Agreement. You may not assign or transfer this Agreement, by operation of law or otherwise, without Nx's prior written consent; provided, however, that you may assign your rights or delegate your obligations, in whole or in part, without such consent, to (i) one or more of your affiliates, or (ii) a third party that succeeds to all or substantially all of your business and assets relating to the subject matter of this Agreement, whether by sale, merger, operation of law or otherwise. Any attempt to assign or transfer this Agreement without such consent will be void. Subject to the foregoing, this Agreement is binding upon and will inure to the benefit of each of the parties and their respective successors and permitted assigns. Unless otherwise expressly provided, no provisions of this Agreement are intended or will be construed to confer upon or give to any person or entity, other than the parties, any rights, remedies or other benefits under or by reason of this Agreement. --- ## Project Configuration A project's configuration is constructed by Nx from three sources: 1. [Tasks inferred by Nx plugins](/docs/concepts/mental-model#inferred-tasks) from tooling configuration 2. [Workspace `targetDefaults`](/docs/reference/nx-json#target-defaults) defined in the `nx.json` file 3. Individual project level configuration files (`package.json` and `project.json`) Each source will [overwrite the previous source](/docs/kb/pass-args-to-commands). That means `targetDefaults` will overwrite inferred tasks and project level configuration will overwrite both `targetDefaults` and inferred tasks. The combined project configuration can be viewed in the project details view by using [Nx Console](/docs/getting-started/editor-setup) in your IDE or by running: ```shell nx show project myproject --web ``` {% project_details title="Project Details View" %} ```json { "project": { "name": "demo", "data": { "root": " packages/demo", "projectType": "application", "targets": { "dev": { "executor": "nx:run-commands", "options": { "command": "vite dev", "continuous": true }, "metadata": { "technologies": ["vite"] } }, "build": { "executor": "nx:run-commands", "inputs": ["production", "^production"], "outputs": ["{projectRoot}/dist"], "options": { "command": "vite build" }, "metadata": { "technologies": ["vite"] } } } } }, "sourceMap": { "targets": ["packages/demo/vite.config.ts", "@nx/vite"], "targets.dev": ["packages/demo/vite.config.ts", "@nx/vite"], "targets.build": ["packages/demo/vite.config.ts", "@nx/vite"] } } ``` {% /project_details %} The project details view also shows where each setting is defined so that you know where to change it. ## Project level configuration files If you need to edit your project settings or modify an inferred task, you can do so in either `package.json` or `project.json` files. The examples on this page show both styles. Nx merges the two files to get each project's configuration. The full [machine readable schema](https://github.com/nrwl/nx/blob/master/packages/nx/schemas/project-schema.json) is available on GitHub. ### When to Use package.json vs project.json Both `package.json` and `project.json` can be used to configure Nx targets, and both support the same configuration options including executors: - **package.json**: Use the `"nx"` property to define targets with executors, options, and other Nx-specific configuration - **project.json**: A dedicated Nx configuration file that keeps your `package.json` focused on package metadata The choice between `package.json` and `project.json` is primarily a matter of preference. The `package.json` is standard for JavaScript projects, so you may prefer to use that over the Nx-specific `project.json` file. The following configuration creates `build` and `test` targets for Nx. {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```jsonc // package.json { "name": "mylib", "scripts": { "test": "jest", }, "nx": { // you could also do this in "scripts", but this "targets" configuration also supports Nx executors "targets": { "build": { "command": "tsc -p tsconfig.lib.json", }, }, }, } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // project.json { "root": "libs/mylib/", "targets": { "test": { "executor": "@nx/jest:jest", "options": {/* ... */}, }, "build": { "executor": "@nx/js:tsc", "options": {/* ... */}, }, }, } ``` {% /tabitem %} {% /tabs %} You can invoke `nx build mylib` or `nx test mylib` without any extra configuration. Below are some more complete examples of project configuration files. For a more intuitive understanding of the roles of each option, you can highlight the options in the excerpt below that relate to different categories. Orchestration settings control the way [Nx runs tasks](/docs/features/run-tasks). Execution settings control the actual task that is run. Caching settings control when [Nx caches a task](/docs/features/cache-task-results) and what is actually cached. {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```jsonc {% meta="{ Orchestration:[15,18,20,23,26],Execution:[5,6,7],Caching:[10-13,16,17,21,22] }" %} // package.json { "name": "mylib", "scripts": { "test": "jest", "build": "tsc -p tsconfig.lib.json", // the actual command here is arbitrary "ignored": "exit 1", }, "nx": { "namedInputs": { "default": ["{projectRoot}/**/*"], "production": ["!{projectRoot}/**/*.spec.tsx"], }, "targets": { "build": { "inputs": ["production", "^production"], "outputs": ["{workspaceRoot}/dist/libs/mylib"], "dependsOn": ["^build"], }, "test": { "inputs": ["default", "^production"], "outputs": [], "dependsOn": ["build"], }, }, "includedScripts": ["test", "build"], // If you want to limit the scripts Nx sees, you can specify a list here. }, } ``` {% /tabitem %} {% tabitem label="project.json" %} ```json {% meta="{ Orchestration: [6,7,13,14,20,23], Execution: [13,17-20,23,24], Caching: [8-11,14,15,21,22] }" %} // project.json { "root": "libs/mylib/", "sourceRoot": "libs/mylib/src", "projectType": "library", "tags": ["scope:myteam"], "implicitDependencies": ["anotherlib"], "namedInputs": { "default": ["{projectRoot}/**/*"], "production": ["!{projectRoot}/**/*.spec.tsx"] }, "targets": { "test": { "inputs": ["default", "^production"], "outputs": [], "dependsOn": ["build"], "executor": "@nx/jest:jest", "options": {} }, "build": { "inputs": ["production", "^production"], "outputs": ["{workspaceRoot}/dist/libs/mylib"], "dependsOn": ["^build"], "executor": "@nx/js:tsc", "options": {} } } } ``` {% /tabitem %} {% /tabs %} ## Task definitions (Targets) A large portion of project configuration is related to defining the tasks for the project. In addition, to defining what the task actually does, a task definition also has properties that define the way that Nx should run that task. Those properties are described in detail below. ### Cache Caching is configured by specifying `"cache": true` in a target's configuration. This will tell Nx that it's ok to cache the results of a given target. For instance, if you have a target that runs tests, you can specify `"cache": true` in the target default configuration for `test` and Nx will cache the results of running tests. ```json // project.json { "targets": { "test": { "cache": true } } } ``` {% aside type="caution" title="Per Project Caching + Distribution" %} If you are using distributed task execution and disable caching for a given target, you will not be able to use distributed task execution for that target. This is because distributed task execution requires caching to be enabled. This means that the target you have disabled caching for, and any targets which depend on that target will fail the pipeline if you try to run them with Nx Agents enabled. {% /aside %} ### Parallelism In Nx 19.5.0+, tasks can be configured to support parallelism or not. By default, tasks are run in parallel with other tasks on a given machine. However, in some cases, tasks can require a shared resource such as a port or memory. For these cases, setting `"parallelism": false`, will ensure that those tasks will not run in parallel with other tasks on a single machine. For example, if the `e2e` tasks all require port 4200, running them in parallel will conflict so the targets can specify to not support parallelism: ```json // project.json { "targets": { "e2e": { "parallelism": false } } } ``` {% aside type="caution" title="Note: Parallelism is only per machine" %} If you are using distributed task execution, tasks will still be run simultaneously on different machines. Because different agents do not share resources with one another, it is perfectly fine for multiple agents to run tasks which do not support parallelism at the same time. Therefore, using Nx Agents is key to running tasks which do not support parallelism quickly and efficiently. {% /aside %} ### Inputs and named inputs Each cacheable task needs to define `inputs` which determine whether the task outputs can be retrieved from the cache or the task needs to be re-run. The `namedInputs` defined in `nx.json` or project level configuration are sets of reusable input definitions. A typical set of inputs may look like this: ```jsonc { "namedInputs": { "default": ["{projectRoot}/**/*"], // every file in the project "production": ["default", "!{projectRoot}/**/*.spec.tsx"], // except test files }, "targets": { "build": { "inputs": [ "production", // this project's production files { "externalDependencies": ["vite"] }, // the version of the vite package ], }, }, } ``` {% cardgrid %} {% linkcard title="Inputs and Named Inputs Reference" description="Learn about all the possible settings for `inputs` and `namedInputs`" href="/docs/reference/inputs" /%} {% linkcard title="Configure Inputs for Task Caching" description="This recipes walks you through a few examples of how to configure `inputs` and `namedInputs`" href="/docs/kb/configure-inputs" /%} {% /cardgrid %} ### Outputs Targets may define outputs to tell Nx where the target is going to create file artifacts that Nx should cache. `"outputs": ["{workspaceRoot}/dist/libs/mylib"]` tells Nx where the `build` target is going to create file artifacts. This configuration is usually not needed. Nx comes with reasonable defaults (imported in `nx.json`) which implement the configuration above. Specifically, by default, the following locations are cached for builds: - `{workspaceRoot}/dist/{projectRoot}`, - `{projectRoot}/build`, - `{projectRoot}/dist`, - `{projectRoot}/public` {% cardgrid %} {% linkcard title="Configure Outputs for Task Caching" description="This recipes walks gives helpful tips to configure `outputs` for tasks" href="/docs/kb/configure-outputs" /%} {% /cardgrid %} Read the [configure outputs for task caching](/docs/kb/configure-outputs) recipe for helpful tips for setting outputs. #### Basic example Usually, a target writes to a specific directory or a file. The following instructs Nx to cache `dist/libs/mylib` and `build/libs/mylib/main.js`: ```json { "targets": { "build": { "outputs": [ "{workspaceRoot}/dist/libs/mylib", "{workspaceRoot}/build/libs/mylib/main.js" ] } } } ``` #### Specifying globs Sometimes, multiple targets might write to the same directory. When possible it is recommended to direct these targets into separate directories. ```json { "targets": { "build-js": { "outputs": ["{workspaceRoot}/dist/libs/mylib/js"] }, "build-css": { "outputs": ["{workspaceRoot}/dist/libs/mylib/css"] } } } ``` But if the above is not possible, globs (parsed by the [GlobSet](https://docs.rs/globset/0.4.5/globset/#syntax) Rust library) can be specified as outputs to only cache a set of files rather than the whole directory. ```json { "targets": { "build-js": { "outputs": ["{workspaceRoot}/dist/libs/mylib/**/*.{js,map}"] }, "build-css": { "outputs": ["{workspaceRoot}/dist/libs/mylib/**/*.css"] } } } ``` More advanced patterns can be used to exclude files and folders in a single line ```json { "targets": { "build-js": { "outputs": ["{workspaceRoot}/dist/libs/!(cache|.next)/**/*.{js,map}"] }, "build-css": { "outputs": ["{workspaceRoot}/dist/libs/mylib/**/!(secondary).css"] } } } ``` ### dependsOn Targets can depend on other targets. This is the relevant portion of the configuration file: ```json { "targets": { "build": { "dependsOn": ["^build"] }, "test": { "dependsOn": ["build"] } } } ``` A common scenario is having to build dependencies of a project first before building the project. This is what the `"dependsOn": ["^build"]` property of the `build` target configures. It tells Nx that before it can build `mylib` it needs to make sure that `mylib`'s dependencies are built as well. This doesn't mean Nx is going to rerun those builds. If the right artifacts are already in the right place, Nx will do nothing. If they aren't in the right place, but they are available in the cache, Nx will retrieve them from the cache. Another common scenario is for a target to depend on another target of the same project. For instance, `"dependsOn": ["build"]` of the `test` target tells Nx that before it can test `mylib` it needs to make sure that `mylib` is built, which will result in `mylib`'s dependencies being built as well. {% aside type="note" title="Dependencies that do not exit" %} If you specify a task in `dependsOn` that never exits, then the dependent task will never start. Label such dependencies as [continuous](#continuous) tasks, which tells Nx to not wait for the process to end before starting the dependent task. {% /aside %} You can also express task dependencies with an object syntax: {% tabs %} {% tabitem label="Dependencies on self" %} ```json { "targets": { "test": { "dependsOn": [ { "target": "build", // target name "params": "ignore" // "forward" or "ignore", defaults to "ignore" } ] } } } ``` {% /tabitem %} {% tabitem label="Dependencies on other projects" %} ```json { "targets": { "build": { "dependsOn": [ { "dependencies": true, // Run this target on all dependencies first "target": "build", // target name "params": "ignore" // "forward" or "ignore", defaults to "ignore" } ] } } } ``` {% /tabitem %} {% tabitem label="Dependencies on specific projects" %} ```json { "targets": { "build": { "dependsOn": [ { "projects": ["my-app"], // Run build on "my-app" first "target": "build", // target name "params": "ignore" // "forward" or "ignore", defaults to "ignore" } ] } } } ``` {% /tabitem %} {% /tabs %} Starting from v19.5.0, wildcards can be used to define dependencies in the `dependsOn` field. ```json { "targets": { "test": { "dependsOn": [ { "target": "build", // target name "params": "ignore" // "forward" or "ignore", defaults to "ignore" }, "build-*", // support for using wildcards in dependsOn, matches: "build-css", "build-js" targets of current project "^build-*", // matches tasks: "build-css", "build-js" targets of dependencies "*build-*", // matches tasks: "build-css", "build-js" as well as "task-with-build-in-middle" targets of current project "^*build-*" // matches tasks: "build-css", "build-js" as well as "task-with-build-in-middle" targets of dependencies ] } } } ``` #### Examples You can write the shorthand configuration above in the object syntax like this: ```json { "targets": { "build": { "dependsOn": [{ "dependencies": true, "target": "build" }] // Run build on my dependencies first }, "test": { "dependsOn": [{ "target": "build" }] // Run build on myself first } } } ``` With the expanded syntax, you also have a third option available to configure how to handle the params passed to the target. You can either forward them or you can ignore them (default). ```json { "targets": { "build": { // forward params passed to this target to the dependency targets "dependsOn": [ { "projects": "{dependencies}", "target": "build", "params": "forward" } ] }, "test": { // ignore params passed to this target, won't be forwarded to the dependency targets "dependsOn": [ { "projects": "{dependencies}", "target": "build", "params": "ignore" } ] }, "lint": { // ignore params passed to this target, won't be forwarded to the dependency targets "dependsOn": [{ "projects": "{dependencies}", "target": "build" }] } } } ``` This also works when defining a relation for the target of the project itself using `"projects": "self"`: ```json { "targets": { "build": { // forward params passed to this target to the project target "dependsOn": [{ "target": "pre-build", "params": "forward" }] } } } ``` The expanded syntax also accepts an `options` property, which controls whether the options configured on this target are forwarded to the dependency targets. Like `params`, it can be set to `"forward"` or `"ignore"` and defaults to `"ignore"`. ```json { "targets": { "build": { "options": { "out": "dist/mylib" }, // forward the options of this target to the dependency target "dependsOn": [{ "target": "pre-build", "options": "forward" }] } } } ``` When the target runs with a configuration, the options of that configuration are merged over the target options before they are forwarded. `params` and `options` are set independently of each other, and when both are forwarded, the params passed on the command line take precedence over the forwarded options. Additionally, when using the expanded object syntax, you can specify individual projects in version 16 or greater. ```json { "targets": { "build": { // Run is-even:pre-build and is-odd:pre-build before this target "dependsOn": [ { "projects": ["is-even", "is-odd"], "target": "pre-build" } ] } } } ``` This configuration is usually not needed. Nx comes with reasonable defaults (imported in `nx.json`) which implement the configuration above. ### Continuous In Nx 21+, tasks that never exit (sometimes called long-running processes) can be configured with `"continuous": true` to prevent their dependent tasks from waiting for task completion. For example, the `e2e` task depends on a continuous `serve` task to ensure that the development server is running. In this example, application's configuration labels the `serve` task as continuous. ```json // apps/myapp/project.json { "targets": { "serve": { "continuous": true } } } ``` And the E2E project's `e2e` task has a dependency on the `serve` task, which ensures that the server is running when we run the `e2e` task. ```json // apps/myapp-e2e/project.json { "targets": { "e2e": { "dependsOn": [ { "projects": "myapp", "target": "serve" } ] } } } ``` ### Sync generators In the same way that `dependsOn` tells Nx to run another task before running this task, the `syncGenerator` property tells Nx to run a generator to ensure that your files are in the correct state before this task is run. [Sync generators](/docs/concepts/sync-generators) are especially useful for keeping configuration files up to date with the project graph. ```json { "targets": { "build": { "syncGenerators": ["some-plugin:my-sync-generator"] } } } ``` ### Executor/command options To define what a task does, you must configure which command or executor will run when the task is executed. In the case of [inferred tasks](/docs/concepts/mental-model#inferred-tasks) you can provide project-specific overrides. As an example, if your repo has projects with a `build` inferred target running the `vite build` command, you can provide some extra options as follows: ```json { "targets": { "build": { "options": { "assetsInlineLimit": 2048, "assetsDir": "static/assets" } } } } ``` For more details on how to pass args to the underlying command see the [Pass Args to Commands recipe](/docs/kb/pass-args-to-commands). In the case of an explicit target using an executor, you can specify the executor and the options specific to that executor as follows: ```json { "targets": { "build": { "executor": "@nx/js:tsc", "options": { "generateExportsField": true } } } } ``` ### Spread token By default, each configuration source overwrites the previous one for every property it defines. The spread token (`"..."`) lets you control merge priority rather than always replacing. **In arrays**, `"..."` is substituted with the items from the base array at that position: ```json // project.json { "targets": { "build": { "inputs": ["my-project-input", "..."] } } } ``` If the inferred target has `inputs: ["default", "^production"]`, the result is `["my-project-input", "default", "^production"]`. **In objects**, a key of `"..."` set to `true` spreads base properties at that position. Keys defined after `"..."` override base values; keys defined before `"..."` can be overridden by base values. ```json // project.json { "targets": { "build": { "options": { "env": { "MY_VAR": "my-value", "...": true } } } } } ``` Nx processes target configuration down to `options[x]` and `configurations[x][y]`; values below that are opaque to the merge pipeline. Spread is therefore resolved at these levels: | Level | Example | | -------------------------------------------------- | --------------------------------------------------------------------------- | | Target root | `"build": { "dependsOn": ["lint"], "...": true }` | | `inputs`, `outputs`, `dependsOn`, `syncGenerators` | `"inputs": ["my-input", "..."]` | | `options` | `"options": { "...": true, "outputPath": "dist/custom" }` | | `options[x]` | `"options": { "env": { "MY_VAR": "val", "...": true } }` | | `configurations` | `"configurations": { "my-config": { ... }, "...": true }` | | `configurations[x]` | `"configurations": { "prod": { "...": true, "sourceMap": false } }` | | `configurations[x][y]` | `"configurations": { "prod": { "env": { "MY_VAR": "val", "...": true } } }` | {% aside type="caution" title="Spread does not apply to deeply nested options" %} Because `options[x]` and `configurations[x][y]` are the innermost levels Nx inspects, a spread token nested any deeper has no effect. For example, `options.webpack = { "...": true }` works (the spread is at `options[x]`), but `options.webpack.plugins = { "...": true }` is ignored because the spread sits inside the opaque value Nx assigns to `options.webpack`. {% /aside %} {% aside type="note" title="Spread does not apply to `tags` and `implicitDependencies`" %} `tags` and `implicitDependencies` always merge across configuration sources — later sources contribute additional entries rather than replacing earlier ones. The spread token is neither needed nor supported for these properties. {% /aside %} ### Target metadata You can add additional metadata to be attached to a target. For example, you can provide a description stating what the target does: ```jsonc // project.json { "targets": { "build": { "metadata": { "description": "Build the application for production", }, }, }, } ``` ## Project metadata The following properties describe the project as a whole. ### tags You can annotate your projects with `tags` as follows: {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```jsonc // package.json { "name": "mylib", "nx": { "tags": ["scope:myteam"], }, } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // project.json { "root": "libs/mylib", "tags": ["scope:myteam"], } ``` {% /tabitem %} {% /tabs %} You can [configure lint rules using these tags](/docs/features/enforce-module-boundaries) to, for instance, ensure that libraries belonging to `myteam` are not depended on by libraries belong to `theirteam`. ### implicitDependencies Nx uses powerful source-code analysis to figure out your workspace's project graph. Some dependencies cannot be deduced statically, so you can set them manually like this. The `implicitDependencies` property is parsed with the [minimatch](https://github.com/isaacs/minimatch) library, so you can review that syntax for more advanced use cases. {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```jsonc // package.json { "name": "mylib", "nx": { "implicitDependencies": ["anotherlib"], }, } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // project.json { "root": "libs/mylib", "implicitDependencies": ["anotherlib"], } ``` {% /tabitem %} {% /tabs %} You can also remove a dependency as follows: {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```jsonc // package.json { "name": "mylib", "nx": { "implicitDependencies": ["!anotherlib"] # regardless of what Nx thinks, "mylib" doesn't depend on "anotherlib" } } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // project.json { "root": "libs/mylib", "implicitDependencies": ["!anotherlib"] # regardless of what Nx thinks, "mylib" doesn't depend on "anotherlib" } ``` {% /tabitem %} {% /tabs %} An implicit dependency could also be a glob pattern: {% tabs syncKey="project-config-file" %} {% tabitem label="package.json" %} ```jsonc // package.json { "name": "mylib", "nx": { "implicitDependencies": ["shop-*"] # "mylib" depends on all projects beginning with "shop-" } } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // project.json { "root": "libs/mylib", "implicitDependencies": ["shop-*"] # "mylib" depends on all projects beginning with "shop-" } ``` {% /tabitem %} {% /tabs %} ### Release The `release` property allows project-level overrides for the [nx release](/docs/features/manage-releases) command. This is particularly useful for configuring Docker image publishing. #### Docker configuration {% badge text="experimental" /%} {% aside type="caution" title="Experimental Feature" %} Docker support in Nx is currently experimental and may undergo breaking changes without following semantic versioning. {% /aside %} Projects with Docker images can override the global Docker configuration: ```jsonc // project.json { "name": "api", "release": { "docker": { // Override the repository name for this specific project "repositoryName": "acme/api", }, }, } ``` The `repositoryName` specified here will override any global or group-level Docker repository configuration when publishing this project's Docker image. ### Metadata You can add additional metadata to be attached to the project. For example, you can provide a description for your project: ```jsonc // project.json { "name": "admin", "metadata": { "description": "This is the admin application", }, } ``` ## Including package.json files as projects in the graph Any `package.json` file that is referenced by the `workspaces` property in the root `package.json` file will be included as a project in the graph. If you are using Lerna, projects defined in `lerna.json` will be included. If you are using pnpm, projects defined in `pnpm-workspace.yml` will be included. If you want to ignore a particular `package.json` file, exclude it from those tools. For example, you can add `!packages/myproject` to the `workspaces` property. ### Ignoring package.json scripts Nx merges `package.json` scripts with any targets defined in `project.json`. If you only wish for some scripts to be used as Nx targets, you can specify them in the `includedScripts` property of the project's `package.json`. ```json // packages/my-library/package.json { "name": "my-library", "version": "0.0.1", "scripts": { "build": "tsc", "postinstall": "node ./tasks/postinstall" }, "nx": { "includedScripts": ["build"] } } ``` --- ## React For setup and guides, see [Nx with React](/docs/technologies/react/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/React" /%} --- ## Nx Release Schedule and Support Policy {% aside type="note" title="Looking for guidance on releasing your projects with Nx Release?" %} The releases of the Nx tooling itself. If you are looking for guidance on releasing your project with Nx Release, [see the documentation on managing releases](/docs/features/manage-releases). {% /aside %} The `nx` package, and all packages under the `@nx` namespace which live alongside each other in the [https://github.com/nrwl/nx](https://github.com/nrwl/nx) repository, are released together in lockstep. You should always use matching versions of the `nx` package and the `@nx` packages, e.g. `nx@23.1.0` and `@nx/js@23.1.0` should be used together. Major Nx versions are released as the _latest_ every six months, typically around April and October. After each major version release, the _previous_ major version moves to long-term support (LTS) for 12 months, after which it becomes unsupported. Each major version has 18 total months of support from when it is first released to when it falls out of LTS. Users should use `nx migrate` to ensure that they stay on a supported version. ### Major, minor, and patch versions - **Patch** versions include security or bug fixes, and are released as needed. - **Minor** versions include new features and fixes, and are released less frequently. It is a good idea to regularly update to the latest minor version. - **Major** versions may contain [breaking changes](#breaking-changes-and-migration-path), and are released twice a year. ## Supported versions The following are the currently supported major versions of Nx. | Version | Support Type | Release Date | | :-----: | :----------: | :----------: | | v23 | Current | 2026-06-16 | | v22 | LTS | 2025-10-22 | | v21 | LTS | 2025-05-05 | ### Current vs LTS The current version of Nx will receive new features as well as any fixes to any unintentional behavior. When a new major version of Nx is released, previous versions will go into LTS. LTS versions of Nx will receive security patches as well as critical fixes. ## Deprecation policy When the Nx team intends to remove an API or feature, it will be marked as _deprecated_. Deprecation warnings can surface in documentation, terminal output, or in TSDoc. The deprecated API will remain functional for a whole major version, after which they will be removed. For example, if a feature is deprecated in v21.1.0, it will be removed in v23.0.0 (two major versions later). ## Breaking changes and migration path Breaking changes, including the removal of deprecated APIs, will be highlighted under _Breaking Changes_ in the [changelog](https://nx.dev/changelog). Whenever possible, the Nx team will provide automatic migrations through [`nx migrate`](/docs/reference/nx-commands#nx-migrate). --- ## Rollup For setup and guides, see [Nx with Rollup](/docs/technologies/build-tools/rollup/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Rollup" /%} --- ## Rsbuild For setup and guides, see [Nx with Rsbuild](/docs/technologies/build-tools/rsbuild/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Rsbuild" /%} --- ## Rspack For setup and guides, see [Nx with Rspack](/docs/technologies/build-tools/rspack/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Rspack" /%} --- ## Storybook For setup and guides, see [Nx with Storybook](/docs/technologies/test-tools/storybook/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Storybook" /%} --- ## Telemetry Nx collects usage data to help improve the developer experience. Telemetry is completely optional and can be disabled at any time. ## Why Nx collects telemetry Telemetry helps the Nx team understand how Nx is used in practice. This data guides decisions about which features to prioritize, identifies common issues, and helps improve performance. No project-specific or personally identifiable information is collected. ## What is collected Nx collects general usage data in the following categories: - **Command usage**: which Nx commands are run (e.g., `build`, `test`, `generate`), how long they take, and how many tasks were executed or cached. - **Environment info**: operating system, CPU architecture, Node.js version, package manager and version, Nx version, and whether the command runs in CI. - **General configuration**: whether [Nx Cloud](https://nx.dev/nx-cloud) is connected. ## What about sensitive data? Nx does **not** collect any metrics that may contain sensitive data. This includes, but is not limited to: environment variables, personally identifiable information, file paths, contents of files, logs, or git remote information. For more information, see the [privacy policy](https://cloud.nx.app/privacy). ## How to opt out When you first run an Nx command in an interactive terminal, Nx asks whether you'd like to share usage data. If you decline, telemetry is disabled for that workspace. You can also disable telemetry at any time by setting `analytics` to `false` in your `nx.json`: ```json // nx.json { "analytics": false } ``` In CI environments, Nx does not prompt for telemetry. Telemetry only runs in CI if `analytics` is explicitly set to `true` in `nx.json`. ## How to re-enable To re-enable telemetry, set `analytics` to `true` in your `nx.json`: ```json // nx.json { "analytics": true } ``` ## Nx Console telemetry The Nx Console editor extensions (VS Code and JetBrains) collect their own telemetry, separate from the Nx CLI. For more information, see [Nx Console telemetry](/docs/kb/console-telemetry). --- ## TypeScript For setup and guides, see [Nx with TypeScript](/docs/technologies/typescript/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/TypeScript" /%} --- ## Vite For setup and guides, see [Nx with Vite](/docs/technologies/build-tools/vite/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Vite" /%} --- ## Vitest For setup and guides, see [Nx with Vitest](/docs/technologies/test-tools/vitest/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Vitest" /%} --- ## Vue For setup and guides, see [Nx with Vue](/docs/technologies/vue/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Vue" /%} --- ## Webpack For setup and guides, see [Nx with Webpack](/docs/technologies/build-tools/webpack/introduction). The pages below are the API reference. {% sidebar_group_cards group="Reference/Webpack" /%} # Knowledge Base --- ## Nx CLI and CI Access Tokens {% youtube src="https://youtu.be/vBokLJ_F8qs" title="Configure CI access tokens" /%} The permissions and membership define what developers can access on [nx.app](https://cloud.nx.app?utm_source=nx-dev&utm_medium=website&utm_campaign=access-tokens), but they don't affect what happens when you run Nx commands in CI. To manage that, you need to provision CI access tokens in your workspace settings, under the `Access Control` tab. Learn more about [cache security best practices](/docs/kb/cache-security). {% aside type="tip" title="Quickest path: use recommended settings" %} The **Access Control** tab in your Nx Cloud workspace has a **Use recommended settings** button that generates the right CI access tokens, requires developer logins for cache reads, etc. ![](../../../assets/nx-cloud/access-control-settings.avif) {% /aside %} ## Access types {% aside type="caution" title="Use Caution With Read-Write Tokens" %} The `read-write` tokens allow full write access to your remote cache. They should only be used in trusted environments. {% /aside %} There are currently two (2) types of CI Access Token for Nx Cloud's runner that you can use with your workspace. Both support distributed task execution and allow Nx Cloud to store metadata about runs. - `read-only` - `read-write` ### Read only access The `read-only` access tokens can only read from the global remote cache. Task results produced with this type of access token will be stored in an isolated remote cache accessible _only_ by that specific branch in a CI context, and cannot influence the global shared cache. The isolated remote cache produced with a `read-only` token is accessible to all machines or agents in the same CI execution, enabling cache sharing during distributed task execution. ### Read & write access The `read-write` access tokens allow task results to be stored in the remote cache for other machines or CI pipelines to download and replay. This access level should only be used for trusted environments such as protected branches within your CI Pipeline. ## Setting CI access tokens You can configure an access token in CI by setting the `NX_CLOUD_ACCESS_TOKEN` environment variable. The `NX_CLOUD_ACCESS_TOKEN` takes precedence over any authentication method in your `nx.json`. We recommend setting up a `read-write` token for you protected branches in CI and a `read-only` token for unprotected branches. You can leverage your CI provider's environment variables management to accomplish this. ### Azure DevOps Azure DevOps provides various [mechanisms to limit access to secrets](https://learn.microsoft.com/en-us/azure/devops/pipelines/security/secrets?view=azure-devops#limit-access-to-secret-variables). We'll be using _Variable groups_ in this process, but you can achieve the same result leveraging [Azure Key Vault](https://learn.microsoft.com/en-us/azure/key-vault/general/overview). 1. In your project, navigate to Pipelines > Library. ![Variable group settings page](../../../assets/nx-cloud/ado-library-start.avif) 2. Create a new _Variable group_ called _protected_. - If you already have a variable group for protected environments, we recommend reusing that variable group. 3. Add the `NX_CLOUD_ACCESS_TOKEN` environment variable with the `read-write` token from Nx Cloud. ![create protected variable group](../../../assets/nx-cloud/ado-protected-var-group.avif) 4. In _Pipeline permissions_, add your current pipeline configuration. ![variable group pipeline permission settings](../../../assets/nx-cloud/ado-pipeline-permission.avif) 5. In _Approvals and checks_, add a new _Branch control_ check. ![variable group branch control settings](../../../assets/nx-cloud/ado-add-branch-control.avif) 6. Create the _Branch control_ check with only allowing your protected branches and checking _Verify branch protection_ option. ![variable group branch control settings](../../../assets/nx-cloud/ado-protected-branch.avif) 7. Create another variable group called _unprotected_. 8. Add the `NX_CLOUD_ACCESS_TOKEN` environment variable with the `read-only` token from Nx Cloud. 9. In _Pipeline permissions_, add your current pipeline configuration. 10. In _Approvals and checks_, add a new _Branch control_ check with the `*` wildcard for branches and leaving _Verify branch protection_ unchecked. ![unprotected variable group settings](../../../assets/nx-cloud/ado-unprotected-branch.avif) 11. Now you should see 2 _Variable groups_ for _protected_ and _unprotected_ usage. ![completed variable group setup](../../../assets/nx-cloud/ado-library-end.avif) 12. Update your pipeline to include the 2 variable groups, with conditional access for the _protected_ variable group. Example usage: ```yaml {% meta="{3-5}" %} // azure-pipelines.yml variables: - group: unprotected - ${{ if eq(variables['Build.SourceBranchName'], 'main') }}: - group: protected ``` {% aside type="tip" title="Can't someone change the variable group?" %} Since we use the _Verify branch protection_ option, CI can only read the variable when running in a protected branch. If a developer tries to edit the pipeline to use the _protected_ variable group, the pipeline will error out since permissions require running in on a protected branch. Take caution though, if you allow team members to have direct write access to a protected branch, then they could modify the pipeline to write to the nx cache without having a code review first. {% /aside %} ### BitBucket Cloud BitBucket Cloud supports setting environment variables per environment called _Deployment variables_. You can read the [official BitBucket Pipelines documentation](https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/#Deployment-variables) for more details. 1. In your repository, navigate to the _Repository settings_ > _Deployment_. 2. Select an environment you have configured for protected branches, or create a new one and protect your primary branches. - Note: selecting branch protection rules is a premium feature of BitBucket Cloud. ![Use deployments variables to provide protected environment variable access](../../../assets/nx-cloud/bitbucket-deployment-env.avif) 3. Set the environment variable `NX_CLOUD_ACCESS_TOKEN` with the `read-write` token from Nx Cloud. 4. Navigate to the _Repository settings_ > _Repository variables_ tab and set the variable `NX_CLOUD_ACCESS_TOKEN` with the `read-only` token from Nx Cloud. ![add read-only Nx Cloud access token to bitbucket](../../../assets/nx-cloud/bitbucket-repo-vars.avif) 5. Update the `bitbucket-pipelines.yml` file to include the deployment name mentioned in step 2. Example usage: ```yaml {% meta="{7}" %} // bitbucket-pipelines.yml pipelines: branches: main: - step: name: 'main checks' deployment: Production ... ``` ### CircleCI Circle CI allows creating _contexts_ and restricting those based on various rules. You can read the [official CircleCI documentation](https://circleci.com/docs/contexts/#restrict-a-context) for more details. 1. In your organization, navigate to _Organization settings_ > _Contexts_ and create a new context. - If you already have a context for protected environments, we recommend reusing that context. ![create a new context for protected environments](../../../assets/nx-cloud/circle-new-context.avif) 2. Click on _Add Expression Restriction_ that restricts the context to protected branches only such as only the `main` branch, e.g., `pipeline.git.branch == "main"`. ![restrict context to protected branches](../../../assets/nx-cloud/circle-expression-restriction.avif) 3. Click on _Add Environment Variable_ and add the `NX_CLOUD_ACCESS_TOKEN` environment variable with the `read-write` token from Nx Cloud. 4. Back on the organization home page, navigate to your projects, then view the pipeline settings. 5. Navigate to _Environment Variables_ and click _Add Environment Variable_ and add the `NX_CLOUD_ACCESS_TOKEN` environment variable with the `read-only` token from Nx Cloud. ![add read-only Nx Cloud access token to circleci](../../../assets/nx-cloud/circle-new-env-var-token.avif) 6. Update your pipeline to include steps where you want to write to the nx cache with the correct contexts. Example usage: ```yaml {% meta="{11-20}" %} // .circleci/config.yml jobs: run-tests-protected: - ... run-tests-prs: - ... workflows: my-workflow: jobs: - run-tests-protected: context: - protected-branches filters: branches: only: main - run-tests-prs: filters: branches: ignore: main ``` ### GitHub Actions GitHub allows specifying different secrets for each environment, where an environment can be on a specific branch. You can read the [official GitHub Actions documentation](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-an-environment) for more details. 1. In your repository, navigate to Settings tab. 2. Click on "Environments" and create an environment for your protected branches. - Typically, organizations already have some kind of 'release' or 'protected' environments that can be leveraged. - If you do not have any protected branches, it's recommended to make at least your _default_ branch a protected branch i.e., `main`/`master`. 3. Add a restriction for how the environment will be applied, and apply to all protected branches. ![Select protected branches for the environment restriction configuration](../../../assets/nx-cloud/github-select-protected-branches.avif) 4. Add the `read-write` access token with the name `NX_CLOUD_ACCESS_TOKEN` to your environment. 5. Click the _Secrets and variables_ > _Actions_ tab in the sidebar. 6. Add the `read-only` access token with the name `NX_CLOUD_ACCESS_TOKEN` to the repository secrets. 7. Now you should see 2 secrets where 1 is a part of the protected environment and the other is the default repository secrets. ![overview of GitHub Action secret configuration settings with environments set](../../../assets/nx-cloud/github-secrets-settings.avif) Example usage: ```yaml {% meta="{4-5}" %} // .github/workflows/ci.yml name: CI env: NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} jobs: main: runs-on: ubuntu-latest steps: ... ``` ### GitLab GitLab allows creating variables scoped to specific environments. You can read the [Official GitLab documentation](https://docs.gitlab.com/ci/environments/#limit-the-environment-scope-of-a-cicd-variable) for more details. 1. In your project, navigate to _Operate_ > _Environments_ and create a new environment. You do not need to fill out the External Url or GitLab agent. - Most projects already have a production/protected environments, so we recommend using this one if it's already defined. ![define gitlab environment for protected branches](../../../assets/nx-cloud/gitlab-new-environment.avif) 2. In your project, navigate to _Settings_ > _CI/CD_ tab and expand the _Variables_ section. 3. Click on _Add variable_ and fill in the following information: - Type: _Variable_ - Environments: _All_ - Visibility: _Masked and hidden_ - Flags: uncheck _Protected variable_ - Description: "read-only token for nx-cloud" - Key: `NX_CLOUD_ACCESS_TOKEN` - Value: Your `read-only` token from Nx Cloud 4. Click _Add variable_. ![add read-only Nx Cloud access token to gitlab](../../../assets/nx-cloud/gitlab-variable-settings-readonly.avif) 5. Click on _Add variable_ again and fill in the following information: - Type: _Variable_ - Environments: Your protected environment created in step 1 - Visibility: _Masked and hidden_ - Flags: check _Protected variable_ - Description: "read-write token for nx-cloud" - Key: `NX_CLOUD_ACCESS_TOKEN` - Value: Your `read-write` token from Nx Cloud 6. Click _Add variable_. ![add read-write Nx Cloud access token to gitlab](../../../assets/nx-cloud/gitlab-variable-settings-readwrite.avif) 7. Now you should see 2 secrets where 1 is a part of the protected & tagged to the environment and the other is not. ![GitLab project variable configuration screen](../../../assets/nx-cloud/gitlab-variable-setting.avif) 8. Update your pipeline to include steps where you want to write to the nx cache with the correct contexts. Example usage: ```yaml {% meta="{3-4}" %} // .gitlab-ci.yml : environment: name: ``` {% aside type="tip" title="Can't someone change the step environment?" %} Since we use the _Protected variable_ flag, CI can only read the variable when running in a protected branch. If a developer tries to edit the steps to run a PR in the environment with the `read-write` token will, then the token will not be populated in CI since their branch is not marked as protected. Take caution though, if you allow team members to have direct write access to a protected branch, then they could modify the steps to write to the nx cache without having a code review first. {% /aside %} ### Jenkins Jenkins configuration can be quite extensive making each Jenkins instance unique. Because of this we can only provide a minimal viable approach, but there can be multiple ways to provide scoped access tokens to your pipelines. The goal is to create two areas within Jenkins, where one is the _protected_ and the other is the _unprotected_. These specifically map to how you deem your branches should have read/write vs read permissions. We recommend making branches that developers cannot directly push to and require a code review to merge to, as the _protected_ branches, and the rest being _unprotected_. 1. Minimally, this can be achieved via the following Jenkins plugins: - [Folders](https://plugins.jenkins.io/cloudbees-folder/), [Credentials](https://plugins.jenkins.io/credentials/), [Credentials Binding](https://plugins.jenkins.io/credentials-binding/) 2. Create a folder for the _unprotected_ and _protected_ pipelines. - The names can be anything that makes sense for your organization, such as _releases_ or _PRs_ etc. 3. Go into the _unprotected_ folder and create a credential for `NX_CLOUD_ACCESS_TOKEN` with the `read-only` token from Nx Cloud. 4. Go into the _protected_ folder and create a credential for `NX_CLOUD_ACCESS_TOKEN` with the `read-write` token from Nx Cloud. 5. Use the credential inside your pipeline `Jenkinsfile` with the Credential Binding plugin. Example usage: ```groovy {% meta="{7-8}" %} // Jenkinsfile pipeline { agent any stages { stage('Build') { steps { withCredentials([string(credentialsId: 'NX_CLOUD_ACCESS_TOKEN', variable: 'NX_CLOUD_ACCESS_TOKEN')]) { sh 'echo "Nx Cloud access token is now set in this context"' } } } } } ``` ### Legacy methods of setting CI access tokens #### Using CI access tokens in nx.json We **do not recommend** that you commit an access token to your repository but older versions of Nx do support this and if you open your `nx.json`, you may see something like this: ```json { "nxCloudAccessToken": "SOMETOKEN" } ``` {% aside type="caution" title="Nx Cloud authentication is changing" %} From Nx 19.7 new workspaces are connected to Nx Cloud with a property called `nxCloudId` instead, and we recommend developers use [`nx login`](/docs/reference/nx-cloud-cli#nx-cloud-login) to provision their own local [personal access tokens](/docs/kb/personal-access-tokens) for user based authentication. {% /aside %} #### Using `nx-cloud.env` You can set an environment variable locally via the `nx-cloud.env` file. Nx Cloud CLI will look in this file to load custom configuration like `NX_CLOUD_ACCESS_TOKEN`. These environment variables will take precedence over the configuration in `nx.json`. --- ## Add Language Support to Nx {% llm_copy_prompt title="Let an AI agent build the plugin" %} Build an Nx plugin that adds support for my toolchain to this workspace, following the structure below. 1. Ask me the following before writing any code: - Which toolchain to support, and which manifest file marks a project (for example `pyproject.toml`, `go.mod`, `Cargo.toml`). - Which targets to infer, and the command behind each one. - How manifests declare dependencies on each other, by package name, by relative path, or both. - Roughly how many projects the workspace has. - Which Nx versions the plugin has to support, since the disk caching helpers live in a lower-stability entry point. 2. Check for a root `package.json`. The plugin is a Node package, so the generator has nothing to write to without one. If it is missing, initialize it with whichever package manager the repository already uses, or with `npm init -y` if there is no JavaScript in the repository at all. Do not run `npm init` against a manifest that already exists, because it rewrites fields such as `type` and `license`. Once the manifest is there, `nx init` installs Nx through the detected package manager, so no separate install step is needed. 3. Generate the plugin with `npx nx add @nx/plugin` and `npx nx g plugin packages/`, in a directory the toolchain does not treat as a workspace member. 4. Export `createNodes` from the plugin entry point: glob the manifest, and for each match return a project with cacheable targets that run the toolchain's own commands. Return `{}` for a manifest that describes an aggregate root rather than a package, and infer only the targets each project can run. 5. Export `createDependencies`: map dependency declarations in the manifests to workspace projects and emit static dependencies. 6. Register the plugin in `nx.json`, then verify with `NX_DAEMON=false NX_CACHE_PROJECT_GRAPH=false nx show project --json`, `nx graph --file=graph.json`, and one real run of every target you inferred. 7. Once the graph is correct, move on to the second phase and optimize it. Measure with `NX_PERF_LOGGING=true`, then work through the performant project graph plugins guide, including disk caching through `@nx/devkit/internal`. Tell me if the Nx version range from step 1 rules those helpers out. Page: {pageUrl} {% /llm_copy_prompt %} Nx plugins package knowledge about a toolchain so every project doesn't need to recreate the same integration. A plugin can contribute projects and tasks to the [project graph](/docs/features/explore-graph), dependencies between projects, [code generators](/docs/features/generate-code), and migrations. {% aside type="note" title="You don't need a plugin to get started" %} A [`project.json`](/docs/reference/project-configuration) file configures projects and tasks for any language without a plugin. A plugin lets Nx read the toolchain configuration files you already maintain instead. {% /aside %} A plugin that adds [multi-language support](/docs/features/multi-language-support) to a workspace does three things: 1. Declares a glob for the configuration files that mark a project (for example `**/pyproject.toml`). 2. Turns each matching file into a project with tasks (`createNodes`). 3. Connects projects with dependencies read from those files (`createDependencies`). The examples below use Python projects managed with [uv](https://docs.astral.sh/uv/), but the same structure applies to any toolchain, so substitute your manifest and commands as you follow along. ## Set up a plugin A plugin is a module that exports `createNodes` and, optionally, `createDependencies`. It ships as a Node package, so the workspace needs a root `package.json` first. Scaffold the plugin with: {% tabs %} {% tabitem label="In an existing Nx workspace" %} ```shell npx nx add @nx/plugin npx nx g plugin packages/nx-uv ``` {% /tabitem %} {% tabitem label="In a repository without Nx" %} ```shell # Only if there is no root package.json yet. npm init -y npx nx init npx nx add @nx/plugin npx nx g plugin packages/nx-uv ``` {% /tabitem %} {% tabitem label="In a new plugin workspace" %} ```shell npx create-nx-plugin nx-uv ``` {% /tabitem %} {% /tabs %} {% aside type="note" title="Watch for your toolchain's workspace globs" %} Some toolchains claim directories through a member glob, and a TypeScript plugin dropped inside one breaks them. A uv root that globs `packages/*` reports `missing a pyproject.toml` once the plugin lands in `packages/nx-uv`. Exclude that path from the glob, or generate the plugin where the glob doesn't reach. {% /aside %} The plugin entry point exports the two functions, each tied to the glob and options you'll fill in over the next sections: ```ts // packages/nx-uv/src/index.ts import { CreateDependencies, CreateNodes } from '@nx/devkit'; // Options users can set in nx.json export interface UvPluginOptions { buildTargetName?: string; testTargetName?: string; } // A tuple of the file glob and a function that creates projects and tasks export const createNodes: CreateNodes = [ '**/pyproject.toml', async (configFiles, options, context) => { // Covered in "Create projects and tasks" }, ]; // Connects projects, reading the same configuration files export const createDependencies: CreateDependencies = ( options, context ) => { // Covered in "Create dependencies between projects" }; ``` Register the plugin in `nx.json` so Nx calls it when computing the project graph: ```jsonc // nx.json { "plugins": [ { "plugin": "nx-uv", "options": { "buildTargetName": "build", "testTargetName": "test", }, }, ], } ``` The `plugin` string must match the `name` in the plugin's `package.json`, so a scoped name like `@acme/nx-uv` works the same way. The `options` object is passed to both functions, and its shape is yours to define. First-party plugins use it to let users rename the targets the plugin creates. ## Choose which files to glob The glob is the entry point for everything else. Match the file that marks the root of a project in your toolchain, usually the manifest or configuration file the tool itself reads: | Toolchain | Files to glob | | -------------- | ------------------- | | Python + uv | `**/pyproject.toml` | | Go | `**/go.mod` | | Rust + Cargo | `**/Cargo.toml` | | PHP + Composer | `**/composer.json` | Keep the glob narrow. Matching `**/*.py` would call your plugin for every source file, while matching `**/pyproject.toml` calls it once per project. Nx already ignores everything in `.gitignore` and `.nxignore` when resolving the glob. ## Create projects and tasks `createNodes` is a tuple of the glob and a function that receives all matching files in one batch. For each file, return the project it defines: the project root, a name, and the targets (tasks) that Nx should register. Wrap your per-file logic in `createNodesFromFiles`, which handles fanning out over the batch and error reporting for you: ```ts // packages/nx-uv/src/index.ts import { createNodesFromFiles, CreateNodes, CreateNodesContext, TargetConfiguration, } from '@nx/devkit'; import { existsSync, readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { parse } from 'smol-toml'; // any TOML parser works export const createNodes: CreateNodes = [ '**/pyproject.toml', async (configFiles, options, context) => { return await createNodesFromFiles( createNodesInternal, configFiles, options, context ); }, ]; function createNodesInternal( configFilePath: string, options: UvPluginOptions | undefined, context: CreateNodesContext ) { const projectRoot = dirname(configFilePath); const pyproject = parse( readFileSync(join(context.workspaceRoot, configFilePath), 'utf-8') ); // A uv workspace root lists its members without declaring a package of its own. const name = pyproject.project?.name; if (!name) { return {}; } const buildTargetName = options?.buildTargetName ?? 'build'; const testTargetName = options?.testTargetName ?? 'test'; const testTarget: TargetConfiguration = { command: 'uv run pytest', options: { cwd: projectRoot }, cache: true, inputs: [ '{projectRoot}/**/*.py', '{projectRoot}/pyproject.toml', '{workspaceRoot}/uv.lock', ], metadata: { technologies: ['python'], description: 'Run pytest via uv', }, }; const targets: Record = { [testTargetName]: testTarget, }; // Only a project that declares a build backend can produce a distribution. if (pyproject['build-system']) { targets[buildTargetName] = { command: 'uv build --out-dir dist', options: { cwd: projectRoot }, cache: true, inputs: ['{projectRoot}/**/*.py', '{projectRoot}/pyproject.toml'], outputs: ['{projectRoot}/dist'], metadata: { technologies: ['python'], description: 'Build a wheel and source distribution via uv', }, }; } return { projects: { [projectRoot]: { name, targets } } }; } ``` With this in place, `nx test ` runs pytest for any project that has a `pyproject.toml`, with caching configured once in the plugin instead of per project. `nx build ` works the same way for the projects that declare a build backend. A few things to know about the returned configuration: - **The config file is the project marker.** For language plugins, the manifest marks the project, so there's no need to check for `project.json` or `package.json` the way JS tooling plugins do. - **Read the manifest before inferring.** Manifests within one workspace are not uniform. A root that only aggregates members is not a project at all, plenty of projects have no build backend, and the project name can live in a toolchain-specific table rather than the standard one. - **Targets are regular Nx targets.** `command`, `cache`, `inputs`, `outputs`, and `dependsOn` behave exactly as they do in `project.json`. Paths must start with `{projectRoot}` or `{workspaceRoot}`, and set `outputs` for tasks that produce files so Nx can restore them from cache. See the [inputs reference](/docs/reference/inputs) for details. - **Consider the lockfile in `inputs`.** Hashing the lockfile (`uv.lock`, `go.sum`, `Cargo.lock`) re-runs tasks when dependency versions change, but any lockfile change busts the cache for every project. The built-in JS support avoids this by hashing only the external packages each project uses, which is worth copying if your lockfile churns often. - **Returned configuration is merged, not final.** Users can override anything your plugin infers by adding a `project.json` file to the project or `targetDefaults` in `nx.json`. Plugin-inferred values have the lowest priority. - **The `metadata` fields** show up in the project details view (`nx show project `), which is where users debug what your plugin inferred. ## Create dependencies between projects `createDependencies` tells Nx how projects relate. `nx affected`, task ordering, and the graph visualization are only as accurate as these edges. Read the dependency information your toolchain already has. For uv workspaces, each member's `pyproject.toml` lists its dependencies by package name: ```toml # packages/api/pyproject.toml [project] name = "api" dependencies = ["shared-utils"] [tool.uv.sources] shared-utils = { workspace = true } ``` Map each package name to the Nx project that declares it, then emit a dependency for every match: ```ts // packages/nx-uv/src/index.ts import { DependencyType, RawProjectGraphDependency, validateDependency, } from '@nx/devkit'; export const createDependencies: CreateDependencies = ( options, context ) => { // Map python package names to nx project names const packageToProject = new Map(); const projectPyprojects = new Map(); for (const [projectName, project] of Object.entries(context.projects)) { const pyprojectPath = join(project.root, 'pyproject.toml'); if (!existsSync(join(context.workspaceRoot, pyprojectPath))) { continue; } const config = parse( readFileSync(join(context.workspaceRoot, pyprojectPath), 'utf-8') ); if (!config.project?.name) { continue; } packageToProject.set(config.project.name, projectName); projectPyprojects.set(projectName, { config, path: pyprojectPath }); } const results: RawProjectGraphDependency[] = []; for (const [projectName, { config, path }] of projectPyprojects) { for (const dep of config.project.dependencies ?? []) { const target = packageToProject.get(dep); if (!target) continue; // external package, not a workspace project const dependency: RawProjectGraphDependency = { source: projectName, target, sourceFile: path, type: DependencyType.static, }; validateDependency(dependency, context); results.push(dependency); } } return results; }; ``` {% aside type="note" title="Dependencies declared by path" %} Some toolchains point at a sibling directory instead of naming a package, as a Poetry path dependency does. Resolve those against the directory of the manifest that declares them, then match the result to a project root. {% /aside %} Manifest files like `pyproject.toml`, `go.mod`, or `Cargo.toml` cover most toolchains, since the dependency information is already written down in one place per project. If your language only expresses dependencies through import statements in source code, parse those files instead, and use `context.filesToProcess` to limit the work to files that changed since the last graph computation. Accurate parsing is often easier in the language itself than in TypeScript. The first-party Gradle, Maven, and .NET plugins all spawn the toolchain once for the whole workspace, have it write a JSON report of projects and dependencies, and build both nodes and dependencies from that report. Nx always calls `createNodes` before `createDependencies`, so the report can be produced once and shared between the two. ## Verify your plugin While developing, two environment variables matter: ```shell # The daemon caches plugin code, so restart it to pick up changes. NX_DAEMON=false NX_CACHE_PROJECT_GRAPH=false npx nx show projects ``` Check the results of your plugin directly: - `nx show project --json` prints the full inferred configuration, including targets your plugin created. - `nx graph --file=graph.json` writes the projects and the dependencies you emitted to a file rather than opening a browser, which works over SSH and in CI. Pass an `.html` path for the interactive view. - `nx ` runs a target you inferred. Run one of every kind you created, because a target that reads correctly in `nx show project` can still fail the moment its command executes. For automated coverage, call your functions directly in a unit test with a fixture directory as the workspace root, and snapshot the returned projects and dependencies: ```ts // packages/nx-uv/src/index.spec.ts import { createNodes } from './index'; const [, createNodesFn] = createNodes; it('creates a project for each pyproject.toml', async () => { const results = await createNodesFn( ['packages/api/pyproject.toml'], { testTargetName: 'test' }, { workspaceRoot: fixtureDirectory, nxJsonConfiguration: {} } ); expect(results).toMatchSnapshot(); }); ``` The first-party plugins that spawn an external tool mock that step in unit tests and feed in a recorded report fixture, so the tests stay fast and deterministic. Plugins scaffolded with `create-nx-plugin` also include an e2e setup that publishes the plugin to a local registry, installs it into a fresh workspace, and asserts on `nx show project --json` output. ## Optimize graph creation `createNodes` runs before every task, so measure what your plugin costs once the graph is correct: ```shell NX_PERF_LOGGING=true NX_DAEMON=false npx nx show projects ``` Nx prints a timing per step, including `:createNodes` and `Load Nx Plugin: `. Keep the daemon off, or the plugin timings never reach your terminal. From there, work through the patterns that bring those numbers down: - Cache computed targets to disk. This uses a handful of helpers from `@nx/devkit/internal`, which nearly every first-party plugin depends on. They sit at a lower stability tier than `@nx/devkit` and can change between versions, and some will be promoted to the public API later. - Load and parse the manifests in one parallel batch. - Keep the configuration you return deterministic. - Keep top-level imports light. [Write a performant project graph plugin](/docs/kb/performant-project-graph-plugins) has the code for each. --- ## Adding Images, Fonts, and Files With Nx, you can **`import` assets directly from your TypeScript/JavaScript code**. ```typescript frame="none" import React from 'react'; import logo from './logo.png'; const Header = () => Logo; export default Header; ``` This import will be replaced by a string of the image path when the application builds. To reduce the number of network requests, if the image file size is less than 10 kB, then the image will be inlined using [data URI](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) instead of a path. This works in CSS files as well. ```css frame="none" .logo { background-image: url(./logo.png); } ``` ## Adding SVGs SVG images can be imported using the method described previously. Alternatively, you can import SVG images as React components using [SVGR](https://react-svgr.com/). ```typescript frame="none" import React from 'react'; import { ReactComponent as Logo } from './logo.svg'; const Header = () => ; export default Header; ``` This method of import allow you to work with the SVG the same way you would with any other React component. You can style it using CSS, [styled-components](https://styled-components.com/), [TailwindCSS](https://tailwindcss.com/), etc. The SVG component accepts a `title` prop, as well as any other props that the `svg` element accepts. {% aside type="caution" title="Manual Configuration Required" %} As of Nx 22, SVGR is removed for Webpack and Next.js, and deprecated for Rspack (will be removed in Nx 23). Manual configuration is required—see the sections below. {% /aside %} ### SVGR for Webpack and Rspack To import SVGs as React components with Webpack or Rspack, you need to install the `@svgr/webpack` package and configure it manually. For detailed configuration options, refer to the [official SVGR webpack documentation](https://react-svgr.com/docs/webpack/). First, install the required dependencies: {% tabs %} {% tabitem label="npm" %} ```shell npm add -D @svgr/webpack file-loader ``` {% /tabitem %} {% tabitem label="yarn" %} ```shell yarn add -D @svgr/webpack file-loader ``` {% /tabitem %} {% tabitem label="pnpm" %} ```shell pnpm add -D @svgr/webpack file-loader ``` {% /tabitem %} {% tabitem label="bun" %} ```shell bun add -D @svgr/webpack file-loader ``` {% /tabitem %} {% /tabs %} Then, configure your webpack config: ```javascript frame="none" // filename: webpack.config.js module.exports = { module: { rules: [ { test: /\.svg$/, issuer: /\.(js|ts|md)x?$/, use: [ { loader: '@svgr/webpack', options: { svgo: false, titleProp: true, ref: true, }, }, 'file-loader', ], }, ], }, }; ``` If you're using `composePlugins` with `withNx`, you can create a `withSvgr` helper function: {% aside type="caution" title="`composePlugins` and `withNx` are deprecated" %} The `@nx/webpack` `composePlugins` and `withNx` helpers are deprecated and will be removed in Nx v24. They still work in v23, but using them logs a deprecation warning. Prefer a standard webpack config using `NxAppWebpackPlugin` and run `nx g @nx/webpack:convert-to-inferred` to migrate. See the [webpack config guide](/docs/kb/webpack-config-setup) for details. {% /aside %} ```javascript frame="none" // filename: webpack.config.js const { composePlugins, withNx } = require('@nx/webpack'); function withSvgr(svgrOptions = {}) { const defaultOptions = { svgo: false, titleProp: true, ref: true, }; const options = { ...defaultOptions, ...svgrOptions }; return function configure(config) { // Remove existing SVG loader if present const svgLoaderIdx = config.module.rules.findIndex( (rule) => typeof rule === 'object' && typeof rule.test !== 'undefined' && rule.test.toString().includes('svg') ); if (svgLoaderIdx !== -1) { config.module.rules.splice(svgLoaderIdx, 1); } // Add SVGR loader config.module.rules.push({ test: /\.svg$/, issuer: /\.(js|ts|md)x?$/, use: [ { loader: require.resolve('@svgr/webpack'), options, }, { loader: require.resolve('file-loader'), options: { name: '[name].[hash].[ext]', }, }, ], }); return config; }; } module.exports = composePlugins(withNx(), withSvgr()); ``` ### SVGR for Next.js As of Nx 22, SVGR support for Next.js is no longer included by default. You can configure it manually using the same approach as Webpack. For detailed information, refer to the [official SVGR documentation](https://react-svgr.com/docs/webpack/). First, install the required dependencies: {% tabs %} {% tabitem label="npm" %} ```shell npm add -D @svgr/webpack file-loader ``` {% /tabitem %} {% tabitem label="yarn" %} ```shell yarn add -D @svgr/webpack file-loader ``` {% /tabitem %} {% tabitem label="pnpm" %} ```shell pnpm add -D @svgr/webpack file-loader ``` {% /tabitem %} {% tabitem label="bun" %} ```shell bun add -D @svgr/webpack file-loader ``` {% /tabitem %} {% /tabs %} Then, update your `next.config.js`: ```javascript frame="none" // filename: next.config.js module.exports = { webpack(config) { // Grab the existing rule that handles SVG imports const fileLoaderRule = config.module.rules.find((rule) => rule.test?.test?.('.svg') ); config.module.rules.push( // Reapply the existing rule, but only for svg imports ending in ?url { ...fileLoaderRule, test: /\.svg$/i, resourceQuery: /url/, // *.svg?url }, // Convert all other *.svg imports to React components { test: /\.svg$/i, issuer: fileLoaderRule.issuer, resourceQuery: { not: [...fileLoaderRule.resourceQuery.not, /url/] }, // exclude if *.svg?url use: ['@svgr/webpack'], } ); // Modify the file loader rule to ignore *.svg, since we have it handled now. fileLoaderRule.exclude = /\.svg$/i; return config; }, // ...other config }; ``` If you're using `composePlugins` with `withNx`, you can update your `next.config.js` to use a `withSvgr` helper function: ```javascript frame="none" // filename: next.config.js const { composePlugins, withNx } = require('@nx/next'); function withSvgr(svgrOptions = {}) { const defaultOptions = { svgo: false, titleProp: true, ref: true, }; const options = { ...defaultOptions, ...svgrOptions }; return function configure(config) { // Remove existing SVG loader if present const svgLoaderIdx = config.module.rules.findIndex( (rule) => typeof rule === 'object' && typeof rule.test !== 'undefined' && rule.test.toString().includes('svg') ); if (svgLoaderIdx !== -1) { config.module.rules.splice(svgLoaderIdx, 1); } // Add SVGR loader config.module.rules.push({ test: /\.svg$/, issuer: /\.(js|ts|md)x?$/, use: [ { loader: require.resolve('@svgr/webpack'), options, }, { loader: require.resolve('file-loader'), options: { name: '[name].[hash].[ext]', }, }, ], }); return config; }; } const nextConfig = { // your Next.js config here }; module.exports = composePlugins(withNx(), withSvgr())(nextConfig); ``` ### SVGR for Vite To import SVGs as React components with Vite, you need to install the `vite-plugin-svgr` package. {% tabs %} {% tabitem label="npm" %} ```shell npm add -D vite-plugin-svgr ``` {% /tabitem %} {% tabitem label="yarn" %} ```shell yarn add -D vite-plugin-svgr ``` {% /tabitem %} {% tabitem label="pnpm" %} ```shell pnpm add -D vite-plugin-svgr ``` {% /tabitem %} {% tabitem label="bun" %} ```shell bun add -D vite-plugin-svgr ``` {% /tabitem %} {% /tabs %} Then, configure Vite as follows: ```javascript frame="none" meta="{ranges: '5,10-18'}" // filename: vite.config.ts /// import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import tsconfigPaths from 'vite-tsconfig-paths'; import svgr from 'vite-plugin-svgr'; export default defineConfig({ // ... plugins: [ svgr({ svgrOptions: { exportType: 'named', ref: true, svgo: false, titleProp: true, }, include: '**/*.svg', }), react(), tsconfigPaths(), // ... ], //... }); ``` --- ## Adding Nx to your Existing Project Nx works in any repository, not just a monorepo. A single project with a handful of `package.json` scripts is enough to benefit, and the setup leaves those scripts as they are. ```json // package.json { ... "scripts": { "build": "next build", "lint": "eslint ./src", "test": "node ./run-tests.js" } } ``` Run these through Nx and they pick up [caching](/docs/features/cache-task-results). Rerun a task without touching the project and Nx restores the previous result instead of doing the work again. ## Install Nx Run the following command: ```shell npx nx@latest init ``` `nx init` asks whether you want a minimum or a guided setup. **Minimum** makes the smallest possible change to your repository: - `nx.json` is created. - `nx` is added to your dev dependencies. - An existing `.gitignore` picks up the Nx cache directories. Nx does not create the file if it's missing. - `package.json` gets an `"nx": {}` property so Nx knows about the root project. Your scripts are untouched. `npm run build` still runs `next build`, and you opt tasks into caching yourself by setting `cache: true` in `targetDefaults`. **Guided** does the same and then asks a few questions. It asks which of your scripts are cacheable and what they output, and writes the matching `targetDefaults` for you: ```json // nx.json { "$schema": "./node_modules/nx/schemas/nx-schema.json", "targetDefaults": { "build": { "cache": true, "outputs": ["{projectRoot}/.next"] }, "lint": { "cache": true } } } ``` Once a task is marked cacheable, run it twice and the second run is restored from the cache: ```shell npx nx build npx nx build # instant, restored from the cache ``` `nx build` runs your `build` script through Nx, and that's the whole setup. Plugins, generators, `project.json` files, and Nx Cloud are all things you can add later, or never. ## Configure an existing script to run with Nx If you want to keep invoking a script through your package manager and still cache it, tell Nx about it. 1. Preface the script with `nx exec -- ` to have `npm run test` invoke the command with Nx. 2. Define caching settings. The `nx exec` command allows you to keep using `npm test` or `npm run test` (or other package manager's alternatives) as you're accustomed to. But still get the benefits of making those operations cacheable. Configuring the `test` script from the example above to run with Nx would look something like this: ```json // package.json { "name": "my-workspace", ... "scripts": { "build": "next build", "lint": "eslint ./src", "test": "nx exec -- node ./run-tests.js" }, ... "nx": { "targets": { "test": { "cache": "true", "inputs": [ "default", "^default" ], "outputs": [] } } } } ``` Now if you run `npm run test` or `nx test` twice, the results will be retrieved from the cache. The `inputs` used in this example are as cautious as possible, so you can significantly improve the value of the cache by [customizing Nx Inputs](/docs/kb/configure-inputs) for each task. ## Add plugins Plugins are optional. Guided setup detects the tools in your repository and offers to add the matching plugins, and you can add them later instead with [`nx add`](/docs/reference/nx-commands#nx-add). A plugin configures tasks from the tool config you already have, so you maintain fewer scripts and stop hand-writing cache inputs and outputs. Adding a plugin can also point your `package.json` scripts at the tasks it created, which extends caching to everywhere those scripts already run, including CI. Guided `nx init` asks first. `nx add` does it without asking for a core `@nx/*` plugin, so pass `--updatePackageScripts=false` to keep your scripts as they are: ```diff // package.json { "name": "my-workspace", ... "scripts": { - "build": "next build && echo 'Build complete'", + "build": "nx next:build && echo 'Build complete'", - "lint": "eslint ./src", + "lint": "nx eslint:lint", "test": "node ./run-tests.js" }, + "nx": {} } ``` `@nx/next/plugin` adds a `next:build` target that runs `next build` with caching set up correctly, so `nx next:build` and `next build` do the same work. `@nx/eslint/plugin` does the same for `eslint ./src`. The `test` script matched no plugin and was left alone. Skip the rewrite and your scripts stay as they are, with the plugin tasks still available through `nx next:build`. ### Inferred tasks `@nx/next` provides `dev` and `start` tasks in addition to the `next:build` task. Those tasks were created by the `@nx/next/plugin` plugin from your existing Next.js configuration. You can see the configuration for the Nx Plugins in `nx.json`: ```json // nx.json { "plugins": [ { "plugin": "@nx/eslint/plugin", "options": { "targetName": "eslint:lint" } }, { "plugin": "@nx/next/plugin", "options": { "buildTargetName": "next:build", "devTargetName": "dev", "startTargetName": "start" } } ] } ``` Each plugin can accept options to customize the projects which they create. You can see more information about configuring the plugins on the [`@nx/next/plugin`](/docs/technologies/react/next/introduction) and [`@nx/eslint/plugin`](/docs/technologies/eslint/introduction) plugin pages. To view all available tasks, open the Project Details view with Nx Console or use the terminal to launch the project details in a browser window. ```shell nx show project my-workspace --web ``` {% project_details title="Project Details View" %} ```json { "project": { "name": "my-workspace", "data": { "root": ".", "targets": { "eslint:lint": { "cache": true, "options": { "cwd": ".", "command": "eslint ./src" }, "inputs": [ "default", "{workspaceRoot}/.eslintrc", "{workspaceRoot}/tools/eslint-rules/**/*", { "externalDependencies": ["eslint"] } ], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["eslint"] } }, "next:build": { "options": { "cwd": ".", "command": "next build" }, "dependsOn": ["^build"], "cache": true, "inputs": [ "default", "^default", { "externalDependencies": ["next"] } ], "outputs": ["{projectRoot}/.next", "{projectRoot}/.next/!(cache)"], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["next"] } }, "dev": { "options": { "cwd": ".", "command": "next dev", "continuous": true }, "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["next"] } }, "start": { "options": { "cwd": ".", "command": "next start", "continuous": true }, "dependsOn": ["build"], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["next"] } } }, "sourceRoot": ".", "name": "my-workspace", "projectType": "library", "implicitDependencies": [], "tags": [] } }, "sourceMap": { "root": ["package.json", "nx/core/package-json-workspaces"], "targets": ["package.json", "nx/core/package-json-workspaces"], "targets.eslint:lint": [".eslintrc.json", "@nx/eslint/plugin"], "targets.eslint:lint.command": [".eslintrc.json", "@nx/eslint/plugin"], "targets.eslint:lint.cache": [".eslintrc.json", "@nx/eslint/plugin"], "targets.eslint:lint.options": [".eslintrc.json", "@nx/eslint/plugin"], "targets.eslint:lint.inputs": [".eslintrc.json", "@nx/eslint/plugin"], "targets.eslint:lint.options.cwd": [".eslintrc.json", "@nx/eslint/plugin"], "targets.next:build": ["next.config.js", "@nx/next/plugin"], "targets.next:build.command": ["next.config.js", "@nx/next/plugin"], "targets.next:build.options": ["next.config.js", "@nx/next/plugin"], "targets.next:build.dependsOn": ["next.config.js", "@nx/next/plugin"], "targets.next:build.cache": ["next.config.js", "@nx/next/plugin"], "targets.next:build.inputs": ["next.config.js", "@nx/next/plugin"], "targets.next:build.outputs": ["next.config.js", "@nx/next/plugin"], "targets.next:build.options.cwd": ["next.config.js", "@nx/next/plugin"], "targets.dev": ["next.config.js", "@nx/next/plugin"], "targets.dev.command": ["next.config.js", "@nx/next/plugin"], "targets.dev.options": ["next.config.js", "@nx/next/plugin"], "targets.dev.options.cwd": ["next.config.js", "@nx/next/plugin"], "targets.start": ["next.config.js", "@nx/next/plugin"], "targets.start.command": ["next.config.js", "@nx/next/plugin"], "targets.start.options": ["next.config.js", "@nx/next/plugin"], "targets.start.dependsOn": ["next.config.js", "@nx/next/plugin"], "targets.start.options.cwd": ["next.config.js", "@nx/next/plugin"], "sourceRoot": ["package.json", "nx/core/package-json-workspaces"], "name": ["package.json", "nx/core/package-json-workspaces"], "projectType": ["package.json", "nx/core/package-json-workspaces"], "targets.nx-release-publish": [ "package.json", "nx/core/package-json-workspaces" ], "targets.nx-release-publish.dependsOn": [ "package.json", "nx/core/package-json-workspaces" ], "targets.nx-release-publish.executor": [ "package.json", "nx/core/package-json-workspaces" ], "targets.nx-release-publish.options": [ "package.json", "nx/core/package-json-workspaces" ] } } ``` {% /project_details %} The project detail view lists all available tasks, the configuration values for those tasks and where those configuration values are being set. ## Fast CI ⚡ Caching pays off locally, and it pays off more in CI, where the same tasks run on every push. As repositories get bigger, making sure that the CI is fast, reliable, and maintainable can get very challenging. Nx provides a solution. - Nx reduces wasted time in CI with the [`affected` command](/docs/features/ci-features/affected). - [Remote caching](/docs/features/ci-features/remote-cache) reuses task artifacts across CI runs when the task inputs match. - Nx Agents [efficiently distribute tasks across machines](/docs/concepts/ci-concepts/parallelization-distribution) ensuring constant CI time regardless of the repository size. The right number of machines is allocated for each PR to ensure good performance without wasting compute. - Nx Atomizer [automatically splits](/docs/features/ci-features/split-e2e-tasks) large e2e tests to distribute them across machines. Nx can also automatically [identify and rerun flaky e2e tests](/docs/features/ci-features/flaky-tasks). ### Connect to Nx Cloud Nx Cloud is a companion app for your CI system that provides remote caching, task distribution, e2e tests deflaking, better DX and more. Now that we're working on the CI pipeline, it is important for your changes to be pushed to a GitHub repository. 1. Commit your existing changes with `git add . && git commit -am "updates"` 2. [Create a new GitHub repository](https://github.com/new) 3. Follow GitHub instructions to push your existing code to the repository Now connect your repository to Nx Cloud with the following command: ```shell npx nx@latest connect ``` A browser window will open to register your repository in your [Nx Cloud](https://cloud.nx.app?utm_source=nx-dev&utm_medium=website&utm_campaign=adding-to-existing-project) account. The link is also printed to the terminal if the windows does not open, or you closed it before finishing the steps. The app will guide you to create a PR to enable Nx Cloud on your repository. ![](../../../assets/guides/adopting-nx/nx-cloud-github-connect.avif) Once the PR is created, merge it into your main branch. ![](../../../assets/guides/adopting-nx/github-cloud-pr-merged.avif) And make sure you pull the latest changes locally: ```shell git pull ``` You should now have an `nxCloudId` property specified in the `nx.json` file. ### Create a CI workflow Use the following command to generate a CI workflow file. ```shell npx nx generate ci-workflow --ci=github ``` This generator creates a `.github/workflows/ci.yml` file that contains a CI pipeline that will run the `lint`, `test`, `build` and `e2e` tasks for projects that are affected by any given PR. Since we are using Nx Cloud, the pipeline will also distribute tasks across multiple machines to ensure fast and reliable CI runs. The key lines in the CI pipeline are: ```yml {% meta="{10-14,21-22}" %} // .github/workflows/ci.yml name: CI # ... jobs: main: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: fetch-depth: 0 filter: tree:0 # This enables task distribution via Nx Cloud # Run this command as early as possible, before dependencies are installed # Learn more at https://nx.dev/ci/reference/nx-cloud-cli#nx-cloud-start-nx-agents # Connect your workspace by running "nx connect" and uncomment this - run: npx nx start-nx-agents - uses: actions/setup-node@v6 with: node-version: 24 cache: 'npm' - run: npm ci - uses: nrwl/nx-set-shas@v5 # Nx Affected runs only tasks affected by the changes in this PR/commit. Learn more: https://nx.dev/ci/features/affected - run: npx nx affected -t lint test build ``` The generator also creates a [`.nx/ci-config.yaml`](/docs/reference/nx-cloud/ci-config) file. It holds the settings that `start-nx-agents` reads, so you tune task distribution there rather than in the workflow: ```yml // .nx/ci-config.yaml dte: distribute-on: 3 linux-medium-js lifecycle: stop-after: - build ``` ### Open a pull request Commit the changes and open a new PR on GitHub. ```shell git add . git commit -m 'add CI workflow file' git push origin add-workflow ``` When you view the PR on GitHub, you will see a comment from Nx Cloud that reports on the status of the CI run. ![Nx Cloud report](../../../assets/guides/adopting-nx/github-pr-cloud-report.avif) The `See all runs` link goes to a page with the progress and results of tasks that were run in the CI pipeline. ![Run details](../../../assets/guides/adopting-nx/nx-cloud-run-details.avif) For more information about how Nx can improve your CI pipeline, check out our [CI setup guides](/docs/kb/setup-ci). ## Learn more {% cardgrid %} {% linkcard title="Customizing Inputs and Named Inputs" description="Learn more about how to fine-tune caching with custom inputs" href="/docs/kb/configure-inputs" /%} {% linkcard title="Cache Task Results" description="Learn more about how caching works" href="/docs/features/cache-task-results" /%} {% linkcard title="Adding Nx to NPM/Yarn/PNPM Workspace" description="Learn more about how to add Nx to an existing monorepo" href="/docs/kb/adding-to-monorepo" /%} {% /cardgrid %} --- ## Adding Nx to an NPM/Yarn/PNPM Monorepo {% aside type="note" title="Migrating from Lerna?" %} Modern Lerna already runs its tasks through Nx, so the migration is an incremental step. See [Nx vs Lerna](/docs/kb/nx-vs-lerna) for how the tools relate and when to switch. {% /aside %} Nx has first-class support for [monorepos](/docs/getting-started/tutorials/crafting-your-workspace). Add it to an existing NPM, Yarn, or PNPM monorepo and you get - fast [task scheduling](/docs/features/run-tasks) - high-performance task [caching](/docs/features/cache-task-results) - affected runs that skip the packages your change doesn't touch None of this is gated on how many packages the monorepo has. The setup installs the `nx` package at the root and adds an `nx.json` for configuring caching and task pipelines. Your workspace layout, your package manager, and your `package.json` scripts stay where they are. {% course_video src="https://youtu.be/3hW53b1IJ84" courseTitle="From PNPM Workspaces to Distributed CI" courseUrl="https://nx.dev/courses/pnpm-nx-next/lessons-01-nx-init" /%} ## Installing Nx Run the following command to automatically set up Nx: ```shell npx nx@latest init ``` `nx init` asks whether you want a minimum or a guided setup. **Minimum** creates `nx.json`, adds `nx` to your root dev dependencies, and adds the Nx cache directories to an existing `.gitignore`. Nothing else changes, and you opt tasks into caching yourself by setting `cache: true` in `targetDefaults`. **Guided** does the same and then asks which of your scripts need to run in dependency order, which are cacheable, and what they output. It writes the matching `targetDefaults`: ```json // nx.json { "$schema": "./node_modules/nx/schemas/nx-schema.json", "targetDefaults": { "build": { "dependsOn": ["^build"], "cache": true, "outputs": ["{projectRoot}/dist"] }, "test": { "cache": true } } } ``` From there, run tasks across the workspace: ```shell npx nx run-many -t build # every package npx nx build web # a single package npx nx affected -t test # only what your change touches ``` Plugins, generators, `project.json` files, and Nx Cloud are all things you can add later, or never. ## Incrementally adopting Nx Nx features are independent of each other, so you can use Nx for a subset of your scripts and keep your existing commands for the rest. For example, use Nx to run your builds: ```shell npx nx run-many -t build ``` But instead keep using NPM/Yarn/PNPM workspace commands for your tests and other scripts. Here's an example of using PNPM commands to run tests across packages ```shell pnpm run -r test ``` ## Add plugins Plugins are optional. Guided setup detects the tools in your workspace and offers to add the matching plugins, and you can add them later instead with [`nx add`](/docs/reference/nx-commands#nx-add). A plugin configures tasks from the tool config you already have, so you maintain fewer scripts and stop hand-writing cache inputs and outputs. Adding a plugin can also point your `package.json` scripts at the tasks it created, which extends caching to everywhere those scripts already run, including CI. Guided `nx init` asks first. `nx add` does it without asking for a core `@nx/*` plugin, so pass `--updatePackageScripts=false` to keep your scripts as they are: ```diff // package.json { "name": "my-workspace", ... "scripts": { - "build": "next build && echo 'Build complete'", + "build": "nx next:build && echo 'Build complete'", - "lint": "eslint ./src", + "lint": "nx eslint:lint", "test": "node ./run-tests.js" }, ... } ``` `@nx/next/plugin` adds a `next:build` target that runs `next build` with caching set up correctly, so `nx next:build` and `next build` do the same work. `@nx/eslint/plugin` does the same for `eslint ./src`. The `test` script matched no plugin and was left alone. Skip the rewrite and your scripts stay as they are, with the plugin tasks still available through `nx next:build`. ### Inferred tasks `@nx/next` provides `dev` and `start` tasks in addition to the `next:build` task. Those tasks were created by the `@nx/next/plugin` plugin from your existing Next.js configuration. You can see the configuration for the Nx Plugins in `nx.json`: ```json // nx.json { "plugins": [ { "plugin": "@nx/eslint/plugin", "options": { "targetName": "eslint:lint" } }, { "plugin": "@nx/next/plugin", "options": { "buildTargetName": "next:build", "devTargetName": "dev", "startTargetName": "start" } } ] } ``` Each plugin can accept options to customize the projects which they create. You can see more information about configuring the plugins on the [`@nx/next/plugin`](/docs/technologies/react/next/introduction) and [`@nx/eslint/plugin`](/docs/technologies/eslint/introduction) plugin pages. To view all available tasks, open the Project Details view with Nx Console or use the terminal to launch the project details in a browser window. ```shell nx show project my-workspace --web ``` {% project_details title="Project Details View" %} ```json { "project": { "name": "my-workspace", "data": { "root": ".", "targets": { "eslint:lint": { "cache": true, "options": { "cwd": ".", "command": "eslint ./src" }, "inputs": [ "default", "{workspaceRoot}/.eslintrc", "{workspaceRoot}/tools/eslint-rules/**/*", { "externalDependencies": ["eslint"] } ], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["eslint"] } }, "next:build": { "options": { "cwd": ".", "command": "next build" }, "dependsOn": ["^build"], "cache": true, "inputs": [ "default", "^default", { "externalDependencies": ["next"] } ], "outputs": ["{projectRoot}/.next", "{projectRoot}/.next/!(cache)"], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["next"] } }, "dev": { "options": { "cwd": ".", "command": "next dev", "continuous": true }, "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["next"] } }, "start": { "options": { "cwd": ".", "command": "next start", "continuous": true }, "dependsOn": ["build"], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["next"] } } }, "sourceRoot": ".", "name": "my-workspace", "projectType": "library", "implicitDependencies": [], "tags": [] } }, "sourceMap": { "root": ["package.json", "nx/core/package-json-workspaces"], "targets": ["package.json", "nx/core/package-json-workspaces"], "targets.eslint:lint": [".eslintrc.json", "@nx/eslint/plugin"], "targets.eslint:lint.command": [".eslintrc.json", "@nx/eslint/plugin"], "targets.eslint:lint.cache": [".eslintrc.json", "@nx/eslint/plugin"], "targets.eslint:lint.options": [".eslintrc.json", "@nx/eslint/plugin"], "targets.eslint:lint.inputs": [".eslintrc.json", "@nx/eslint/plugin"], "targets.eslint:lint.options.cwd": [".eslintrc.json", "@nx/eslint/plugin"], "targets.next:build": ["next.config.js", "@nx/next/plugin"], "targets.next:build.command": ["next.config.js", "@nx/next/plugin"], "targets.next:build.options": ["next.config.js", "@nx/next/plugin"], "targets.next:build.dependsOn": ["next.config.js", "@nx/next/plugin"], "targets.next:build.cache": ["next.config.js", "@nx/next/plugin"], "targets.next:build.inputs": ["next.config.js", "@nx/next/plugin"], "targets.next:build.outputs": ["next.config.js", "@nx/next/plugin"], "targets.next:build.options.cwd": ["next.config.js", "@nx/next/plugin"], "targets.dev": ["next.config.js", "@nx/next/plugin"], "targets.dev.command": ["next.config.js", "@nx/next/plugin"], "targets.dev.options": ["next.config.js", "@nx/next/plugin"], "targets.dev.options.cwd": ["next.config.js", "@nx/next/plugin"], "targets.start": ["next.config.js", "@nx/next/plugin"], "targets.start.command": ["next.config.js", "@nx/next/plugin"], "targets.start.options": ["next.config.js", "@nx/next/plugin"], "targets.start.dependsOn": ["next.config.js", "@nx/next/plugin"], "targets.start.options.cwd": ["next.config.js", "@nx/next/plugin"], "sourceRoot": ["package.json", "nx/core/package-json-workspaces"], "name": ["package.json", "nx/core/package-json-workspaces"], "projectType": ["package.json", "nx/core/package-json-workspaces"], "targets.nx-release-publish": [ "package.json", "nx/core/package-json-workspaces" ], "targets.nx-release-publish.dependsOn": [ "package.json", "nx/core/package-json-workspaces" ], "targets.nx-release-publish.executor": [ "package.json", "nx/core/package-json-workspaces" ], "targets.nx-release-publish.options": [ "package.json", "nx/core/package-json-workspaces" ] } } ``` {% /project_details %} The project detail view lists all available tasks, the configuration values for those tasks and where those configuration values are being set. ## Configure an existing script to run with Nx If you want to keep invoking a script through your package manager and still cache it, tell Nx about it. 1. Preface the script with `nx exec -- ` to have `npm run test` invoke the command with Nx. 2. Define caching settings. The `nx exec` command allows you to keep using `npm test` or `npm run test` (or other package manager's alternatives) as you're accustomed to. But still get the benefits of making those operations cacheable. Configuring the `test` script from the example above to run with Nx would look something like this: ```json // package.json { "name": "my-workspace", ... "scripts": { "build": "next build", "lint": "eslint ./src", "test": "nx exec -- node ./run-tests.js" }, ... "nx": { "targets": { "test": { "cache": "true", "inputs": [ "default", "^default" ], "outputs": [] } } } } ``` Now if you run `npm run test` or `nx test` twice, the results will be retrieved from the cache. The `inputs` used in this example are as cautious as possible, so you can significantly improve the value of the cache by [customizing Nx Inputs](/docs/kb/configure-inputs) for each task. ## Fast CI ⚡ Caching pays off locally, and it pays off more in CI, where the same tasks run on every push. As repositories get bigger, making sure that the CI is fast, reliable, and maintainable can get very challenging. Nx provides a solution. - Nx reduces wasted time in CI with the [`affected` command](/docs/features/ci-features/affected). - [Remote caching](/docs/features/ci-features/remote-cache) reuses task artifacts across CI runs when the task inputs match. - Nx Agents [efficiently distribute tasks across machines](/docs/concepts/ci-concepts/parallelization-distribution) ensuring constant CI time regardless of the repository size. The right number of machines is allocated for each PR to ensure good performance without wasting compute. - Nx Atomizer [automatically splits](/docs/features/ci-features/split-e2e-tasks) large e2e tests to distribute them across machines. Nx can also automatically [identify and rerun flaky e2e tests](/docs/features/ci-features/flaky-tasks). ### Connect to Nx Cloud Nx Cloud is a companion app for your CI system that provides remote caching, task distribution, e2e tests deflaking, better DX and more. Now that we're working on the CI pipeline, it is important for your changes to be pushed to a GitHub repository. 1. Commit your existing changes with `git add . && git commit -am "updates"` 2. [Create a new GitHub repository](https://github.com/new) 3. Follow GitHub instructions to push your existing code to the repository Now connect your repository to Nx Cloud with the following command: ```shell npx nx@latest connect ``` A browser window will open to register your repository in your [Nx Cloud](https://cloud.nx.app?utm_source=nx-dev&utm_medium=website&utm_campaign=adding-to-monorepo) account. The link is also printed to the terminal if the windows does not open, or you closed it before finishing the steps. The app will guide you to create a PR to enable Nx Cloud on your repository. ![](../../../assets/guides/adopting-nx/nx-cloud-github-connect.avif) Once the PR is created, merge it into your main branch. ![](../../../assets/guides/adopting-nx/github-cloud-pr-merged.avif) And make sure you pull the latest changes locally: ```shell git pull ``` You should now have an `nxCloudId` property specified in the `nx.json` file. ### Create a CI workflow Use the following command to generate a CI workflow file. ```shell npx nx generate ci-workflow --ci=github ``` This generator creates a `.github/workflows/ci.yml` file that contains a CI pipeline that will run the `lint`, `test`, `build` and `e2e` tasks for projects that are affected by any given PR. Since we are using Nx Cloud, the pipeline will also distribute tasks across multiple machines to ensure fast and reliable CI runs. The key lines in the CI pipeline are: ```yml {% meta="{11-15,22-23}" %} // .github/workflows/ci.yml" name: CI # ... jobs: main: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: fetch-depth: 0 filter: tree:0 # This enables task distribution via Nx Cloud # Run this command as early as possible, before dependencies are installed # Learn more at https://nx.dev/ci/reference/nx-cloud-cli#nx-cloud-start-nx-agents # Connect your workspace by running "nx connect" and uncomment this - run: npx nx start-nx-agents - uses: actions/setup-node@v6 with: node-version: 24 cache: 'npm' - run: npm ci - uses: nrwl/nx-set-shas@v5 # Nx Affected runs only tasks affected by the changes in this PR/commit. Learn more: https://nx.dev/ci/features/affected - run: npx nx affected -t lint test build ``` The generator also creates a [`.nx/ci-config.yaml`](/docs/reference/nx-cloud/ci-config) file. It holds the settings that `start-nx-agents` reads, so you tune task distribution there rather than in the workflow: ```yml // .nx/ci-config.yaml dte: distribute-on: 3 linux-medium-js lifecycle: stop-after: - build ``` ### Open a pull request Commit the changes and open a new PR on GitHub. ```shell git add . git commit -m 'add CI workflow file' git push origin add-workflow ``` When you view the PR on GitHub, you will see a comment from Nx Cloud that reports on the status of the CI run. ![Nx Cloud report](../../../assets/guides/adopting-nx/github-pr-cloud-report.avif) The `See all runs` link goes to a page with the progress and results of tasks that were run in the CI pipeline. ![Run details](../../../assets/guides/adopting-nx/nx-cloud-run-details.avif) For more information about how Nx can improve your CI pipeline, check out our [detailed tutorial for you CI provider](/docs/kb/setup-ci). ## Learn more {% cardgrid %} {% linkcard title="Cache Task Results" description="Learn more about how caching works" href="/docs/features/cache-task-results" /%} {% linkcard title="Task Pipeline Configuration" description="Learn more about how to setup task dependencies" href="/docs/concepts/task-pipeline-configuration" /%} {% linkcard title="Nx Ignore" description="Learn about how to ignore certain projects using .nxignore" href="/docs/reference/nxignore" /%} {% linkcard title="Migrating from Turborepo to Nx" description="Read about Migrating from Turborepo to Nx" href="/docs/kb/from-turborepo" /%} {% /cardgrid %} --- ## Disable Graph Links Created from Analyzing Source Files If you want to disable detecting dependencies from source code and want to only use the dependencies as defined in `package.json` (the same way yarn does), you can add the following configuration to your `nx.json` file: ```json // nx.json { "pluginsConfig": { "@nx/js": { "analyzeSourceFiles": false } } } ``` ## Default The default setting for Nx repos is `"analyzeSourceFiles": true`. The assumption is that if there is a real link in the code between projects, you want to know about it. For Lerna repos, the default value is `false` in order to maintain backward compatibility with the way Lerna has always calculated dependencies. --- ## Angular - Configuring styles and preprocessor options When working with Angular and Storybook in an Nx workspace, you may need to configure styles and CSS preprocessors to ensure your components render correctly in Storybook. ## Global styles ### Adding global styles to Storybook You can add global styles to your Storybook by creating a `preview.ts` file in your `.storybook` directory: ```typescript // .storybook/preview.ts import type { Preview } from '@storybook/angular'; // Import global styles import '../src/styles.scss'; // Your global styles import '@angular/material/prebuilt-themes/indigo-pink.css'; // Material theme const preview: Preview = { parameters: { actions: { argTypesRegex: '^on[A-Z].*' }, controls: { matchers: { color: /(background|color)$/i, date: /Date$/, }, }, }, }; export default preview; ``` ### Using Angular material styles If you're using Angular Material, you can include the theme in your Storybook: ```typescript // .storybook/preview.ts import '@angular/material/prebuilt-themes/indigo-pink.css'; // or your custom theme import '../src/styles/material-theme.scss'; ``` ## SCSS/SASS configuration ### Configuring SCSS in Storybook If your Angular project uses SCSS, you can configure Storybook to handle SCSS files properly: ```typescript // .storybook/main.ts import type { StorybookConfig } from '@storybook/angular'; const config: StorybookConfig = { stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'], addons: [], framework: { name: '@storybook/angular', options: {}, }, core: { builder: '@storybook/builder-webpack5', }, typescript: { check: false, checkOptions: {}, reactDocgen: 'react-docgen-typescript', reactDocgenTypescriptOptions: { shouldExtractLiteralValuesFromEnum: true, propFilter: (prop) => prop.parent ? !/node_modules/.test(prop.parent.fileName) : true, }, }, }; export default config; ``` ### Including SCSS variables and mixins To use SCSS variables and mixins in your stories, you may need to configure additional include paths: ```typescript // .storybook/main.ts import type { StorybookConfig } from '@storybook/angular'; const config: StorybookConfig = { // ... other config webpackFinal: async (config) => { // Add SCSS support with include paths const scssRule = config.module?.rules?.find((rule: any) => rule.test?.toString().includes('scss') ); if (scssRule && scssRule.use) { const sassLoaderOptions = scssRule.use.find((use: any) => use.loader?.includes('sass-loader') ); if (sassLoaderOptions) { sassLoaderOptions.options = { ...sassLoaderOptions.options, sassOptions: { includePaths: ['src/styles', 'src/assets/styles'], }, }; } } return config; }, }; export default config; ``` ## Tailwind CSS configuration ### Setting up Tailwind CSS with Storybook If your Angular project uses Tailwind CSS, configure it in your Storybook: 1. Install Tailwind CSS for Storybook: ```shell npm install -D @storybook/addon-postcss ``` 2. Add the addon to your Storybook configuration: ```typescript // .storybook/main.ts import type { StorybookConfig } from '@storybook/angular'; const config: StorybookConfig = { // ... other config addons: [ { name: '@storybook/addon-postcss', options: { postcssLoaderOptions: { implementation: require('postcss'), }, }, }, ], }; export default config; ``` 3. Import Tailwind in your preview file: ```typescript // .storybook/preview.ts import '../src/styles.css'; // This should include Tailwind CSS imports ``` ## Component-specific styles ### Using Angular component styles in stories Component styles defined in Angular components should work automatically in Storybook. However, if you need to override styles for stories, you can do so in individual story files: ```typescript // button.stories.ts import type { Meta, StoryObj } from '@storybook/angular'; import { ButtonComponent } from './button.component'; const meta: Meta = { title: 'Components/Button', component: ButtonComponent, parameters: { // Add custom styles for this story docs: { description: { component: 'A customizable button component.', }, }, }, decorators: [ (story) => ({ template: `
`, }), ], }; export default meta; type Story = StoryObj; export const Default: Story = { args: { label: 'Button', }, }; export const CustomStyling: Story = { args: { label: 'Custom Button', }, decorators: [ (story) => ({ template: `
`, }), ], }; ``` ## Troubleshooting style issues ### Common issues and solutions 1. **Styles not loading**: Ensure your global styles are imported in `.storybook/preview.ts` 2. **SCSS variables not available**: Configure include paths in the webpack configuration 3. **Angular Material styles missing**: Import the Material theme CSS file in your preview 4. **Component styles not applying**: Check that component stylesheets are properly referenced in the component decorator 5. **CSS modules not working**: Ensure your webpack configuration supports CSS modules if your project uses them ### Debug style loading You can debug style loading by checking the browser's developer tools to see if stylesheets are being loaded correctly and if there are any 404 errors for missing style files. --- ## Micro Frontends with Angular An Angular micro frontend architecture splits an Angular application into smaller applications that separate teams build, test, and deploy independently, composed in the browser at runtime. Whether the pattern fits your organization at all is covered in [What is Micro Frontend Architecture?](/docs/kb/micro-frontend-architecture): reach for it when independent deployment is a hard requirement, not when you want faster builds. ## The supported path: Native Federation As of Nx v23, the `@nx/angular` `host` and `remote` generators are deprecated, and Angular Module Federation in Nx is no longer supported going forward. The supported path for Angular micro frontends is [`@angular-architects/native-federation`](https://www.npmjs.com/package/@angular-architects/native-federation), which implements the same runtime composition model on top of the Angular CLI build using web standards (ES modules and import maps) instead of bundler-specific machinery. Native Federation apps are regular Angular applications in your workspace. Follow the package's documentation to configure the federation host and remotes, and a worked Angular Native Federation example lives in the [mf-examples repository](https://github.com/nrwl/mf-examples). ## Nx still orchestrates the apps Moving federation config out of Nx doesn't change what Nx does for the workspace. Each micro frontend stays a separate project in the [project graph](/docs/features/explore-graph), which is what keeps a multi-team Angular workspace manageable: - **Affected-only CI** - [`nx affected`](/docs/features/ci-features/affected) rebuilds and retests only the applications a change touches, and shared library changes compute the exact set of affected micro frontends. - **Caching** - [local and remote caching](/docs/features/cache-task-results) means an app another team already built is restored, not rebuilt. - **Module boundaries** - [tag rules](/docs/features/enforce-module-boundaries) keep one team's internals private to their slice. - **Distribution** - [Nx Agents](/docs/features/ci-features/distribute-task-execution) spread builds and tests across machines, so adding micro frontends stops adding CI time. ## What about existing Nx Angular Module Federation setups? Workspaces built on `@nx/angular:host` and `@nx/angular:remote` keep working in Nx v23 on the deprecated executors, which will be removed in v24. Plan a migration to Native Federation, or evaluate whether your setup needs runtime composition at all: if every team deploys together anyway, regular applications with [buildable libraries](/docs/kb/buildable-and-publishable-libraries) remove the runtime failure modes entirely. The [deprecation details](/docs/kb/consumer-and-provider#what-changed-in-v23) list the exact generators and executors affected. ## Should Angular and React micro frontends mix? Module Federation makes mixed-framework setups possible, but every extra framework multiplies the shared dependency surface and splits your developers into camps. Mixing makes sense only as a deliberate transition strategy, such as an incremental migration from Angular to React or the reverse. For the single-framework React path, see [Micro Frontends with React](/docs/kb/react-micro-frontends). --- ## Module Federation with Server-Side Rendering {% aside type="caution" title="Deprecated: Angular Module Federation" %} The `@nx/angular` `host` and `remote` generators and Module Federation executors used on this page are deprecated in Nx v23 and will be removed in v24. Existing workspaces keep working in the meantime. For the supported Angular micro frontend path, see [Micro Frontends with Angular](/docs/kb/angular-micro-frontends). {% /aside %} Walk you through creating a Module Federated setup with Server Side Rendering (SSR) for Angular and React using Nx and its generators. ## Steps ### Create an empty workspace Run the following command with the options listed to create an empty workspace. ```text {% title="npx create-nx-workspace@latest myorg --preset=apps" frame="terminal" %} NX Let's create a new workspace [https://nx.dev/getting-started/intro] ✔ Which CI provider would you like to use? · skip ✔ Would you like remote caching to make your build faster? · skip ``` {% linkcard title="Opting into Nx Cloud" description="You will also be prompted whether to add Nx Cloud to your workspace. We won't address this in this recipe, but you can see the introduction to Nx Cloud for more details." href="/docs/kb/setup-ci" /%} ### Install your framework plugin {% aside type="note" title="Keep Nx Package Versions In Sync" %} Make sure to install the `@nx/angular` or `@nx/react` versions that matches the version of `nx` in your repository. If the version numbers get out of sync, you can encounter some difficult to debug errors. You can [fix Nx version mismatches with this recipe](/docs/kb/keep-nx-versions-in-sync). {% /aside %} {% tabs syncKey="framework" %} {% tabitem label="Angular" %} ```shell nx add @nx/angular ``` {% /tabitem %} {% tabitem label="React" %} ```shell nx add @nx/react ``` {% /tabitem %} {% /tabs %} ### Generating a host and multiple remotes with SSR We will generate the apps required for a storefront application. We will need the following applications: - Store - _host application_ - Product - _remote application_ - Checkout - _remote application_ Nx allows you to do this with a single command: {% tabs syncKey="framework" %} {% tabitem label="Angular" %} ```shell npx nx g @nx/angular:host apps/store --ssr --remotes=product,checkout ``` {% /tabitem %} {% tabitem label="React" %} ```shell npx nx g @nx/react:host apps/store --ssr --remotes=product,checkout ``` {% /tabitem %} {% /tabs %} This will generate three applications, set up with SSR and Module Federation. ### Serving the store application When using Module Federation, we want to serve the host application along with the remote applications so that everything works as expected. To do this, run: {% tabs syncKey="framework" %} {% tabitem label="Angular" %} ```shell npx nx serve-ssr store ``` {% /tabitem %} {% tabitem label="React" %} ```shell npx nx serve store ``` {% /tabitem %} {% /tabs %} This will run all three application servers but only the `store` will be watching for file changes. If you make a change to one of the remote applications (`checkout` or `product`) the changes will not be hot reloaded. ### Serving the store application with file watching for checkout If working on a remote application, we can still serve it via the host application and have it watch for changes. To serve the `store` application and watch for changes on the `checkout` application run: {% tabs syncKey="framework" %} {% tabitem label="Angular" %} ```shell npx nx serve-ssr store --devRemotes=checkout ``` {% /tabitem %} {% tabitem label="React" %} ```shell npx nx serve store --devRemotes=checkout ``` {% /tabitem %} {% /tabs %} ### Additional resources To learn more about Module Federation, we have some resources you might find useful: - [Reference: NxModuleFederationPlugin](/docs/kb/nx-module-federation-plugin) - [Guide: Faster Builds with Module Federation](/docs/kb/faster-builds-with-module-federation) - [Video: Speed up your Angular serve and build times with Module Federation and Nx](https://www.youtube.com/watch?v=JkcaGzhRjkc) --- ## Nx and Angular Versions The latest version of Nx supports the [actively supported versions of Angular (current and LTS versions)](https://angular.dev/reference/releases#actively-supported-versions). Workspaces in any of those versions are recommended to use the latest version of Nx to benefit from all the new features and fixes. {% aside type="note" title="Older Nx and Angular versions" %} The support for multiple versions of Angular in the latest version of Nx was added in **v15.7.0** and started by supporting Angular v14 and v15. If your workspace is in an older version of Angular or you can't update to the latest version of Nx for some reason, please have a look at the next section to know which version of Nx to use. {% /aside %} ## Nx and Angular version compatibility matrix Below is a reference table that matches versions of Angular to the version of Nx that is compatible with it. The table shows the version of Angular, the recommended version of Nx to use and the range of Nx versions that support the version of Angular. We provide a recommended version, and it is usually the latest minor version of Nx in the range provided because there will have been bug fixes added since the first release in the range. | Angular Version | **Nx Version _(recommended)_** | Nx Version _(range)_ | | --------------- | ------------------------------ | ---------------------------------------- | | ~22.1.0 | **latest** | >=23.2.0 <=latest | | ~22.0.0 | **latest** | >=23.1.0 <=latest | | ~21.2.0 | **latest** | >=22.6.0 <=latest | | ~21.1.0 | **latest** | >=22.4.0 <=latest | | ~21.0.0 | **latest** | >=22.3.0 <=latest | | ~20.3.0 | **latest** | >=21.6.1 <=latest | | ~20.2.0 | **latest** | >=21.5.1 <=latest | | ~20.1.0 | **latest** | >=21.3.0 <=latest | | ~20.0.0 | **latest** | >=21.2.0 <=latest | | ~19.2.0 | **~23.0.0** | >=20.5.0 <23.1.0 | | ~19.1.0 | **~23.0.0** | >=20.4.0 <23.1.0 | | ~19.0.0 | **~23.0.0** | >=20.2.0 <23.1.0 | | ~18.2.0 | **~22.2.0** | >=19.6.0 <22.3.0 | | ~18.1.0 | **~22.2.0** | >=19.5.0 <22.3.0 | | ~18.0.0 | **~22.2.0** | >=19.1.0 <22.3.0 | | ~17.3.0 | **~21.1.0** | >=18.2.0 <21.2.0 | | ~17.2.0 | **~21.1.0** | >=18.1.1 <21.2.0 | | ~17.1.0 | **~21.1.0** | >=17.3.0 <21.2.0 | | ~17.0.0 | **~21.1.0** | >=17.1.0 <21.2.0 | | ~16.2.0 | **~20.1.0** | >=16.7.0 <20.2.0 | | ~16.1.0 | **~20.1.0** | >=16.4.0 <20.2.0 | | ~16.0.0 | **~20.1.0** | >=16.1.0 <20.2.0 | | ~15.2.0 | **~19.0.0** | >=15.8.0 <19.1.0 | | ~15.1.0 | **~19.0.0** | >=15.5.0 <19.1.0 | | ~15.0.0 | **~19.0.0** | >=15.2.0 <=15.4.8 \|\| >=15.7.0 <19.1.0 | | ~14.2.0 | **~17.0.0** | >=14.6.0 <=15.1.1 \|\| >=15.7.0 <17.1.0 | | ~14.1.0 | **~17.0.0** | >=14.5.0 <=14.5.10 \|\| >=15.7.0 <17.1.0 | | ~14.0.0 | **~17.0.0** | >=14.2.1 <=14.4.3 \|\| >=15.7.0 <17.1.0 | | ^13.0.0 | **14.1.9** | >=13.2.0 <=14.1.9 | | ^12.0.0 | **13.1.4** | >=12.3.0 <=13.1.4 | | ^11.0.0 | **12.2.0** | >=11.0.0 <=12.2.0 | | ^10.0.0 | **10.4.15** | >=9.7.0 <=10.4.15 | | ^9.0.0 | **9.6.0** | >=8.12.4 <=9.6.0 | | ^8.0.0 | **8.12.2** | >=8.7.0 <=8.12.2 | Additionally, you can check the supported versions of Node and Typescript for the version of Angular you are using in the [Angular docs](https://angular.dev/reference/versions#actively-supported-versions). ## Nx and Angular Rspack version compatibility matrix Below is a reference table that matches versions of [Angular Rspack](/docs/technologies/angular/angular-rspack/introduction) to the versions of Angular (lower than 20.2.0) and Nx that is compatible with it. {% aside type="note" title="Newer Angular versions" %} The table below only shows the version of Angular Rspack that is compatible with Angular versions lower than 20.2.0. Starting with Angular 20.2.0, the Angular Rspack version to install is aligned with the Nx version, so refer to [the table above](#nx-and-angular-version-compatibility-matrix). {% /aside %} | Angular Version | Angular Rspack Version | Nx Version | | --------------- | ---------------------- | ------------------- | | ~20.1.0 | **~21.2.0** | >= 21.1.0 <= 21.5.0 | | ~20.0.0 | **~21.1.0** | >= 21.1.0 <= 21.5.0 | | ~19.2.0 | **~20.8.0** | >= 20.8.1 <= 21.1.0 | | ~19.2.0 | **~20.7.0** | >= 20.8.1 <= 21.1.0 | | ~19.2.0 | **~20.6.0** | >= 20.6.0 <= 21.1.0 | --- ## Angular - Set up Compodoc for Storybook on Nx [Compodoc](https://compodoc.app/) is a documentation tool for Angular applications. When used with Storybook, Compodoc can automatically generate documentation for your Angular components by analyzing their TypeScript code, decorators, and JSDoc comments. ## What is compodoc? Compodoc automatically generates documentation by: - Analyzing TypeScript interfaces and classes - Reading Angular decorators (`@Input`, `@Output`, etc.) - Parsing JSDoc comments - Generating `argTypes` for Storybook controls ## Setting up Compodoc with Storybook ### Prerequisites You need to have Storybook already configured for your Angular project. If you haven't set up Storybook yet, follow the [Angular Storybook setup guide](/docs/kb/overview-angular). ### Install compodoc Install Compodoc as a dev dependency: ```shell npm install --save-dev @compodoc/compodoc ``` ### Configure Compodoc in your Storybook Add Compodoc configuration to your Storybook's `main.ts` file: ```typescript // .storybook/main.ts import type { StorybookConfig } from '@storybook/angular'; const config: StorybookConfig = { stories: ['../src/**/*.stories.@(js|jsx|ts|tsx|mdx)'], addons: [], framework: { name: '@storybook/angular', options: {}, }, typescript: { check: false, checkOptions: {}, reactDocgen: 'react-docgen-typescript', reactDocgenTypescriptOptions: { shouldExtractLiteralValuesFromEnum: true, propFilter: (prop) => prop.parent ? !/node_modules/.test(prop.parent.fileName) : true, compilerOptions: { allowSyntheticDefaultImports: false, esModuleInterop: false, }, }, }, docs: { autodocs: true, }, }; export default config; ``` ### Generate compodoc documentation Add a script to your `package.json` to generate Compodoc documentation: ```json // package.json { "scripts": { "docs:json": "compodoc -p tsconfig.json -e json -d .", "storybook": "npm run docs:json && nx storybook your-project" } } ``` ### Configure your Angular component for better documentation Add JSDoc comments and use TypeScript interfaces for better documentation generation: ```typescript // button.component.ts import { Component, Input, Output, EventEmitter } from '@angular/core'; export interface ButtonProps { /** The button label */ label: string; /** Primary or secondary styling */ primary?: boolean; /** Disabled state */ disabled?: boolean; /** Button size */ size?: 'small' | 'medium' | 'large'; } /** * A customizable button component */ @Component({ selector: 'app-button', template: ` `, styleUrls: ['./button.component.css'], }) export class ButtonComponent implements ButtonProps { /** The button label */ @Input() label: string = ''; /** Primary or secondary styling */ @Input() primary: boolean = false; /** Disabled state */ @Input() disabled: boolean = false; /** Button size */ @Input() size: 'small' | 'medium' | 'large' = 'medium'; /** Click event handler */ @Output() onClick = new EventEmitter(); get classes(): string { return [ 'btn', this.primary ? 'btn-primary' : 'btn-secondary', `btn-${this.size}`, ].join(' '); } } ``` ### Enhanced story with Compodoc integration Create a story that leverages the Compodoc-generated documentation: ```typescript // button.stories.ts import type { Meta, StoryObj } from '@storybook/angular'; import { ButtonComponent } from './button.component'; const meta: Meta = { title: 'Components/Button', component: ButtonComponent, parameters: { docs: { description: { component: 'A customizable button component with multiple variants and sizes.', }, }, }, argTypes: { size: { control: { type: 'select' }, options: ['small', 'medium', 'large'], }, onClick: { action: 'clicked' }, }, }; export default meta; type Story = StoryObj; export const Primary: Story = { args: { primary: true, label: 'Primary Button', size: 'medium', }, }; export const Secondary: Story = { args: { primary: false, label: 'Secondary Button', size: 'medium', }, }; export const Large: Story = { args: { label: 'Large Button', size: 'large', }, }; export const Small: Story = { args: { label: 'Small Button', size: 'small', }, }; export const Disabled: Story = { args: { label: 'Disabled Button', disabled: true, }, }; ``` ## Benefits of using Compodoc with Storybook 1. **Automatic Documentation**: Compodoc generates component documentation automatically from your TypeScript code 2. **Better Controls**: Enhanced Storybook controls based on your component's `@Input` properties 3. **Type Safety**: Leverages TypeScript types for accurate documentation 4. **JSDoc Integration**: Uses JSDoc comments for rich descriptions 5. **Less Maintenance**: Documentation stays in sync with your code ## Running Storybook with compodoc With the setup complete, run your Storybook: ```shell npm run storybook ``` This will generate the Compodoc documentation and then start Storybook with enhanced documentation for your Angular components. --- ## Set Up Application Proxies It is useful to set up frontend proxies to your backend app during local development. By proxying requests, you won't need to set up [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) when communicating with the backend. ## Webpack dev-server Webpack dev-server has built-in support for [proxies](https://webpack.js.org/configuration/dev-server/#devserverproxy). For example, if you want to proxy all requests to `/api` to `http://localhost:3000`, then you would use this configuration: ```js // webpack.config.js // ... module.exports = { //... devServer: { proxy: [ { context: ['/api'], target: 'http://localhost:3000', }, ], }, }; ``` Say that your frontend app is at port `4200`, then requests to `http://localhost:4200/api` will proxy to the backend app at port `3000`. If you don't want `/api` to be passed along, then use `pathRewrite`. ```js // webpack.config.js // ... module.exports = { //... devServer: { proxy: [ { context: ['/api'], target: 'http://localhost:3000', pathRewrite: { '^/api': '' }, }, ], }, }; ``` ## Vite server Vite server has built-in support for [proxies](https://vite.dev/config/server-options#server-proxy). For example, if you want to proxy all requests to `/api` to `http://localhost:3000`, then you would use this configuration: ```js // vite.config.ts // ... export default defineConfig({ // ... server: { proxy: { '/api': 'http://localhost:3000', }, }, }); ``` Say that your frontend app is at port `4200`, then requests to `http://localhost:4200/api` will proxy to the backend app at port `3000`. If you don't want `/api` to be passed along, then use `rewrite`. ```js // vite.config.ts // ... export default defineConfig({ // ... server: { proxy: { '/api': { target: 'http://localhost:3000', rewrite: (path) => path.replace(/^\/api/, ''), }, }, }, }); ``` ## Automatically configure frontend proxies {% aside type="caution" title="Not recommended for new projects" %} We recommend configuring proxies using support from existing tooling. Webpack and Vite both come with proxy support out of the box, as do most modern tools. {% /aside %} The Node, Nest and Express app generators have an option to configure proxy API requests. This can be done by passing the `--frontendProject` with the project name you wish to enable proxy support for. ```shell nx g @nx/node:app apps/ --frontendProject my-react-app nx g @nx/nest:app apps/ --frontendProject my-react-app nx g @nx/express:app apps/ --frontendProject my-react-app ``` This command will generate and configure a `proxy.conf.json` file that will be used by the frontend project's `serve` target to redirect calls to `/api` to instead go to `http://localhost:3000/api`. ```json // /apps/my-react-app/proxy.conf.json { "/api": { "target": "http://localhost:3000", "secure": false } } ``` --- ## Assignment Rules Assignment rules allow you to control which tasks can run on which agents. Save on agent costs by provisioning different sizes of agents to suite the individual needs of your tasks. Ensure resource intensive targets like `e2e-ci` and `build` have what they need by using larger agents and with a specified parallelism. Lighter tasks like `lint` and `test` can run on smaller agents. Assignment rules are written in `yaml` and reach Nx Cloud through [`dte.assignment-rules`](/docs/reference/nx-cloud/ci-config#dteassignment-rules) in `.nx/ci-config.yaml`. You can use assignment rules with [Nx Agents](/docs/features/ci-features/distribute-task-execution). The rules reach a run in one of three ways, and the wrapper differs between them: - **Inline.** Set `dte.assignment-rules` to the list of rules itself, with no wrapper. - **In their own file.** Set `dte.assignment-rules` to that file's path. The file wraps the list in a top-level `assignment-rules:` key. - **Alongside your distribution config.** Put an `assignment-rules:` key next to `distribute-on:` in the file that `dte.distribute-on` points at. The examples below use the second and third forms, since a rules file is easier to review than a long inline list. The inline form looks like this: ```yaml # .nx/ci-config.yaml dte: distribute-on: 5 linux-medium-js assignment-rules: - targets: - e2e-ci run-on: - agent: linux-large-js ``` ## How to define an assignment rule Each assignment rule has one of the following properties that it matches against tasks: `projects`, `targets`, and/or `configurations`. You can provide a list of globs to match against the tasks in your workspace. It also has a list of possible [agent types](/docs/kb/launch-templates) that tasks with the matching properties can run on. Rules are defined in yaml like the following: {% tabs syncKey="dte-kind" %} {% tabitem label="Assignment rules with Nx Agents" %} ```yaml // .nx/workflows/distribution-config.yaml distribute-on: default: 5 linux-medium-js, 5 linux-large-js assignment-rules: - projects: - app1 targets: - e2e-ci* configurations: - production run-on: - agent: linux-medium parallelism: 5 - targets: - lint - build run-on: - agent: linux-large parallelism: 10 ``` {% /tabitem %} {% tabitem label="Assignment rules when you bring your own compute" %} ```yaml // .nx/workflows/assignment-rules.yaml assignment-rules: - projects: - app1 targets: - e2e-ci* run-on: - agent: linux-medium parallelism: 5 - targets: - lint,build run-on: - agent: linux-large parallelism: 10 ``` {% /tabitem %} {% /tabs %} The above rule will match any task that has a project named `app1`, any targets that begin with `e2e-ci`, and a configuration named `production`. Any tasks that match this rule will only be allowed to run on agents with `linux-medium-js` launch templates. Agents assigned these tasks will also be able to execute up to `5` tasks in parallel. The second rule above will match any task that has a `lint` or `build` target. These tasks only run on `linux-large` agents and up to 10 tasks can be executed in parallel by agents of that type. You can mix and match any of the criteria in an assignment rule provided that you follow the constraints: - At least one of the following properties is defined: `projects`, `targets`, `configurations`. - There is at least one [agent type](/docs/kb/launch-templates) specified in the `run-on` field. If no parallelism is specified, the parallelism of the executed command will be used instead. If that is not specified, then the parallelism will default to `1` - For assignment rules with Nx Agents, every changeset in your `distribute-on` field must include at **least one agent** that matches each agent type specified in the `run-on` field across all assignment rules. For example, if your rules distribute tasks on `linux-small-js`, `linux-medium-js`, and `linux-large-js`, then at least one agent of each type must be available; otherwise, tasks associated with those rules cannot be executed. {% aside type="note" title="If you bring your own compute, you must define your own agent types" %} You must define your own agent types and attach them to your agents using the `NX_AGENT_LAUNCH_TEMPLATE` environment variable. Ensure that for each `run-on` field in your assignment rules, you have corresponding agents in your agent pool that have the same agent type. See below for an [example](#using-assignment-rules-when-you-bring-your-own-compute) of how to define your own agent types. {% /aside %} ### Assignment rule property reference #### projects A list of string globs that matches against projects in your workspace. #### targets A list of string globs that matches against targets in your workspace. #### configurations A list of string globs that matches against configurations in your workspace. #### run-on Specification of which agent and how to run your tasks: - **agent**: the type of agent to run on (`linux-medium`, `linux-large`) - **parallelism**: the number of parallel executions allowed for agents of a given type ### Glob reference You can use globs for better control over how to define your assignment rules. ##### `*` matches zero or more characters - ✅ `lint*` matches `lint-js`, `linting-test` - ✅ `*test*` matches `business-test-2`, `test-12`, `10-test` - ❌ `lint*` does not match `eslint`, `lin-test`, `lin` ##### `?` matches exactly one character - ✅ `app?` matches `app1`, `app3`, `apps` - ❌ `app?` does not match `app10`, `apps1`, `bus-app1` ##### `!` at start negates the pattern - ✅ `!prod` matches `development`, `staging` - ❌ `!prod` does not match `prod` #### List delimited globs If you provide a list of globs to an individual rule property (`projects`, `targets`, `configurations`), it will match any of the patterns for that given property. ```yaml // .nx/workflows/assignment-rules.yaml assignment-rules: - targets: - e2e-ci* - lint* run-on: - agent: linux-medium parallelism: 2 ``` The following rule will match the following tasks: - starts with `e2e-ci` (i.e `e2e-ci--playwright-button-test`) - starts with `lint` (i.e `lint-js`) #### Comma delimited globs Within each list entry, you can define a comma delimited list of globs. This notation will match a given property only if all globs match. ```yaml // .nx/workflows/assignment-rules.yaml assignment-rules: - targets: - 'e2e-ci*,*server-test' - 'lint*' run-on: - agent: linux-large parallelism: 5 ``` The following rule will match the following tasks: - starts with `e2e-ci` and ends with `server-test` (i.e `e2e-ci--playwright-server-test`) - starts with `lint` (i.e `lint-js`) ### Configuring parallelism through assignment rules You can specify how many tasks of a certain type can run in parallel on a particular agent. Each agent object within the `run-on` list can have the `parallelism` property. Configuring parallelism through your assignment rules will override the other parallelism configurations in your workspace. For a given command run with DTE, parallelism is determined by in the following order: 1. Parallelism defined in the assignment rules 2. Parallelism defined in the `--parallel` flag in your command 3. Parallelism defined in your `nx.json` file (`parallel: 3`) If none of these methods of configuring parallelism are used, the parallelism of executed tasks will default to `1`. Note that there are two special cases for parallelism with assignment rules where the behaviour may differ. 1. All tasks that are marked as `non-cacheable` (they are configured with `cache: false`) will be run with a parallelism of `1` regardless of the parallelism defined in the assignment rules or execution. This is usually the case with tasks such as `e2e-ci` which may requires each process to have its own environment or resources to run. 2. Assignment rules only apply to distributed executions (DTE). If you want to run multiple tasks in parallel without DTE (via the `--no-dte` flag), you will need to use the `--parallel` flag in your commands. ```shell nx affected -t lint test built --no-dte --parallel=3 ``` #### Assignment rules parallelism example ```shell // .github/workflows/ci.yaml nx affected -t lint test build --parallel=3 ``` ```yaml // .nx/workflows/assignment-rules.yaml assignment-rules: - targets: - lint - test run-on: - agent: linux-medium parallelism: 4 - targets: - build run-on: - agent: linux-large ``` In the above example, the `lint` and `test` targets will run on `linux-medium` agents with a parallelism of `4` as defined within the rules. The `build` target will run on `linux-large` agents, but note that there is no parallelism defined for that target. The parallelism for `build` tasks will then use the value provided by the `--parallel` flag, which is `3`. #### Setting default parallelism for multiple tasks Putting globs and parallelism together, you can set a default parallelism for all tasks within your executions. Take the following statement: > Only `e2e-ci` tasks should run on large agents. All other tasks should run on medium agents with a parallelism of 5. This can be represented as the following yaml config. ```yaml assignment-rules: # Since this rule was defined first and `targets` has a higher precedence order, # e2e-ci tasks will use this rule - targets: - e2e-ci* run-on: - agent: linux-large # This rule will match all projects in your workspace - projects: - '*' run-on: - agent: linux-medium parallelism: 5 ``` ## Assignment rule precedence Having multiple assignment rules means that often rules may overlap or apply to the same tasks. For a given task, only one rule will ever be applied. To determine which rule take priority, a rule of thumb is that **more specific rules take precedence over more general rules**. You can consult our precedence chart for a full list of rule priorities. A checkmark indicates that a rule has a particular property defined. If two rules have the same priority based on the below chart, the rule that appears first in the `assignment-rules` list will take precedence. | Priority | Configuration | Target | Project | | :------: | :-----------: | :----: | :-----: | | 1 | ✅︎ | ✅︎ | ✅︎ | | 2 | ✅︎ | ✅︎ | | | 3 | ✅︎ | | ✅︎ | | 4 | | ✅︎ | ✅︎ | | 5 | ✅︎ | | | | 6 | | ✅︎ | | | 7 | | | ✅ | ### Rule precedence example In this example, the task defined below can match multiple assignment rules. However, since the second rule specifies all three properties (`projects`, `targets`, and `configurations`) rather than just two (`projects` and `targets`), it takes precedence, and we automatically apply the second rule when distributing the task. ```json {% title="A task from your workspace" %} { "project": "app1", "target": "build", "configuration": "production" } ``` ```yaml // .nx/workflows/distribution-config.yaml assignment-rules: - projects: - app1 targets: - build run-on: - agent: linux-large-js parallelism: 3 # A task for app1:build:production will use this rule because it is more # specific (matches three properties instead of just two) - projects: - app1 targets: - build configurations: - production run-on: - agent: linux-medium-js parallelism: 5 ``` ## Using assignment rules when you bring your own compute {% aside type="note" title="Enterprise Feature" %} Bringing your own compute requires the [Nx Enterprise plan](https://nx.dev/enterprise?utm_source=nx.dev&utm_medium=callout&utm_campaign=bring-your-own-compute). [Nx Agents](/docs/features/ci-features/distribute-task-execution) distributes your tasks on all plans. {% /aside %} A typical `assignment-rules.yaml` file might look like this: ```yaml // .nx/workflows/assignment-rules.yaml assignment-rules: - projects: - app1 targets: - build configurations: - production run-on: - agent: linux-medium parallelism: 5 - agent: linux-large - targets: - lint run-on: - agent: linux-medium - configurations: - development run-on: - agent: linux-medium - agent: linux-large ``` Note that the agent types supplied in the `run-on` property will be used to determine which agents will have rules applied to them. You can choose to name your agent types anything you want, but they must be set on your agents via the `NX_AGENT_LAUNCH_TEMPLATE` environment variable. You can then reference your assignment rules file from `.nx/ci-config.yaml`: ```yaml # .nx/ci-config.yaml dte: distribute-on: manual assignment-rules: .nx/workflows/assignment-rules.yaml lifecycle: stop-after: - e2e-ci ``` The following is an example of what this looks like within a Github Actions pipeline: ```yaml // .github/workflows/ci.yaml --- jobs: main: name: Main Job runs-on: ubuntu-latest steps: - ... # setup steps for your main job - run: npx nx start-nx-agents - ... # Nx commands you want to distribute medium-agents: name: Agents ${{ matrix.agent }} runs-on: group: medium-agents strategy: matrix: agent: [1, 2, 3] steps: - name: Checkout uses: actions/checkout@v7 - uses: actions/setup-node@v6 with: node-version: 24 cache: 'npm' - ... # other setup steps you may need - name: Install dependencies run: npm ci - name: Start Agent ${{ matrix.agent }} run: npx nx start-agent env: NX_AGENT_NAME: ${{ matrix.agent }} NX_AGENT_LAUNCH_TEMPLATE: 'linux-medium' # This value needs to match one of the 'run-on' values defined in the assignment rules large-agents: name: Agents ${{ matrix.agent }} runs-on: group: large-agents strategy: matrix: agent: [4, 5, 6] steps: - name: Checkout uses: actions/checkout@v7 - uses: actions/setup-node@v6 with: node-version: 24 cache: 'npm' - ... # other setup steps you may need - name: Install dependencies run: npm ci - name: Start Agent ${{ matrix.agent }} run: npx nx start-agent env: NX_AGENT_NAME: ${{ matrix.agent }} NX_AGENT_LAUNCH_TEMPLATE: 'linux-large' # This value needs to match one of the 'run-on' values defined in the assignment rules ``` ## Using assignment rules with dynamic Nx Agents A typical `distribution-config.yaml` file might look like this: ```yaml // .nx/workflows/distribution-config.yaml distribute-on: default: 5 linux-medium-js, 5 linux-large-js assignment-rules: - projects: - app1 targets: - build configurations: - production run-on: - agent: linux-large-js - targets: - lint run-on: - agent: linux-medium-js parallelism: 3 - configurations: - development run-on: - agent: linux-medium-js - agent: linux-large-js ``` You can then reference your distribution configuration from `.nx/ci-config.yaml`: ```yaml # .nx/ci-config.yaml dte: distribute-on: .nx/workflows/distribution-config.yaml lifecycle: stop-after: - e2e-ci ``` Your main job starts the run without repeating any of that configuration: ```yaml {% meta="{9}" %} // .github/workflows/main.yaml ... jobs: - job: main name: Main Job ... steps: ... - run: npx nx start-nx-agents - .. ``` ### More examples of assignment rules with dynamic agents #### Invalid assignment rules example ```yaml // .nx/workflows/distribution-config.yaml distribute-on: # Invalid changeset that is missing `linux-large-js`. Tasks assigned to large agents won't be able to execute. small-changeset: 1 linux-small-js, 2 linux-medium-js medium-changeset: 2 linux-small-js, 2 linux-medium-js, 3 linux-large-js large-changeset: 3 linux-small-js, 3 linux-medium-js, 4 linux-large-js assignment-rules: # Missing one of `projects`, `targets`, `configurations` - run-on: - agent: linux-medium-js parallelism: 1 - agent: linux-large-js parallelism: 3 # Missing `run-on` - targets: - lint configurations: - production # Agent type not found in any of the `distribute-on` changesets - projects: - lib1 targets: - test run-on: - agent: linux-extra-large-js ``` #### Valid assignment rules example ```yaml // .nx/workflows/distribution-config.yaml distribute-on: default: 3 linux-small-js, 2 linux-medium-js, 1 linux-large-js # All rules below are valid assignment rules assignment-rules: - projects: - app1 run-on: - agent: linux-medium-js - agent: linux-large-js - targets: - lint configurations: - production run-on: - agent: linux-large-js parallelism: 10 - projects: - lib1 targets: - test run-on: - agent: linux-medium-js ``` ## Deprecated assignment rules Assignment rules used to be defined with the following schema. However, this schema did not support multi-glob matching, nor parallelism. Rules defined in this format will still work, but we recommend updating them to the new schema. ```yaml // .nx/workflows/distribution-config.yaml # We recommend updating your assignment rules to the most recent schema assignment-rules: - project: app1 # replaced by `projects` target: e2e-ci* # replaced by `targets` configuration: production # replaced by `configurations` runs-on: # replaced by `run-on` - linux-medium-js - linux-large-js ``` --- ## Automate GitHub Releases Nx Release can automate the creation of [GitHub releases](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository) for you. GitHub releases are a great way to communicate the changes in your projects to your users. ## Authenticating with GitHub In order to be able to create the release on GitHub, you need to provide a valid token which can be used for authenticating with the GitHub API. Nx release supports two main ways of doing this: 1. In all environments it will preferentially check for an environment variable (the environment variable can either be called `GITHUB_TOKEN` or `GH_TOKEN`). Please ensure that this environment variable is set in your CI environment (and that the token it has been set to has been configured with the appropriate permissions to create releases) before attempting to create a release in CI. 2. It can also detect if you have a valid, authenticated installation of the [official `gh` CLI tool](https://cli.github.com), and leverage that automatically as a fallback when no environment variable is set. ## GitHub release contents When a GitHub release is created, it will include the changelog that Nx Release generates with entries based on the changes since the last release. Nx Release will parse the `feat` and `fix` type commits according to the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification and sort them into appropriate sections of the changelog. Take a look at the [Nx releases page](https://github.com/nrwl/nx/releases) to see examples of GitHub releases generated by Nx Release. ## Enable release creation To enable GitHub release creation for your workspace, set `release.changelog.workspaceChangelog.createRelease` to `'github'` in `nx.json`: ```jsonc // nx.json { "release": { "changelog": { "workspaceChangelog": { "createRelease": "github", }, }, }, } ``` ## Preview the release Use `nx release --dry-run` to preview the GitHub release instead of creating it. This allows you to see what the release will look like without pushing anything to GitHub. ## Disable file creation Since GitHub releases contain the changelog, you may wish to disable the generation and management of local the `CHANGELOG.md` file. To do this, set `release.changelog.workspaceChangelog.file` to `false` in `nx.json`: ```jsonc // nx.json { "release": { "changelog": { "workspaceChangelog": { "file": false, "createRelease": "github", }, }, }, } ``` Note: When configured this way, Nx Release will not delete existing changelog files, just ignore them. ## Project level changelogs Nx Release supports creating GitHub releases for project level changelogs as well. This is particularly useful when [releasing projects independently](/docs/guides/nx-release/release-projects-independently). To enable this, set `release.changelog.projectChangelogs.createRelease` to `'github'` in `nx.json`: ```jsonc // nx.json { "release": { "changelog": { "projectChangelogs": { "createRelease": "github", }, }, }, } ``` {% aside type="caution" title="Project and Workspace GitHub Releases" %} Nx Release does not support creating GitHub releases for both project level changelogs and the workspace changelog. You will need to choose one or the other. {% /aside %} ## Customizing the GitHub instance to use GitHub enterprise server If you are not using github.com, and instead using a self-hosted GitHub Enterprise Server instance, you can use a configuration object instead of the string for "createRelease" to provide the relevant hostname, and optionally override the API base URL, although this is not typically needed as it will default to `https://${hostname}/api/v3`. ```jsonc // nx.json { "release": { "changelog": { "workspaceChangelog": { "createRelease": { "provider": "github-enterprise-server", "hostname": "github.example.com", }, }, }, }, } ``` --- ## Automate GitLab Releases Nx Release can automate the creation of [GitLab releases](https://docs.gitlab.com/user/project/releases/) for you. GitLab releases are a great way to communicate the changes in your projects to your users. ## Authenticating with GitLab In order to be able to create the release on GitLab, you need to provide a valid token which can be used for authenticating with the GitLab API. Nx release supports two main ways of doing this: 1. In all environments it will preferentially check for an environment variable (the environment variable can either be called `GITLAB_TOKEN` or `GL_TOKEN`). 2. In GitLab CI it will check for and use the automatically created GitLab token in the `CI_JOB_TOKEN` environment variable. ## GitLab release contents When a GitLab release is created, it will include the changelog that Nx Release generates with entries based on the changes since the last release. Nx Release will parse the `feat` and `fix` type commits according to the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification and sort them into appropriate sections of the changelog. ## Enable release creation To enable GitLab release creation for your workspace, set `release.changelog.workspaceChangelog.createRelease` to `'gitlab'` in `nx.json`: ```jsonc // nx.json { "release": { "changelog": { "workspaceChangelog": { "createRelease": "gitlab", }, }, }, } ``` ## Preview the release Use `nx release --dry-run` to preview the GitLab release instead of creating it. This allows you to see what the release will look like without pushing anything to GitLab. ## Disable file creation Since GitLab releases contain the changelog, you may wish to disable the generation and management of the local `CHANGELOG.md` file. To do this, set `release.changelog.workspaceChangelog.file` to `false` in `nx.json`: ```jsonc // nx.json { "release": { "changelog": { "workspaceChangelog": { "file": false, "createRelease": "gitlab", }, }, }, } ``` Note: When configured this way, Nx Release will not delete existing changelog files, just ignore them. ## Project level changelogs Nx Release supports creating GitLab releases for project level changelogs as well. This is particularly useful when [releasing projects independently](/docs/guides/nx-release/release-projects-independently). To enable this, set `release.changelog.projectChangelogs.createRelease` to `'gitlab'` in `nx.json`: ```jsonc // nx.json { "release": { "changelog": { "projectChangelogs": { "createRelease": "gitlab", }, }, }, } ``` {% aside type="caution" title="Project and Workspace GitLab Releases" %} Nx Release does not support creating GitLab releases for both project level changelogs and the workspace changelog. You will need to choose one or the other. {% /aside %} ## Customizing the GitLab instance If you are not using gitlab.com, and are instead using a self-hosted GitLab instance, you can use a configuration object instead of the string for "createRelease" to provide the relevant hostname, and optionally override the API base URL, although this is not typically needed as it will default to `https://${hostname}/api/v4`. ```jsonc // nx.json { "release": { "changelog": { "workspaceChangelog": { "createRelease": { "provider": "gitlab", "hostname": "gitlab.example.com", }, }, }, }, } ``` --- ## Storybook best practices for making the most out of Nx ## Purpose of this guide The purpose of this guide is to help you [set up Storybook in your Nx workspace](/docs/technologies/test-tools/storybook/introduction) so that you can get the most out of Nx and its powerful capabilities. ## When to use Storybook Usually, Storybook is mainly used for two reasons. Testing and documentation. You can read more on when and why to use Storybook in the [Why Storybook in 2022?](https://storybook.js.org/blog/why-storybook-in-2022/) article and also in the [Introduction to Storybook](https://storybook.js.org/docs/react/get-started/introduction) documentation page. ### Testing Storybook helps you test your UIs. You can read more about testing with Storybook in the [How to test your UIs with Storybook](https://storybook.js.org/docs/writing-tests) documentation page. Essentially, Storybook uses the stories as a starting point for testing. ### Documentation Storybook helps you document your UI elements, or your design system, effectively and in an interactive way. You can read more in the [How to document components](https://storybook.js.org/docs/react/writing-docs/introduction) documentation page. Essentially, you can use Storybook to publish a catalog of your components. A catalog that you can share with the design team, the developer team, the product team, anyone else in the product development process, or even the client. The components are isolated, interactive, and can be represented in all possible forms that they can take (e.g. for a button: enabled, disabled, active, etc). You can read more about publishing your Storybook in the [Publish Storybook](https://storybook.js.org/docs/react/sharing/publish-storybook) documentation page. ## Nx and Storybook Now let's see how Nx can be used to accommodate both of these pillars of Storybook. Nx takes lots of the burden off your arms when setting up Storybook initially. It essentially provides you with all that you need to start using Storybook's capabilities (testing and documentation) right away, without having to write a single line of code. ### Development tools First, let's see what Nx offers, when you are in the process of developing a project with Storybook. #### Configuration generation You can generate the Storybook configuration files and settings using the Nx [`@nx/storybook:configuration` generator](/docs/technologies/test-tools/storybook/generators#configuration). You can read more about configuring Storybook with Nx in our [`@nx/storybook` introduction page](/docs/technologies/test-tools/storybook/introduction#generating-storybook-configuration). With Nx, you configure Storybook for each individual project. #### Stories generation If you are on a project using Angular, React or React Native, you can also generate stories for your components. You can do so either by using each package's `storybook-configuration` generators or by using the `stories` generator, if you already have Storybook configured for your project. If your project is not configured yet, check out one of these guides: - [Set up Storybook for React (and Next.js) Projects](/docs/kb/storybook-for-react) - [Set up Storybook for Angular Projects](/docs/kb/overview-angular) - [Set up Storybook for Vue Projects](/docs/kb/overview-vue) If your project is [already configured](/docs/technologies/test-tools/storybook/introduction), you can use the `stories` generator: - [React (and Next.js) stories generator](/docs/technologies/react/generators#stories) - [Angular stories generator](/docs/technologies/angular/generators#stories) - [Vue stories generator](/docs/technologies/vue/generators#stories) The stories generator will read your inputs (if you're using Angular), or your props (if you're using React), and will generate stories with the corresponding arguments/controls already prefilled. #### Storybook interaction tests [Storybook interaction tests](https://storybook.js.org/docs/writing-tests/interaction-testing) allow you to test user interactions within your Storybook stories. It enhances your [Storybook](https://storybook.js.org/) setup, ensuring that not only do your components look right, but they also work correctly when interacted with. Nx will generate interaction tests for your stories. You can read more in our [Setting up Storybook Interaction Tests with Nx guide](/docs/kb/storybook-interaction-tests). ### CI/CD tools Now let's see how Nx helps in the CI/CD journey, as well. #### Interaction tests in your CI You can set up your interaction tests to run as part of your CI. You can read more in the [Storybook docs](https://storybook.js.org/docs/writing-tests/integrations/test-runner#set-up-ci-to-run-tests). #### Serve When you are configuring Storybook, Nx [adds a serve and a build target for Storybook](/docs/technologies/test-tools/storybook/introduction#generating-storybook-configuration) in your `project.json`, as we explained above. You can use these targets to [serve](/docs/technologies/test-tools/storybook/executors#storybook) and [build](/docs/technologies/test-tools/storybook/executors#build) storybook locally, and also in production. Cypress will also use these targets when firing up the e2e tests. While developing, you can serve your Storybooks locally to see if your components work and look as expected. This can help you and speed up the development and debugging process (no need to fire up a complex dev stack). #### Build and deploy The build and deploy step usually comes in handy when you are ready to use Storybook for documentation, and you want to publish it. The [building](/docs/technologies/test-tools/storybook/executors#build) step of Storybook is integrated in the Nx ecosystem, as explained above, and you can trigger your Storybook builds as you would trigger any other build inside your workspace. When you publish your organization's Storybook, as a result, ideally, you would want to have one shareable Storybook page/application living under one URL, that you can share. With Nx, you can build your Storybook and it will be ready for deployment. **However**, at this point, you have one Storybook per project in your workspace, and you could end up with far too many Storybooks that are built and ready for deployment. This is not ideal, and does not accomplish the ultimate goal of "one shareable documentation page". In the following section, we are going to see how to set up Storybook in these cases, to get the most out of Nx. ## How to set up Storybook to get the most out of Nx ### Philosophy Setting up Storybook on Nx reflects - and takes advantage of - the [mental model](/docs/concepts/mental-model) of Nx. What that means, in essence, is that you still maintain the individual Storybook instances (per project) which you use for testing and local development, but you also keep one extra "container" for publishing, that serves as a single entry point. Let's see this in more detail. #### Local development and testing ##### Development and debugging In the process of setting up Storybook in your Nx workspace that we described above, you end up with one Storybook instance per project. That way, you can use your project's Storybook targets to serve, test and build Storybook: ```shell nx storybook my-project ``` and ```shell nx build-storybook my-project ``` and ```shell nx test-storybook my-project ``` This feature is extremely useful when developing locally. The containerized stories in your Storybook are the only ones that are built/served/tested when you want to debug just one component, or just one library. You don't have to wait for a huge Storybook containing all your stories in your repository to fire up. You just need to wait for the Storybook of a single project to start. This speeds up the process. ##### Caching, affected, dependency management Since each Storybook, in this case, is attached to a project, so is the serving of Storybook and the building of Storybook and the e2e tests for that project. That means that Nx is aware of these tasks, so it caches them, it knows when to fetch them from the cache or re-run them according to the affected status of that project. It also knows that project's dependencies and knows which things to rebuild before each task. #### Publishing When you are publishing your Storybook, you can follow the principles described in the [project size](/docs/kb/project-size) decision page. The general idea is to have one central Storybook container, into which you are going to gather your stories from multiple libraries. You can think of the central Storybook container as a grouping of similar-concept or same-scope UI parts of your workspace. In the same way you are scoping libraries, you can group your stories as well. Then, according to your use-case, you can have one central Storybook for your whole workspace, importing all the stories from all the projects. Alternatively, you can have one Storybook per "scope", which imports all the stories from projects the same scope. Or even one Storybook per application, importing all the stories of all the libraries that it is depending on. As you can see, there are many options, and you can choose the one that best suits your needs. {% aside type="note" title="Storybook Composition" %} In order to achieve some things mentioned above, you may use [Storybook Composition](/docs/kb/storybook-composition-setup). However, in this case, you would still need to build each project's Storybook individually, and also deploy it individually. So in the cases where you have multiple projects, Storybook Composition would not be very efficient. {% /aside %} Before moving on to the examples section, it could be useful to read the [Library Types](/docs/kb/project-dependency-rules) documentation page and the [Grouping libraries](/docs/kb/folder-structure) documentation page. These could help you decide which way fits your use case better. ## Examples / use cases You can check out the following examples (recipes) to see publishing strategies for Storybook in Nx: - [One main Storybook instance for all projects](/docs/kb/one-storybook-for-all) - [One Storybook instance per scope](/docs/kb/one-storybook-per-scope) - [One main Storybook instance using Storybook Composition](/docs/kb/one-storybook-with-composition) ## Conclusion We have given a direction towards the most efficient way to use Storybook in a Nx workspace, in a way that takes advantage of the all that Nx has to offer. We have covered the different ways to set up Storybook, and publish it, too. We have also covered the different use cases that apply to each of the solutions. If you have any questions or suggestions, please feel free to reach out to us on [GitHub](https://github.com/nrwl/nx), and don't hesitate to ask your questions or share your stories in the [Official Nx Discord Server](https://go.nx.dev/community). ### Nx & Storybook documentation You can find all Storybook-related Nx documentation in the [packages page](/docs/technologies/test-tools/storybook/introduction). --- ## Bring your own AI API key Bring your own key routes [self-healing CI](/docs/features/ci-features/self-healing-ci) through your own provider account, so inference bills to you instead of Nx Cloud. It is a paid add-on, available on the Enterprise plan and enabled for your organization by Nx. Contact your developer productivity engineer to turn it on. The **AI provider** row does not appear in your organization settings until it is enabled. {% aside type="caution" title="Claude models only" %} `fix-ci` runs on the Claude Agent SDK through the Anthropic Messages API. Gemini, GPT, and Nova do not work, even when your provider serves them. {% /aside %} ## Choose a mode Credentials reach `fix-ci` one of two ways. In **stored mode** Nx Cloud holds them for you, and in **CI env mode** they stay in your own CI secret store. Pick between them at `/orgs//ai-provider`, where they appear as **Use your own AI provider** and **Supply your credentials in CI env**. | Property | Stored in Nx Cloud | Set in your CI | | -------------------------- | ----------------------------------------- | ---------------------------------------------------------- | | Credential storage | Encrypted in Nx Cloud | Your CI secret store, Nx stores nothing | | Configured by | An organization admin, in the settings UI | Your CI or platform team, on the `fix-ci` step | | Providers | Anthropic, AWS Bedrock, Azure AI Foundry | Anthropic, AWS Bedrock, Azure AI Foundry, Google Vertex AI | | IAM and STS authentication | Not supported | Supported | ![The Self-Healing CI AI provider setting, showing the CI env and Nx Cloud provider options](../../../assets/nx-cloud/self-healing-ci-ai-provider-mode.png) Google Vertex AI and AWS IAM or STS credentials are only available in CI env mode. ## Set the model tiers Self-healing CI uses three model tiers. | Tier | Purpose | Environment variable | | --------- | -------------------------------------------------------------------- | ---------------------------------- | | Primary | Fix generation. Use Opus, or Sonnet 4.5 and later. | `ANTHROPIC_DEFAULT_SONNET_MODEL` | | Fallback | Used when the primary is overloaded. Must differ from the primary. | `ANTHROPIC_DEFAULT_FALLBACK_MODEL` | | Low-power | Background work such as classification and summaries. Usually Haiku. | `ANTHROPIC_DEFAULT_HAIKU_MODEL` | The variable names are slots, not model families. The primary tier reads `ANTHROPIC_DEFAULT_SONNET_MODEL` whichever model you put in it, so an Opus ID belongs there. Self-healing CI does not read `ANTHROPIC_DEFAULT_OPUS_MODEL`. Each tier takes the model identifier your provider publishes: - Anthropic: a Claude model name, from the [Claude model list](https://platform.claude.com/docs/en/about-claude/models/overview). - AWS Bedrock: a Bedrock model ID, shaped `anthropic.--v1:0`. - Azure AI Foundry: the deployment name you gave the model. - Google Vertex AI: a Vertex model ID. In stored mode you enter these as form fields, and in CI env mode you set the variables. ## Set up stored mode Select **Use your own AI provider**, then pick your provider and fill in the form. You need organization admin access, and a Claude model provisioned in your provider for each tier, which is a console step in AWS or Azure. Nx sets the runtime environment for every `fix-ci` run, so your CI configuration doesn't change. To rotate a credential, use **Replace credentials**. Clearing the provider deletes the stored secret. ## Set up CI env mode Set the provider variables wherever `fix-ci` runs. Nx stores nothing, and the Claude Agent SDK reads the variables at session start. `fix-ci` runs in two places, so the variables have to be present in both. It runs on your main CI job for failures outside agents, meaning non-distributed commands, and on Nx Agents for distributed tasks that fail there. On the main job, set the variables the way you manage any other CI secret. Nx Agents start clean, so [forward the variables to the agents](/docs/kb/launch-template-examples#pass-environment-variables). Forwarding covers Anthropic and Foundry outright. AWS and Vertex need more care on the agents, covered in their sections below. ## Anthropic In stored mode, supply an Anthropic API key. A gateway or proxy base URL is optional. In CI env mode, set the following variables. | Variable | Required | Value | | --------------------------- | -------- | -------------------------------------------------------------- | | `ANTHROPIC_API_KEY` | Yes | Anthropic API key | | `ANTHROPIC_BASE_URL` | No | Gateway or proxy origin. Omit for `api.anthropic.com` | | `ANTHROPIC_DEFAULT_*_MODEL` | No | The three tiers. Unset falls back to the default Claude models | ## AWS Bedrock In stored mode, supply a long-term Bedrock API key as a bearer token, plus the AWS region. Short-term keys expire and break self-healing once they do. In CI env mode, set the following variables. | Variable | Required | Value | | ----------------------------------------------------------------- | ------------- | ----------------------------------------------- | | `CLAUDE_CODE_USE_BEDROCK` | Yes | `1` | | `AWS_REGION` | Yes | Region serving your models, such as `us-west-2` | | `AWS_BEARER_TOKEN_BEDROCK` | One auth path | Long-term Bedrock API key as a bearer token | | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN` | One auth path | IAM or STS credentials | | `ANTHROPIC_DEFAULT_*_MODEL` | Yes | Bedrock model IDs | Through CI, `fix-ci` doesn't authenticate with a single key. It calls the AWS SDK, which walks the standard AWS credential chain to find whatever credentials the runner exposes, whether that's environment variables, a shared credentials file, or an attached role. `CLAUDE_CODE_USE_BEDROCK` and `AWS_REGION` only say where to go, and supplying the credentials is the part you set up. On your own runners, the AWS SDK can use whatever the machine already carries, such as an attached instance or task profile, a `~/.aws` file, or credentials your CI injects through OIDC. Often you set only the region and the Bedrock flag, and the existing AWS identity does the rest. Nx Agents carry none of your AWS identity, so pass the credentials as environment variables. A long-term Bedrock API key in `AWS_BEARER_TOKEN_BEDROCK` is the shortest path. For IAM or STS, mint short-lived credentials on your main job and forward `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`, and `AWS_REGION` to the agents. ## Azure AI Foundry The host is `services.ai.azure.com`, not `openai.azure.com`. Model values are deployment names, one per tier. The endpoint takes a resource name such as `my-resource`, or a bare origin such as `https://my-resource.services.ai.azure.com`, with no path. Nx and the Claude Agent SDK append `/anthropic/v1/messages` themselves, so a stored `/anthropic` doubles and returns a 404. The Nx Cloud form rejects a path and previews the resolved URL before you save. In stored mode, supply a Foundry API key and an endpoint in either of those two forms. In CI env mode, set the following variables. | Variable | Required | Value | | ---------------------------- | ---------------- | --------------------------------------------------------------------- | | `CLAUDE_CODE_USE_FOUNDRY` | Yes | `1` | | `ANTHROPIC_FOUNDRY_RESOURCE` | One of these two | Resource name only, such as `my-resource`. The SDK derives the origin | | `ANTHROPIC_FOUNDRY_BASE_URL` | One of these two | Bare origin, no path. Use for a custom origin | | `ANTHROPIC_FOUNDRY_API_KEY` | Yes | Foundry API key | | `ANTHROPIC_DEFAULT_*_MODEL` | Yes | Foundry deployment names | ## Google Vertex AI Vertex has no Nx Cloud form, so it runs in CI env mode only. | Variable | Required | Value | | -------------------------------- | -------- | -------------------------------------------------------------------------------------- | | `CLAUDE_CODE_USE_VERTEX` | Yes | `1` | | `ANTHROPIC_VERTEX_PROJECT_ID` | Yes | Google Cloud project ID | | `CLOUD_ML_REGION` | Yes | `global`, a multi-region such as `us` or `eu`, or a region such as `us-east5` | | `GOOGLE_APPLICATION_CREDENTIALS` | Yes | Path to a service account key file, unless Application Default Credentials are present | | `ANTHROPIC_DEFAULT_*_MODEL` | Yes | Vertex model IDs | Current models take the bare identifier, such as `claude-sonnet-5`. A dated snapshot separates the date with `@`, shaped `claude-@`. Authentication goes through the Google credential chain, which usually wants a service account key file rather than a single value. On your own runners, use Application Default Credentials through `gcloud auth application-default login`, or point `GOOGLE_APPLICATION_CREDENTIALS` at a key file already on the machine. Forwarding to Nx Agents passes values, not files, so carry the key file as a value and rebuild it on the agent: 1. Put the service account JSON into an environment variable. Base64-encode it to avoid newline and quoting problems. 1. Forward that variable to the agents. 1. In the init step of your launch template, decode the value back out to a file and point `GOOGLE_APPLICATION_CREDENTIALS` at that path. For region and model details, see [Claude on Google Vertex AI](https://code.claude.com/docs/en/google-vertex-ai). ## Verify the setup Open a CI pipeline execution and check the self-healing tab for fix suggestions. Your `nx fix-ci` step should run without logging errors about a missing or rejected provider. --- ## Bring Your Own Compute [Nx Agents](/docs/features/ci-features/distribute-task-execution) distributes your tasks across multiple machines on every Nx Cloud plan. On the [Nx Enterprise plan](https://nx.dev/enterprise?utm_source=nx.dev&utm_medium=callout&utm_campaign=bring-your-own-compute) you can also bring your own compute, running the agents on your own CI provider instead of Nx Cloud-hosted machines. The recipes below show how to set that up. Every recipe on this page uses the same [`.nx/ci-config.yaml`](/docs/reference/nx-cloud/ci-config) file. Setting `dte.distribute-on` to `manual` tells Nx Cloud to distribute tasks to the agents your own pipeline starts instead of provisioning Nx Agents. `lifecycle.stop-after` lists the targets that mark the run complete. ```yaml # .nx/ci-config.yaml dte: distribute-on: manual lifecycle: stop-after: - e2e-ci ``` The main job in each recipe starts the run with `nx start-nx-agents`, which reads that file. The command takes no configuration flags. {% tabs syncKey="ci-provider" %} {% tabitem label="GitHub" %} Our [reusable GitHub workflow](https://github.com/nrwl/ci) represents a good set of defaults that works for a large number of our users. However, reusable GitHub workflows come with their [limitations](https://docs.github.com/en/actions/using-workflows/reusing-workflows). If the reusable workflow above doesn't satisfy your needs you should create a custom workflow. If you were to rewrite the reusable workflow yourself, it would look something like this: ```yaml // .github/workflows/ci.yml name: CI on: push: branches: - main pull_request: # Needed for nx-set-shas when run on the main branch permissions: actions: read contents: read env: NX_CLOUD_DISTRIBUTED_EXECUTION: true # this enables DTE NX_BRANCH: ${{ github.event.number || github.ref_name }} NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # this is needed if our pipeline publishes to npm jobs: main: name: Nx Cloud - Main Job runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: # We need to fetch all branches and commits so that Nx affected has a base to compare against. fetch-depth: 0 filter: tree:0 # Set node/npm/yarn versions using volta - uses: volta-cli/action@v4 with: package-json-path: '${{ github.workspace }}/package.json' - name: Use the package manager cache if available uses: actions/setup-node@v6 with: node-version: 24 cache: 'npm' - name: Install dependencies run: npm ci - name: Derive appropriate SHAs for base and head for `nx affected` commands uses: nrwl/nx-set-shas@v5 - name: Start the Nx Cloud CI run using the settings in .nx/ci-config.yaml run: npx nx start-nx-agents - name: Check the formatting run: npx nx record -- nx format:check - name: Lint, test, build, and run e2e run: npx nx affected -t lint,test,build,e2e-ci --configuration=ci # Self-Healing CI: recommend fixes for failures. Learn more: https://nx.dev/ci/features/self-healing-ci - name: Self-Healing CI run: npx nx fix-ci if: always() agents: name: Agent ${{ matrix.agent }} runs-on: ubuntu-latest strategy: matrix: # Add more agents here as your repository expands agent: [1, 2, 3] steps: - name: Checkout uses: actions/checkout@v7 # Set node/npm/yarn versions using volta - uses: volta-cli/action@v4 with: package-json-path: '${{ github.workspace }}/package.json' - name: Use the package manager cache if available uses: actions/setup-node@v6 with: node-version: 24 cache: 'npm' - name: Install dependencies run: npm ci - name: Start Nx Agent ${{ matrix.agent }} run: npx nx start-agent env: NX_AGENT_NAME: ${{ matrix.agent }} # Upload agent resource metrics (Requires Nx 22.1 or higher.) — https://nx.dev/docs/features/ci-features/resource-usage - name: Upload agent metrics if: always() run: npx nx-cloud upload-agent-metrics env: NX_AGENT_NAME: ${{ matrix.agent }} # Self-Healing CI: recommend fixes for failures. Learn more: https://nx.dev/ci/features/self-healing-ci - name: Self-Healing CI run: npx nx fix-ci if: always() env: NX_AGENT_NAME: ${{ matrix.agent }} ``` There are comments throughout the workflow to help you understand what is happening in each section. {% /tabitem %} {% tabitem label="Circle CI" %} Run agents directly on Circle CI with the workflow below: ```yaml // .circleci/config.yml version: 2.1 orbs: nx: nrwl/nx@1.5.1 jobs: main: docker: - image: cimg/node:lts-browsers steps: - checkout - run: npm ci - nx/set-shas # Start the Nx Cloud CI run using the settings in .nx/ci-config.yaml - run: npx nx start-nx-agents # Send logs to Nx Cloud for any CLI command - run: npx nx record -- nx format:check # Lint, test, build and run e2e on agent jobs for everything affected by a change - run: npx nx affected --base=$NX_BASE --head=$NX_HEAD -t lint,test,build,e2e-ci --parallel=2 --configuration=ci # Self-Healing CI: recommend fixes for failures. Learn more: https://nx.dev/ci/features/self-healing-ci - run: npx nx fix-ci when: always agent: docker: - image: cimg/node:lts-browsers parameters: ordinal: type: integer steps: - checkout - run: npm ci # Wait for instructions from Nx Cloud - run: command: npx nx start-agent no_output_timeout: 60m environment: NX_AGENT_NAME: << parameters.ordinal >> # Upload agent resource metrics (Requires Nx 22.1 or higher.) — https://nx.dev/docs/features/ci-features/resource-usage - run: command: npx nx-cloud upload-agent-metrics environment: NX_AGENT_NAME: << parameters.ordinal >> when: always # Self-Healing CI: recommend fixes for failures. Learn more: https://nx.dev/ci/features/self-healing-ci - run: command: npx nx fix-ci environment: NX_AGENT_NAME: << parameters.ordinal >> when: always workflows: build: jobs: - agent: matrix: parameters: ordinal: [1, 2, 3] - main ``` This configuration is setting up two types of jobs - a main job and three agent jobs. The main job tells Nx Cloud to use DTE and then runs normal Nx commands as if this were a single pipeline set up. Once the commands are done, it notifies Nx Cloud to stop the agent jobs. The agent jobs set up the repo and then wait for Nx Cloud to assign them tasks. {% /tabitem %} {% tabitem label="Azure" %} Run agents directly on Azure Pipelines with the workflow below: ```yaml // azure-pipelines.yml trigger: - main pr: - main variables: CI: 'true' ${{ if eq(variables['Build.Reason'], 'PullRequest') }}: NX_BRANCH: $(System.PullRequest.PullRequestNumber) TARGET_BRANCH: $[replace(variables['System.PullRequest.TargetBranch'],'refs/heads/','origin/')] BASE_SHA: $(git merge-base $(TARGET_BRANCH) HEAD) ${{ if ne(variables['Build.Reason'], 'PullRequest') }}: NX_BRANCH: $(Build.SourceBranchName) BASE_SHA: $(git rev-parse HEAD~1) HEAD_SHA: $(git rev-parse HEAD) jobs: - job: agents strategy: parallel: 3 displayName: Nx Cloud Agent pool: vmImage: 'ubuntu-latest' steps: - checkout: self fetchDepth: '0' fetchFilter: tree:0 persistCredentials: true - script: npm ci - script: npx nx start-agent env: NX_AGENT_NAME: $(System.JobPositionInPhase) # Upload agent resource metrics (Requires Nx 22.1 or higher.) — https://nx.dev/docs/features/ci-features/resource-usage - script: npx nx-cloud upload-agent-metrics condition: always() env: NX_AGENT_NAME: $(System.JobPositionInPhase) # Self-Healing CI: recommend fixes for failures. Learn more: https://nx.dev/ci/features/self-healing-ci - script: npx nx fix-ci condition: always() env: NX_AGENT_NAME: $(System.JobPositionInPhase) - job: main displayName: Nx Cloud Main pool: vmImage: 'ubuntu-latest' steps: # Get last successfull commit from Azure Devops CLI - bash: | LAST_SHA=$(az pipelines build list --branch $(Build.SourceBranchName) --definition-ids $(System.DefinitionId) --result succeeded --top 1 --query "[0].triggerInfo.\"ci.sourceSha\"") if [ -z "$LAST_SHA" ] then echo "Last successful commit not found. Using fallback 'HEAD~1': $BASE_SHA" else echo "Last successful commit SHA: $LAST_SHA" echo "##vso[task.setvariable variable=BASE_SHA]$LAST_SHA" fi displayName: 'Get last successful commit SHA' condition: ne(variables['Build.Reason'], 'PullRequest') env: AZURE_DEVOPS_EXT_PAT: $(System.AccessToken) - script: git branch --track main origin/main - script: npm ci - script: npx nx start-nx-agents - script: npx nx record -- nx format:check --base=$(BASE_SHA) --head=$(HEAD_SHA) - script: npx nx affected --base=$(BASE_SHA) --head=$(HEAD_SHA) -t lint,test,build,e2e-ci --parallel=2 --configuration=ci # Self-Healing CI: recommend fixes for failures. Learn more: https://nx.dev/ci/features/self-healing-ci - script: npx nx fix-ci condition: always() ``` This configuration is setting up two types of jobs - a main job and three agent jobs. The main job tells Nx Cloud to use DTE and then runs normal Nx commands as if this were a single pipeline set up. Once the commands are done, it notifies Nx Cloud to stop the agent jobs. The agent jobs set up the repo and then wait for Nx Cloud to assign them tasks. {% /tabitem %} {% tabitem label="Bitbucket" %} Run agents directly on Bitbucket Pipelines with the workflow below: ```yaml // bitbucket-pipelines.yml image: node:22 clone: depth: full definitions: steps: - step: &agent name: Agent script: - export NX_BRANCH=$BITBUCKET_PR_ID - export NX_AGENT_NAME=$BITBUCKET_STEP_UUID - npm ci - npx nx start-agent after-script: - export NX_AGENT_NAME=$BITBUCKET_STEP_UUID # Upload agent resource metrics (Requires Nx 22.1 or higher.) — https://nx.dev/docs/features/ci-features/resource-usage - npx nx-cloud upload-agent-metrics # Self-Healing CI: recommend fixes for failures. Learn more: https://nx.dev/ci/features/self-healing-ci - npx nx fix-ci pipelines: pull-requests: '**': - parallel: - step: name: CI script: - export NX_BRANCH=$BITBUCKET_PR_ID - npm ci - npx nx start-nx-agents - npx nx record -- nx format:check - npx nx affected --target=lint,test,build,e2e-ci --parallel=2 after-script: # Self-Healing CI: recommend fixes for failures. Learn more: https://nx.dev/ci/features/self-healing-ci - npx nx fix-ci - step: *agent - step: *agent - step: *agent ``` This configuration is setting up two types of jobs - a main job and three agent jobs. The main job tells Nx Cloud to use DTE and then runs normal Nx commands as if this were a single pipeline set up. Once the commands are done, it notifies Nx Cloud to stop the agent jobs. The agent jobs set up the repo and then wait for Nx Cloud to assign them tasks. {% /tabitem %} {% tabitem label="GitLab" %} Run agents directly on GitLab with the workflow below: ```yaml // .gitlab-ci.yml image: node:22 # Creating template for DTE agents .dte-agent: interruptible: true cache: key: files: - yarn.lock paths: - '.yarn-cache/' script: - yarn install --cache-folder .yarn-cache --prefer-offline --frozen-lockfile - export NX_AGENT_NAME=$CI_JOB_ID - yarn nx start-agent after_script: - export NX_AGENT_NAME=$CI_JOB_ID # Upload agent resource metrics (Requires Nx 22.1 or higher.) — https://nx.dev/docs/features/ci-features/resource-usage - yarn nx-cloud upload-agent-metrics # Self-Healing CI: recommend fixes for failures. Learn more: https://nx.dev/ci/features/self-healing-ci - yarn nx fix-ci # Creating template for a job running DTE (orchestrator) .base-pipeline: interruptible: true only: - main - merge_requests cache: key: files: - yarn.lock paths: - '.yarn-cache/' before_script: - yarn install --cache-folder .yarn-cache --prefer-offline --frozen-lockfile - NX_HEAD=$CI_COMMIT_SHA - NX_BASE=${CI_MERGE_REQUEST_DIFF_BASE_SHA:-$CI_COMMIT_BEFORE_SHA} artifacts: expire_in: 5 days paths: - dist # Main job running DTE nx-dte: stage: affected extends: .base-pipeline script: - yarn nx start-nx-agents - yarn nx record -- nx format:check --base=$NX_BASE --head=$NX_HEAD - yarn nx affected --base=$NX_BASE --head=$NX_HEAD -t lint,test,build,e2e-ci --parallel=2 # Self-Healing CI: recommend fixes for failures. Learn more: https://nx.dev/ci/features/self-healing-ci after_script: - yarn nx fix-ci # Create as many agents as you want nx-dte-agent1: extends: .dte-agent stage: affected nx-dte-agent2: extends: .dte-agent stage: affected nx-dte-agent3: extends: .dte-agent stage: affected ``` This configuration is setting up two types of jobs - a main job and three agent jobs. The main job tells Nx Cloud to use DTE and then runs normal Nx commands as if this were a single pipeline set up. Once the commands are done, it notifies Nx Cloud to stop the agent jobs. The agent jobs set up the repo and then wait for Nx Cloud to assign them tasks. {% /tabitem %} {% tabitem label="Jenkins" %} Run agents directly on Jenkins with the workflow below: ```groovy // Jenkinsfile pipeline { agent none environment { NX_BRANCH = env.BRANCH_NAME.replace('PR-', '') } stages { stage('Pipeline') { parallel { stage('Main') { when { branch 'main' } agent any steps { sh "npm ci" sh "npx nx start-nx-agents" sh "npx nx record -- nx format:check" sh "npx nx affected --base=HEAD~1 -t lint,test,build,e2e-ci --configuration=ci --parallel=2" } post { // Self-Healing CI: recommend fixes for failures. Learn more: https://nx.dev/ci/features/self-healing-ci always { sh "npx nx fix-ci" } } } stage('PR') { when { not { branch 'main' } } agent any steps { sh "npm ci" sh "npx nx start-nx-agents" sh "npx nx record -- nx format:check" sh "npx nx affected --base origin/${env.CHANGE_TARGET} -t lint,test,build,e2e-ci --parallel=2 --configuration=ci" } post { // Self-Healing CI: recommend fixes for failures. Learn more: https://nx.dev/ci/features/self-healing-ci always { sh "npx nx fix-ci" } } } # Add as many agent you want stage('Agent1') { agent any environment { NX_AGENT_NAME = '1' } steps { sh "npm ci" sh "npx nx start-agent" } post { always { // Upload agent resource metrics (Requires Nx 22.1 or higher.) — https://nx.dev/docs/features/ci-features/resource-usage sh "npx nx-cloud upload-agent-metrics" // Self-Healing CI: recommend fixes for failures. Learn more: https://nx.dev/ci/features/self-healing-ci sh "npx nx fix-ci" } } } stage('Agent2') { agent any environment { NX_AGENT_NAME = '2' } steps { sh "npm ci" sh "npx nx start-agent" } post { always { // Upload agent resource metrics (Requires Nx 22.1 or higher.) — https://nx.dev/docs/features/ci-features/resource-usage sh "npx nx-cloud upload-agent-metrics" // Self-Healing CI: recommend fixes for failures. Learn more: https://nx.dev/ci/features/self-healing-ci sh "npx nx fix-ci" } } } stage('Agent3') { agent any environment { NX_AGENT_NAME = '3' } steps { sh "npm ci" sh "npx nx start-agent" } post { always { // Upload agent resource metrics (Requires Nx 22.1 or higher.) — https://nx.dev/docs/features/ci-features/resource-usage sh "npx nx-cloud upload-agent-metrics" // Self-Healing CI: recommend fixes for failures. Learn more: https://nx.dev/ci/features/self-healing-ci sh "npx nx fix-ci" } } } } } } } ``` This configuration is setting up two types of jobs - a main job and three agent jobs. The main job tells Nx Cloud to use DTE and then runs normal Nx commands as if this were a single pipeline set up. Once the commands are done, it notifies Nx Cloud to stop the agent jobs. The agent jobs set up the repo and then wait for Nx Cloud to assign them tasks. {% /tabitem %} {% /tabs %} {% aside type="caution" title="Two types of parallelization" %} The agent configuration and the `--parallel` flag both parallelize tasks, but in different ways. In the examples above, there will be 3 agents running tasks and each agent will try to run 2 tasks at once. If a particular CI run only has 2 tasks, only one agent will be used. {% /aside %} ## Rerunning jobs with DTE Rerunning only failed jobs results in agent jobs not running, which causes the CI pipeline to hang and eventually timeout. This is a common pitfall when using a CI providers "rerun failed jobs", or equivalent, feature since agent jobs will always complete successfully. To enforce rerunning all jobs, you can set up your CI pipeline to exit early with a helpful error. For example: > You reran only failed jobs, but CI requires rerunning all jobs. > Rerun all jobs in the pipeline to prevent this error. At a high level: 1. Create a job that always succeeds and uploads an artifact on the pipeline with the run attempt number of the pipeline. 2. The main and agent jobs can read the artifact file when starting and assert they are on the same re-try attempt. 3. If the reattempt number does not match, then error with a message stating to rerun all jobs. Otherwise, the pipelines are on the same rerun and can proceed as normally. --- ## Configuring Browser Support The official Nx plugins rely on [browserslist](https://github.com/browserslist/browserslist) for configuring application browser support. This affects builds, both production and development, and will decide on which transformations will be run on the code when built. In general, the more modern your applications browser support is, the smaller the filesize as the code can rely on modern API's being present and not have to ship polyfills or shimmed code. By default, applications generated from official Nx generators ship an aggressively modern browser support config, in the form of a `.browserslistrc` file in the root of the application with the following contents. ```text last 1 Chrome version last 1 Firefox version last 2 Edge major versions last 2 Safari major version last 2 iOS major versions Firefox ESR not IE 9-11 ``` This configuration is used for many tools including babel, autoprefixer, postcss, and more to decide which transforms are necessary on the source code when producing built code to run in the browser. For additional information regarding the format and rule options, please see: https://github.com/browserslist/browserslist#queries ## Debugging browser support Sometimes broad configurations like `> 0.5%, not IE 11` can lead to surprising results, due to supporting browsers like Opera Mini or Android UC browser. To see what browsers your configuration is supporting, run `npx browserslist` in the application's directory to get an output of browsers and versions to support. ```text {% title="npx browserslist" frame="terminal" %} and_chr 61 chrome 83 edge 83 edge 81 firefox 78 firefox 68 ie 11 ios_saf 13.4-13.5 ios_saf 13.3 ios_saf 13.2 ios_saf 13.0-13.1 ios_saf 12.2-12.4 ios_saf 12.0-12.1 safari 13.1 safari 13 safari 12.1 safari 12 ``` Alternatively, if your support config is short you can just add it as a string param on the CLI: ```shell npx browserslist '> 0.5%, not IE 11' ``` --- ## Publishable and Buildable Nx Libraries The `--buildable` and `--publishable` options are available on the Nx library generators for the following plugins: - Angular - React - NestJs - Node This document will look to explain the motivations for why you would use either the `--buildable` or `--publishable` option, as well as the mechanics of how they adjust the result when you add them to your generator. ## Publishable libraries You might use the `--publishable` option when generating a new Nx library if your intention is to distribute it outside the monorepo. One typical scenario for this may be that you use Nx to develop your organizations UI design system component library (maybe using its Storybook integration), which should be available also to your organizations' apps that are not hosted within the same monorepo. A normal Nx library - let's call it "workspace library" - is not made for building or publishing. Rather it only includes common lint and test targets in its `project.json` file. These libraries are directly referenced from one of the monorepo's applications and built together with them. Keep in mind that the `--publishable` flag does not enable automatic publishing. Rather it adds to your Nx workspace library a builder target that **compiles** and **bundles** your app. The resulting artifact will be ready to be published to some registry (e.g. [npm](https://npmjs.com/)). By having that builder, you can invoke the build via a command like: `nx build mylib` (where "mylib" is the name of the lib) which will then produce an optimized bundle in the `dist/mylib` folder. One particularity when generating a library with `--publishable` is that it requires you to also provide an `--importPath`. Your import path is the actual scope of your distributable package (e.g.: `@myorg/mylib`) - which needs to be a [valid npm package name](https://docs.npmjs.com/files/package.json#name). To publish the library (for example to npm) you can run the CLI command: `npm publish` from the artifact located in the `dist` directory. Setting up some automated script in Nx `tools` folder may also come in handy. For more details on the mechanics, remember that Nx is an open source project, so you can see the actual impact of the generator by looking at the source code (the best starting point is probably `packages//src/generators/library/library.ts`). ## Buildable libraries Buildable libraries are similar to "publishable libraries" described above. Their scope however is not to distribute or publish them to some external registry. Thus they might not be optimized for bundling and distribution. Buildable libraries are mostly used for producing some pre-compiled output that can be directly referenced from an Nx workspace application without the need to again compile it. A typical scenario is to leverage Nx incremental building capabilities. {% aside type="caution" title="More details" %} In order for a buildable library to be pre-compiled, it can only depend on other buildable libraries. This allows you to take full advantage of incremental builds. {% /aside %} For more details on the mechanics, remember that Nx is an open source project, so you can see the actual impact of the generator by looking at the source code (the best starting point is probably `packages//src/generators/library/library.ts`). --- ## Bun Workspaces: Setup, Commands, and Best Practices Bun workspaces let you manage multiple packages in a single repository (or a monorepo) using the same `workspaces` field npm and Yarn read. `bun install` resolves every package in a single pass, dedupes shared dependencies to the root `node_modules`, and links local packages declared with the `workspace:` protocol. For the full configuration reference, see the [Bun workspaces documentation](https://bun.com/docs/install/workspaces). ## Set up a Bun workspace ### 1. List packages in the workspaces field A Bun workspace is a repository whose root `package.json` has a `workspaces` field listing the directories that hold `package.json` files. Bun supports full glob syntax here, including negative patterns like `!**/excluded/**`: ```jsonc // package.json { "name": "my-workspace", "private": true, "workspaces": ["apps/*", "packages/*"], } ``` ### 2. Add the packages Every directory matched by those globs that contains a `package.json` becomes a workspace package. A common layout separates applications from shared packages: {% filetree %} - my-workspace/ - apps/ - web/ - package.json - packages/ - shared-ui/ - package.json - package.json {% /filetree %} ### 3. Link local packages with workspace:\* To depend on another package in the workspace, reference it with the `workspace:` protocol so Bun resolves it to the local package. Bun supports `workspace:*`, `workspace:^`, and `workspace:~`, and replaces them with real semver versions on publish: ```jsonc // apps/web/package.json { "name": "web", "dependencies": { "react": "^19.0.0", "@my-workspace/shared-ui": "workspace:*", }, } ``` Run `bun install` once at the root to install every package's dependencies and link the internal ones. ## Install every package with one bun install A single `bun install` at the root installs and dedupes dependencies for all workspaces, so shared packages exist once in the root `node_modules`. Install speed is the headline Bun feature: per the Bun team's benchmarks, `bun install` runs up to 28x faster than `npm install`, which makes full reinstalls in CI and fresh clones noticeably cheaper in a workspace with many packages. ## Target packages with --filter The `--filter` flag narrows both installs and script runs to matching packages. For `bun run`, place `--filter` before the script name: ```shell bun run --filter '*' build # run build in every package bun run --filter web build # run build in a single package bun install --filter 'pkg-*' # install deps only for matching packages ``` Filters accept globs and compose: pass `--filter` multiple times, and prefix a pattern with `!` to exclude it, as in `bun install --filter 'pkg-*' --filter '!pkg-c'`. ## Share dependency versions with Bun catalogs Catalogs define a dependency version once and reference it from every package, so packages can't drift onto different versions of the same dependency. Bun reads catalogs from the root `package.json`: the `catalog` field holds the default catalog, and `catalogs` holds named catalogs. Nx resolves these references (`catalog:` in generators, migrations, release versioning, and the `@nx/dependency-checks` lint rule) starting in Nx 23.2. ```jsonc // package.json { "catalog": { "react": "^19.0.0", "react-dom": "^19.0.0", }, "catalogs": { "testing": { "vitest": "^3.0.0", }, }, } ``` ```jsonc // apps/web/package.json { "name": "web", "dependencies": { "react": "catalog:", "vitest": "catalog:testing", }, } ``` `catalog:` points at the `catalog` field and `catalog:testing` at a named catalog. Two Bun behaviors differ from pnpm: - **`default` is not a special name.** `catalog:` resolves only against the `catalog` field, and `catalog:default` addresses a named catalog literally called `default`. Bun does not treat `catalogs.default` as the default catalog. - **Definition locations are all-or-nothing.** You can also nest `catalog`/`catalogs` under the object form of the `workspaces` field. If either field exists there, Bun ignores the top-level fields entirely rather than merging the two locations. The same `catalog` vs `catalogs.default` distinction applies to [Yarn catalogs](/docs/kb/yarn-workspaces#share-dependency-versions-with-yarn-catalogs) (Yarn 4.10+). ## How mature are Bun workspaces? Bun workspaces are the newest of the four implementations. Setup, linking, the `workspace:` protocol, and `--filter` cover the everyday workflows, but there's no filtering by dependency relationship or by what changed in git, which the pnpm `--filter` flag offers. Bun workspaces also have less production mileage overall, and some ecosystem tools expect an npm, Yarn, or pnpm lockfile rather than the Bun lockfile. If your workspace leans on publishing workflows or unusual install hooks, test those paths before migrating and check the [Bun issue tracker](https://github.com/oven-sh/bun/issues) for open workspace issues. ## Do Bun workspaces replace a monorepo tool? No. Bun workspaces make installs fast and link local packages, but they don't cache task results or detect which projects a commit affects, so CI reruns every task on every push no matter how fast the install was. Add Nx on top of Bun for task caching, task pipeline orchestration, and affected runs, at any workspace size: ```shell npx nx@latest init ``` Your existing `package.json` scripts keep working and Bun keeps managing installs. ### Run tasks before and after Nx Before Nx, Bun runs a script across packages, but it runs every task every time and has no notion of which packages a change affects: ```shell bun run --filter '*' build # every package bun run --filter web build # a single package ``` After `nx init`, run the same scripts through Nx: ```shell nx run-many -t build # every project nx build web # a single project nx affected -t build # only projects touched by your changes ``` The first run executes your scripts. A second run with no changes is [restored from the cache](/docs/features/cache-task-results), and [`nx affected`](/docs/features/ci-features/affected) skips the projects your change does not touch. ### Configure caching and dev servers `nx init` writes an `nx.json` with `targetDefaults` that control how targets behave: ```jsonc // nx.json { "targetDefaults": { "build": { "cache": true, "dependsOn": ["^build"], }, "dev": { "continuous": true, }, }, } ``` `cache: true` makes a target cacheable: Nx hashes each project's inputs (source files, dependencies, and config) and restores its outputs from the cache when nothing has changed. Nothing is cached unless you opt in, so you stay in control. `dependsOn: ["^build"]` builds a project's dependencies first. `continuous: true` marks tasks that don't exit, like dev servers and watchers, so tasks that depend on them don't wait for them to complete. ### Speed up CI with Nx Cloud Nx Cloud adds more ways to speed up CI for a Bun workspace: [remote caching](/docs/features/ci-features/remote-cache) shares the cache across CI runs and teammates, [Nx Agents](/docs/features/ci-features/distribute-task-execution) distribute tasks across machines, and [self-healing CI](/docs/features/ci-features/self-healing-ci) proposes fixes when tasks fail. For the full walkthrough, see [Adding Nx to an existing monorepo](/docs/kb/adding-to-monorepo) and [CI setup](/docs/getting-started/setup-ci). Working with a different package manager? See [pnpm workspaces](/docs/kb/pnpm-workspaces), [npm workspaces](/docs/kb/npm-workspaces), or [Yarn workspaces](/docs/kb/yarn-workspaces). New to monorepos? Start with [what a monorepo is and why teams use one](/docs/kb/what-is-a-monorepo). --- ## Bundling Projects for Deployment Bundling compiles your code and all its dependencies into a single file (e.g. `main.js`). The output is self-contained, so you don't need `node_modules` or an install step at deploy time. If your app has native dependencies or you want Docker layer caching for `node_modules`, see [pruning projects for deployment](/docs/kb/deploying-node-projects) instead. ## When to bundle instead of prune | Approach | Best for | Trade-off | | ------------------------------------------- | --------------------------------------- | --------------------------------------------- | | Bundling | Serverless functions, simple APIs | Single file output, no `node_modules` needed | | [Pruning](/docs/kb/deploying-node-projects) | Docker deployments, native dependencies | Keeps `node_modules` but only production deps | Use bundling when: - Your app has no native dependencies that require OS-level installation - You want a single deployable artifact with no install step - You're targeting serverless platforms or lightweight containers {% aside type="note" title="Lazy loaded chunks" %} If you use lazy loaded chunks, bundling produces more than a single file. You'll have one file per chunk entrypoint. Make sure all files are handled correctly for your deployment needs. {% /aside %} ## Bundling Node.js applications {% tabs syncKey="bundler" %} {% tabitem label="Webpack" %} Webpack provides full control over the bundling process. Configure your `webpack.config.js` to bundle all dependencies. - `generatePackageJson: false` - Skips generating a `package.json` since all dependencies are bundled - `externalDependencies: 'none'` - Bundles all dependencies instead of treating them as external ```js {% meta="{16-17}" %} // webpack.config.js const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin'); const { join } = require('path'); module.exports = { output: { path: join(__dirname, 'dist'), }, plugins: [ new NxAppWebpackPlugin({ target: 'node', compiler: 'tsc', main: './src/main.ts', tsConfig: './tsconfig.app.json', outputHashing: 'none', generatePackageJson: false, externalDependencies: 'none', }), ], }; ``` {% /tabitem %} {% tabitem label="esbuild" %} esbuild offers faster builds than Webpack. Configure bundling in your `project.json`: The `bundle: true` option tells esbuild to include all dependencies in the output. ```json {% meta="{11}" %} // project.json { "name": "my-app", "targets": { "build": { "executor": "@nx/esbuild:esbuild", "options": { "platform": "node", "outputPath": "dist/my-app", "format": ["cjs"], "bundle": true, "main": "apps/my-app/src/main.ts", "tsConfig": "apps/my-app/tsconfig.app.json", "generatePackageJson": false, "esbuildOptions": { "outfile": "dist/my-app/main.js" } } } } } ``` {% /tabitem %} {% tabitem label="Vite" %} Vite can also bundle Node.js applications with vite by using the `build.rollupOptions.external`. Set `external: []` to bundle all dependencies into the output. ```ts {% meta="{15}"%} // vite.config.ts import { defineConfig } from 'vite'; export default defineConfig({ build: { target: 'node22', outDir: 'dist', lib: { entry: 'src/main.ts', formats: ['cjs'], fileName: 'main', }, rollupOptions: { // Bundle all dependencies external: [], }, }, }); ``` {% /tabitem %} {% /tabs %} To build and deploy: ```shell nx build my-app # Deploy dist/my-app/main.js - no node_modules needed ``` ## When not to bundle If you're publishing a library package to npm, avoid bundling dependencies. Declare them in `package.json` so package managers can handle versioning and deduplication. For Docker-based deployments, non-bundled builds can improve build times through layer caching. When dependencies don't change, Docker reuses the cached `node_modules` layer and only rebuilds your application code. Instead of running `build`, use the `prune` target to prepare your application for deployment with its dependencies: ```shell nx prune my-app ``` For the full setup, see [pruning projects for deployment](/docs/kb/deploying-node-projects). This creates a deployment-ready structure: ```text dist/my-app/ ├── main.js # Your application code ├── package.json # Dependencies manifest └── node_modules/ # Production dependencies ``` In your Dockerfile, copy these files and run with Node.js: ```dockerfile COPY dist/my-app /app WORKDIR /app CMD ["node", "main.js"] ``` ## Bundling libraries Publish workspace dependencies as separate packages rather than bundling them into a single library. This gives better versioning control and lets consumers manage their own dependency trees. Bundling workspace libraries makes sense when: - The library contains only types that should be inlined - You want to distribute an internal library as part of your package {% tabs syncKey="bundler" %} {% tabitem label="esbuild" %} Configure the `external` option to control which dependencies get bundled: ```json // project.json { "name": "my-lib", "targets": { "build": { "executor": "@nx/esbuild:esbuild", "options": { "platform": "node", "outputPath": "dist/libs/my-lib", "format": ["cjs", "esm"], "bundle": true, "main": "libs/my-lib/src/index.ts", "tsConfig": "libs/my-lib/tsconfig.lib.json", "external": ["^[^./].*$", "!@my-org/utils"] } } } } ``` The `external` patterns work as follows: - `"^[^./].*$"` - Externalizes all npm packages (paths not starting with `.` or `/`) - `"!@my-org/utils"` - Exception: bundles `@my-org/utils` despite matching the first pattern See esbuild [documentation if using an esbuild configuration file directly](https://esbuild.github.io/api/#external) {% /tabitem %} {% tabitem label="Vite" %} For Vite-based builds, configure the `external` function in `rollupOptions`: ```ts // vite.config.ts import { defineConfig } from 'vite'; export default defineConfig({ build: { lib: { entry: 'src/index.ts', formats: ['es', 'cjs'], }, rollupOptions: { external: (id) => { // Bundle workspace libraries if (id.startsWith('@my-org/')) { return false; } // Externalize npm packages if (!id.startsWith('.') && !id.startsWith('/')) { return true; } return false; }, }, }, }); ``` The `external` function receives each import and returns: - `false` to bundle the dependency - `true` to keep it as an external import See `rollupOptions` from [vite documentation for more information](https://vite.dev/config/build-options#build-rollupoptions) {% /tabitem %} {% /tabs %} ## Managing workspace dependencies When building libraries that consume other workspace libraries, define the dependency relationship in `package.json`: ```json // libs/my-lib/package.json { "name": "@my-org/my-lib", "dependencies": { "@my-org/utils": "workspace:*" } } ``` The `workspace:*` syntax: - During development: resolves to the local workspace package - During publish: gets replaced with the actual version number This ensures your library correctly declares its dependencies regardless of whether they're bundled. ## Quick reference | Scenario | Tool | Key Settings | | --------------------------------------- | ------- | ------------------------------------------------------------ | | Bundled Node app | Webpack | `generatePackageJson: false`, `externalDependencies: 'none'` | | Bundled Node app | esbuild | `bundle: true`, `generatePackageJson: false` | | Bundled Node app | Vite | `external: []` in `rollupOptions` | | Non-bundled Node app | Any | Run `prune` target, deploy with `node_modules` | | Publishable lib (bundle workspace deps) | esbuild | `bundle: true`, `external: ["^[^./].*$", "!@org/lib"]` | | Publishable lib (bundle workspace deps) | Vite | Custom `external` function in `rollupOptions` | --- ## Cache Security {% aside type="caution" title="Use Caution With Read-Write Tokens" %} Read-write tokens allow full write access to your remote cache. They should only be used in trusted environments. For instance, open source projects should only use read-write tokens as secrets configured for protected branches (e.g, main). Read-only tokens should be used in all other cases. {% /aside %} A cache allows you to reuse work that has already been done, but it also introduces a potential security risk - cache poisoning. A poisoned cache is one where the cached files have been altered in some way by a malicious actor. When a developer or the CI pipeline use that poisoned cache, the task output will be what the malicious actor wants instead of the correct task output. Nx takes security seriously and has put in place many precautions (we're [SOC 2 compliant](https://security.nx.app)). Listed below are some precautions that you need to take in your own codebase. ## What data is sent to the cache? Nx does not send your actual code to the remote cache. There are 3 kinds of data that are sent to the Nx Cloud remote cache for each task: 1. A hash of the inputs to the task. There is no way to reconstitute the actual source code files that were used to create a particular hash value. 2. Any files that were created as outputs from a task. 3. The terminal output created when running the task. If a malicious actor were able to modify the cache and those output files were then executed, that malicious actor could run arbitrary code on developer machines or in CI. ## Recommended precautions In order to keep your cache secure, there are a few steps we recommend you take: ### Use personal access tokens to provide fine-Grained access control for local development When you use a [personal access token](/docs/kb/personal-access-tokens) to connect to Nx Cloud, you can control the level of access that your developers have to the cache after they authenticate by logging in. By default, all personal access tokens have read-write access to the cache. If you need to give a developer write access to the cache, you can do so in the workspace settings of the Nx Cloud UI. You can strengthen your workspace security further by revoking all access to the cache for unauthenticated users. This is done by changing the ID Access Level in your workspace settings. By default this is set to `read-write`, but you can change it to `read-only` to limit access or `none` to prevent all access. ### Avoid using CI access tokens in `nx.json` Avoid [specifying a token](/docs/kb/access-tokens) with the `nxCloudAccessToken` property in `nx.json` as they're visible to anyone with codebase access. A `read-write` token grants complete cache write access, enabling potential cache poisoning by unauthorized users. Instead, [restrict CI access tokens](/docs/kb/access-tokens) to protected CI environments and use [personal access tokens](/docs/kb/personal-access-tokens) for local development. ### Use scoped tokens in CI We recommend using a [read-write token](/docs/kb/access-tokens#read-write-access) only for protected branches (branches that don't allow direct push). A `read-write` access token allows reading from and writing to the shared global cache of your workspace. In all other branches, we recommend using a [read-only token](/docs/kb/access-tokens#read-only-access). A `read-only` token only allows reading from the shared global cache, while writing is limited to an execution specific isolated cache. This allows your CI pipelines to share computational work between the [distributed agents](/docs/features/ci-features/distribute-task-execution). For workspaces with an enabled [source control integration with Nx Cloud](/docs/kb/source-control-integration), we can securely scope the isolated cache to the pull request branch, without opening up the possibility of cache poisoning in your trusted environments. Learn more about [access token architecture](/docs/kb/access-tokens#setting-ci-access-tokens). ### No need to revoke tokens after employees leave When an employee leaves a company, it is standard practice to change all the passwords that they had access to. That is not necessary for Nx Cloud tokens. In order to poison the cache, the former employee would need to have both the read-write token and the current code on the latest commit on the `main` branch. The odds of the employee being able to guess the hash value that will be created for the current commit on the `main` branch are infinitesimally small even after a single commit. ### Skip the cache when creating a deployment artifact In order to guarantee that cache poisoning will never affect your end users, [skip the cache](/docs/kb/skipping-cache) when creating build artifacts that will actually be deployed. Skipping the cache for this one CI run is a very small performance cost, but it gives you 100% confidence that cache poisoning will not be an issue for the end users. ### Do not manually share your local cache Nx implicitly trusts the local cache which is stored by default in a per-user `~/.nx//cache` folder. You can change the location of that folder in the `nx.json` file, so it could be tempting to place it on a network drive and easily share your cache with everyone on the company network. However, by doing this you've voided the guarantee of immutability from your cache. If someone has direct access to the cached files, they could directly poison the cache. Nx will automatically detect if a cache entry has been created in your local cache using a different machine and warn you with an [Unknown Local Cache Error](/docs/kb/unknown-local-cache). Instead, use Nx Cloud [remote caching](/docs/features/ci-features/remote-cache). ### Configure end to end encryption Nx Cloud guarantees your cache entries will remain immutable - once they've been registered they can't be changed. This is guaranteed because the only way to access the cache is through the Nx Cloud API and we have policies enabled in our cloud storage that specifically disables overwrites and deletions of cached artifacts. But what if a hacker were somehow able make their way into the server holding the cache artifacts? Since you set up [end to end encryption](/docs/guides/nx-cloud/encryption), the files they see on disk will be fully encrypted with a key that only exists in your workspace. ### Use an on-Premise version of Nx Cloud if needed If you need to have all cache artifacts on servers that you control, there is an on-premise version of Nx Cloud that you can use as part of the [Enterprise plan](https://nx.dev/enterprise). ## Security decisions In any security discussion, there is a trade off between convenience and security. It could be that some of these threats do not apply to your organization. If that is the case you could relax some of the security precautions and gain the performance benefits of more task results being stored in the remote cache. Every organization is different and Nx can be adapted to best meet your needs without opening up vulnerabilities. If you would Nx team members to help your organization fine tune your set up, [talk to us about Nx Enterprise](https://nx.dev/enterprise). --- ## Large Next.js Apps Made Faster with Nx Repo contains: - 5 shared buildable packages/libraries with 250 components each - 5 Next.js applications built out of 20 app-specific libraries. Each app-specific lib has 250 components each. Each library uses the shared components. The repo shows how Nx works in a large workspace. It also benchmarks Nx and explains the optimisations Nx uses to be fast. {% github_repository url="https://github.com/vsavkin/large-monorepo" /%} --- ## Change Cache Location Nx stores the cache in a per-user directory outside your workspace, `~/.nx//cache`, where the id identifies the workspace rather than the folder it happens to sit in. Every checkout of one workspace shares that directory -- the main checkout, every worktree added to it, and a second clone -- so a task built in one is a cache hit in the others, and renaming or moving a checkout keeps its cache. Cache results are stored for a week before they get deleted. You can customize the location in the `nx.json` file: ```json // nx.json { "cacheDirectory": "/tmp/mycache" } ``` ## Why it is outside the workspace Each workspace gets one directory, `~/.nx/`, holding both `cache` and `databases`. Checkouts share it. A main checkout, every worktree added to it, and a separate clone all resolve to the same directory, so none of them rebuilds what another already has. The id comes from the workspace's identity -- its Nx Cloud id where there is one, otherwise a key derived from the git remote -- so it does not change when you rename or move a checkout. A workspace with no derivable identity, such as a directory that is not a git repository, keeps its own copy. An AI agent sandbox can reach it. A sandbox grants filesystem paths, and `~/.nx` is spelled the same on every machine, so the grant can live in a settings file you commit. An absolute checkout path cannot. `nx configure-ai-agents` writes that grant into `.claude/settings.json` for Claude Code. {% aside type="note" title="If you cache `.nx/cache` in CI" %} Nx no longer writes there by default. Point your CI cache step at `~/.nx` instead, or set `NX_CACHE_DIRECTORY` to keep the old location. {% /aside %} ## Opting out Setting `cacheDirectory` opts out of the shared location: Nx uses what you configured, in the checkout that configures it. `NX_CACHE_DIRECTORY`, `NX_WORKSPACE_DATA_DIRECTORY` and `NX_PROJECT_GRAPH_CACHE_DIRECTORY` opt out the same way. ## When Nx falls back When Nx cannot use `~/.nx`, the checkout keeps its own `.nx` directories instead. Two things cause it: Nx cannot make the directory owner-only, usually after a `sudo nx` run that kept your `HOME` and left it owned by root; or the process cannot write there at all, which is what an agent sandbox does. Nx tells the second case apart by writing a marker file rather than by recognizing the caller, so a sandbox Nx has never heard of is covered as long as it denies the write. A sandbox that permits writes to `~/.nx` is not falling back, and does not need to. `nx reset` clears both the shared directory for the current workspace and the checkout's own. Nothing reclaims the directory belonging to a workspace you no longer have checked out anywhere, so remove those by hand. --- ## Speed Up CI with a Build Cache CI caching means storing the results of tasks (builds, tests, lints) keyed by their inputs, so a pipeline that runs the same task on the same code replays the stored result instead of executing it again. In a typical workflow, the same code is built and tested many times: locally before pushing, on every CI run of a PR, and again after merging. A build cache eliminates every repeat execution, which is why teams adopting one commonly see 30-70% faster CI. ## How a build cache works A build cache computes a hash of everything a task depends on: source files, dependencies, configuration, environment. If a result for that hash exists, the cached output (files and terminal output) is replayed. If not, the task runs and the result is stored. Correctness depends entirely on the inputs being complete, so cache tooling that understands your project structure matters more than raw storage speed. Nx [caches task results](/docs/features/cache-task-results) with inputs inferred from your tooling configuration, and [how caching works](/docs/concepts/how-caching-works) covers the hashing model in detail. ## Local cache vs remote cache A local cache only helps one machine. CI machines are usually ephemeral, so a local cache alone does almost nothing for pipeline times: every run starts cold. A remote cache shares results across every machine, so CI replays work your teammates or previous pipeline runs already did, and your local machine replays work CI did. [Nx Cloud remote caching](/docs/features/ci-features/remote-cache) requires no CI-provider cache configuration and includes access control and integrity checks. If you need to run the cache on your own infrastructure, use a [self-hosted remote cache](/docs/kb/self-hosted-caching). ## What caching can't fix A cache only removes repeat work, and two other sources of CI time need different tools: - **First-time work** - a change to a shared library invalidates the cache for everything that depends on it. [`nx affected`](/docs/features/ci-features/affected) keeps the task list scoped to what a change actually touches, and [reduce wasted time in CI](/docs/kb/reduce-waste) analyzes how the two combine. - **The critical path** - when a large PR invalidates many tasks, one machine can only parallelize so far. [Distributed task execution](/docs/features/ci-features/distribute-task-execution) spreads the remaining work across agents, with cached results shared between them through the same remote cache. Together these form a pipeline where the worst case is bounded by the largest single task, not the size of the repository. The [building blocks of fast CI](/docs/concepts/ci-concepts/building-blocks-fast-ci) covers the full picture. ## Frequently asked questions ### Is it safe to cache test results in CI? Yes, when the cache key covers every input the test depends on. Nx hashes source files, dependency graphs, configuration, and declared environment variables, and [task sandboxing](/docs/features/ci-features/sandboxing) can enforce that tasks don't read undeclared inputs. Flaky tests are the real hazard: a flaky failure cached as a result is misleading, which is why Nx Cloud [detects flaky tasks](/docs/features/ci-features/flaky-tasks) and retries them instead of caching the noise. ### How much faster does CI get with a remote cache? It depends on how often the same task re-runs on unchanged code. Across Nx Cloud workspaces we observed 30-70% faster CI, with the biggest wins in repositories with many projects where most PRs touch a small slice. ### Do I need a monorepo to benefit from CI caching? No, but the benefit scales with project count. In a single-project repository, any change invalidates most of the cache. In a [monorepo](/docs/kb/what-is-a-monorepo), each project caches independently, so most projects replay from cache on any given PR. --- ## Understanding "CI Execution Failed" ## Task runner-related ### No additional tasks detected Nx Cloud is not aware of any more tasks to distribute. This can occur if Nx Cloud thinks it is done receiving tasks to distribute and all existing tasks have been completed. If you are receiving this error before your full pipeline has completed, add the last target run in your pipeline to [`lifecycle.stop-after`](/docs/reference/nx-cloud/ci-config#lifecyclestop-after) in `.nx/ci-config.yaml`. ### The Nx Cloud heartbeat process failed to report its status in time While running in CI environments, Nx Cloud spawns a background process called the "heartbeat" to help maintain status synchronization between itself and external platforms. When the heartbeat process does not report to Nx Cloud for 30 seconds or longer, Nx Cloud assumes something has gone wrong and terminates the current CI Pipeline Execution. This behavior can be disabled by setting [`lifecycle.heartbeat`](/docs/reference/nx-cloud/ci-config#lifecycleheartbeat) to `false` in `.nx/ci-config.yaml`. Turning the heartbeat off makes you responsible for completing the run, so the pipeline must also run `npx nx complete-ci-run` as its last step, with `if: always()` or the equivalent for your provider. Without it the CI Pipeline Execution may never complete. See [heartbeat and manual shutdown handling](/docs/kb/heartbeat-and-manual-shutdown-handling) for the full sequence. ### A command was issued to stop all Nx Cloud agents Nx Cloud provides two commands to forcibly stop agents, [stop-all-agents and complete-ci-run](/docs/reference/nx-cloud-cli#nx-cloud-stop-all-agents). The two command options are functionally equivalent, but the `complete-ci-run` command is preferred. Once these commands are invoked, the current CI Pipeline Execution is closed and can no longer receive new work. If this error occurs at the beginning of a CI Pipeline Execution it could be caused by a non-unique [NX_CI_EXECUTION_ID](/docs/reference/environment-variables#nxbranch-and-nxciexecutionid) that is causing Nx Cloud to associate this CIPE with a CIPE that has already concluded. ### Nx Cloud agents were stopped due to an error Nx Cloud detected a failed task in the current CI Pipeline Execution and has halted further execution. This behavior can be disabled by setting [`lifecycle.fail-after`](/docs/reference/nx-cloud/ci-config#lifecyclefail-after) to `0` in `.nx/ci-config.yaml`. ### A CI configuration file was detected, but start-ci-run does not load it `start-ci-run` takes its configuration from CLI flags and never reads `.nx/ci-config.yaml`, so the two cannot be combined. Once that file exists, replace the `start-ci-run` invocation with `nx-cloud start-nx-agents`. See [migrate from start-ci-run to start-nx-agents](/docs/kb/migrate-to-ci-config) for the flag-to-key mapping. ### No CI configuration file found `nx-cloud start-nx-agents` reads all of its configuration from `.nx/ci-config.yaml` (or `.yml`) in the workspace's `.nx` folder, and exits when that file is missing. Add the file and commit it. See the [CI configuration file reference](/docs/reference/nx-cloud/ci-config). ## Nx Agents-related ### Failed to start Nx Agents workflow Nx Cloud was unable to start the agents workflow with the configuration in your [`.nx/ci-config.yaml`](/docs/reference/nx-cloud/ci-config) file. View the CI Pipeline Execution in the Nx Cloud UI for additional details. ### Unable to get workflow status from Nx Agents Nx Cloud was unable to communicate with the Nx Agents assigned to a workflow for the current CI Pipeline Execution. View the CI Pipeline Execution in the Nx Cloud UI for additional details. ## Status reconciliation-related ### One or more workflows were cancelled The current CI Pipeline Execution had a workflow cancelled due to either: - a manual request in the Nx Cloud UI, or - a push to the same branch that already had a running workflow. ### One or more workflows encountered a critical error The current CI Pipeline Execution encountered a critical error in a child execution environment. View the CI Pipeline Execution in the Nx Cloud UI for additional details. ### One or more workflows failed The current CI Pipeline Execution had at least one workflow with failed steps. ### One or more workflows encountered an error The current CI Pipeline Execution had at least one workflow that executed tasks which failed. See also: [Nx Cloud agents were stopped due to an error](#nx-cloud-agents-were-stopped-due-to-an-error) ### One or more workflows timed out The current CI Pipeline Execution had at least one workflow that exceeded the timeout duration. View the CI Pipeline Execution in the Nx Cloud UI for additional details. --- ## Code Ownership One of the most obvious benefits of having a monorepo is that you can share code across projects, applying the _Don't Repeat Yourself_ principle across the whole codebase. Code sharing could mean using a function or a component in multiple projects. Or code sharing could mean using a TypeScript interface to define the network API interface for both the front end and back end applications. Code sharing is usually a good thing, but it can cause problems. ## Too much sharing If everyone can use and modify every piece of code, you can run into problems. ### Devs modifying another team's code Another team can add complexity to code that your team maintains to satisfy their one use case. This adds an extra burden on you and may make it difficult to adapt that piece of code for other use cases. This can be solved by using a `CODEOWNERS` file that explicitly defines which people in an organization need to approve PRs that touch a particular section of the codebase. ### Outside devs using internal code Another team can use a piece of code that is intended to be internal to your project. Now if you change that piece of code, their project is broken. So your team is either locked in to that API or you have to solve problems in another team's project. To solve this, Nx provides a lint rule `enforce-module-boundaries` that will throw an error if a project imports code that is not being exported from the `index.ts` file at the root of a library. Now the `index.ts` file is the definitive published API for that library. ### Projects depending on the wrong libraries Libraries with presentational components can accidentally use code from a library that holds a data store. Projects with Angular code can accidentally use code from a React project. Projects from team A could accidentally use code in projects that are intended to be only for team B. These kinds of rules will vary based on the organization, but they can all be enforced automatically using tags and the `enforce-module-boundaries` lint rule. ## Defining code ownership As more teams contribute to the same repository, establish clear code ownership. Since Nx allows us to place projects in any directory structure, those directories can become code-ownership boundaries. That's why the [folder structure](/docs/kb/folder-structure) of an Nx workspace often reflects the structure of an organization. GitHub, GitLab, and Bitbucket all support a `CODEOWNERS` file that assigns required reviewers by path. ```plaintext /apps/booking/ @acme/booking-team /libs/booking/ @acme/booking-team /libs/shared/ui/ @acme/design-system-team /libs/shared/data-access/ @acme/platform-team ``` If you want to know more about code ownership on GitHub, please check [the documentation on the `CODEOWNERS` file](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners). {% aside type="tip" title="Define ownership by project" %} Maintaining a `CODEOWNERS` file by hand means revisiting it every time a project moves or a new project is added. The [`@nx/owners` plugin](/docs/reference/owners/overview) lets you define code ownership based on projects -- using the same project matcher syntax as [`nx run-many`](/docs/reference/nx-commands#nx-run-many) -- and compiles it into a valid `CODEOWNERS` file for GitHub, Bitbucket, or GitLab. {% /aside %} --- ## Compile TypeScript Libraries to Multiple Formats {% youtube src="https://youtu.be/Vy4d0-SF5cY" title="Packaging Typescript Lbraries" /%} It can be difficult to set up a TypeScript library to compile to ESM and CommonJS. You can use `@nx/rollup` to take care of it for you. ## Use Rollup to compile your TypeScript project If you do not use Rollup already, install the corresponding Nx plugin as follows: ```shell nx add @nx/rollup ``` Make sure the version of `@nx/rollup` matches your other `@nx/*` package versions. ## Configure Rollup to create multiple formats Create a `rollup.config.cjs` file in your project with the following configuration: ```javascript {% meta="{10}" %} // packages/my-awesome-lib/rollup.config.cjs const { withNx } = require('@nx/rollup/with-nx'); module.exports = withNx( { main: './src/index.ts', outputPath: './dist', tsConfig: './tsconfig.lib.json', compiler: 'swc', format: ['esm', 'cjs'], additionalEntryPoints: ['./src/foo.ts'], }, { // Additional rollup configuration options } ); ``` The `@nx/rollup/plugin` will automatically infer a `build` target for any project with a `rollup.config.cjs` file. {% aside title="Using the Executor Directly" %} You can also configure the build using the `@nx/rollup:rollup` executor in your `project.json` or `package.json` instead of the inferred plugin approach: ```jsonc // packages/my-awesome-lib/project.json { "name": "my-awesome-lib", "targets": { "build": { "executor": "@nx/rollup:rollup", "options": { "main": "packages/my-awesome-lib/src/index.ts", "format": ["esm", "cjs"], "additionalEntryPoints": ["packages/my-awesome-lib/src/foo.ts"], "generateExportsField": true, }, }, }, } ``` {% /aside %} After compiling our package using `nx build my-awesome-lib` we'll get the following output in our `dist` folder. {% filetree %} - my-awesome-lib/ - dist/ - foo.cjs.js - foo.esm.js - foo.d.ts - index.cjs.js - index.esm.js - index.d.ts - src/ - lib/ - my-awesome-lib.d.ts - src/ - ... - ... {% /filetree %} ## Configure package exports To ensure your package works correctly with TypeScript's module resolution, you need to configure the `exports` field in your source `package.json` with proper `types` entries. This is critical because TypeScript requires explicit type declarations for each export condition. {% callout type="warning" title="Types Must Be Specified for Each Condition" %} When using conditional exports with both ESM and CJS formats, you must include `types` entries for each condition. Without this, TypeScript may fail to resolve types correctly, causing compilation errors for consumers of your package. {% /callout %} Update your source `package.json` to include the exports configuration: ```json // packages/my-awesome-lib/package.json { "name": "my-awesome-lib", "version": "0.0.1", "type": "commonjs", "main": "./dist/index.cjs.js", "module": "./dist/index.esm.js", "types": "./dist/index.d.ts", "exports": { "./package.json": "./package.json", ".": { "import": { "types": "./dist/index.d.ts", "default": "./dist/index.esm.js" }, "require": { "types": "./dist/index.d.ts", "default": "./dist/index.cjs.js" } }, "./foo": { "import": { "types": "./dist/foo.d.ts", "default": "./dist/foo.esm.js" }, "require": { "types": "./dist/foo.d.ts", "default": "./dist/foo.cjs.js" } } } } ``` Now consumers of your package can access the appropriate format for their codebase and TypeScript will correctly resolve types regardless of whether they're using ESM or CommonJS. ## Verify your package After building your package, verify that the types are correctly configured. A common mistake is misconfiguring the `exports` field, which can cause TypeScript compilation errors for consumers of your package. Use [Are the types wrong?](https://arethetypeswrong.github.io/) to check your package: - **Web app**: Upload your built package or check a published npm package at [arethetypeswrong.github.io](https://arethetypeswrong.github.io/) - **CLI tool**: Install `@arethetypeswrong/cli` to check packages locally or in CI: ```shell npx @arethetypeswrong/cli ./dist/my-awesome-lib ``` This tool will identify common issues like missing type declarations for specific export conditions, helping you catch problems before publishing. --- ## Compose Executors An executor is just a function, so you can import and invoke it directly, as follows: ```typescript // example-executor.ts import printAllCaps from 'print-all-caps'; export default async function ( options: Schema, context: ExecutorContext ): Promise<{ success: true }> { // do something before await printAllCaps({ message: 'All caps' }); // do something after } ``` This only works when you know what executor you want to invoke. Sometimes, however, you need to invoke a target. For instance, the e2e target is often configured like this: ```json // project.json { "e2e": { "builder": "@nx/cypress:cypress", "options": { "cypressConfig": "apps/myapp-e2e/cypress.json", "tsConfig": "apps/myapp-e2e/tsconfig.e2e.json", "devServerTarget": "myapp:serve" } } } ``` In this case we need to invoke the target configured in devSeverTarget. We can do it as follows: ```typescript // example-executor.ts async function* startDevServer( opts: CypressExecutorOptions, context: ExecutorContext ) { const { project, target, configuration } = parseTargetString( opts.devServerTarget ); for await (const output of await runExecutor<{ success: boolean; baseUrl?: string; }>( { project, target, configuration }, { watch: opts.watch, }, context )) { if (!output.success && !opts.watch) throw new Error('Could not compile application files'); yield opts.baseUrl || (output.baseUrl as string); } } ``` The `runExecutor` utility will find the target in the configuration, find the executor, construct the options (as if you invoked it in the terminal) and invoke the executor. Note that `runExecutor` always returns an iterable instead of a promise. ## Devkit helper functions | Property | Description | | ------------------------ | -------------------------------------------------------------- | | logger | Wraps `console` to add some formatting | | getPackageManagerCommand | Returns commands for the package manager used in the workspace | | parseTargetString | Parses a target string into `{project, target, configuration}` | | readTargetOptions | Reads and combines options for a given target | | runExecutor | Constructs options and invokes an executor | See more helper functions in the [Devkit API Docs](/docs/reference/devkit) ## Using RxJS observables The Nx devkit only uses language primitives (promises and async iterables). It doesn't use RxJS observables, but you can use them and convert them to a `Promise` or an async iterable. You can convert `Observables` to a `Promise` with `toPromise`. ```typescript import { of } from 'rxjs'; export default async function (opts) { return of({ success: true }).toPromise(); } ``` You can use the [`rxjs-for-await`](https://www.npmjs.com/package/rxjs-for-await) library to convert an `Observable` into an async iterable. ```typescript import { of } from 'rxjs'; import { eachValueFrom } from 'rxjs-for-await'; export default async function (opts) { return eachValueFrom(of({ success: true })); } ``` --- ## Composing Generators Generators are useful individually, but reusing and composing generators allows you to build whole workflows out of simpler building blocks. ## Using Nx devkit generators Nx Devkit generators can be imported and invoked like any javascript function. They often return a `Promise`, so they can be used with the `await` keyword to mimic synchronous code. Because this is standard javascript, control flow logic can be adjusted with `if` blocks and `for` loops as usual. ```typescript import { libraryGenerator } from '@nx/js'; export default async function (tree: Tree, schema: any) { await libraryGenerator( tree, // virtual file system tree { name: schema.name } // options for the generator ); } ``` ## Using jscodeshift codemods Codemods created for use with [`jscodeshift`](https://github.com/facebook/jscodeshift) can be used within Nx Devkit generators using the `visitNotIgnoredFiles` helper function. This way you can compose codemods with other generators while retaining `--dry-run` and Nx Console compatibilities. ```typescript import { Tree, visitNotIgnoredFiles } from '@nx/devkit'; import { applyTransform } from 'jscodeshift/src/testUtils'; import arrowFunctionsTransform from './arrow-functions'; // The schema path can be an individual file or a directory export default async function (tree: Tree, schema: { path: string }): any { visitNotIgnoredFiles(tree, schema.path, (filePath) => { const input = tree.read(filePath).toString(); const transformOptions = {}; const output = applyTransform( { default: arrowFunctionsTransform, parser: 'ts' }, transformOptions, { source: input, path: filePath } ); tree.write(filePath, output); }); } ``` --- ## Configuring the Cloud Runner The Nx Cloud runner is configured in `nx.json`. {% tabs %} {% tabitem label="Nx >= 19.7" %} ```json // nx.json { "nxCloudId": "SOMEID" } ``` {% /tabitem %} {% tabitem label="Nx <= 19.6" %} ```json // nx.json "tasksRunnerOptions": { "default": { "runner": "nx-cloud", "options": { "nxCloudId": "SOMEID" } } } ``` To utilize personal access tokens and Nx Cloud ID with Nx <= 19.6, the nx-cloud npm package is also required to be installed in your workspaces `package.json`. ```json // package.json { "devDependencies": { "nx-cloud": "latest" } } ``` {% /tabitem %} {% /tabs %} ## CI access tokens CI Access Tokens are used in CI environments to provide read-write privileges for pipelines. They should not be committed to source control and should instead be exposed as CI environment secrets. You can configure CI Access Tokens as environment variables (`NX_CLOUD_AUTH_TOKEN` and `NX_CLOUD_ACCESS_TOKEN` are aliases of each other) or define them in `nx.json` as follows: {% tabs %} {% tabitem label="Nx >= 17" %} ```json { "nxCloudAccessToken": "SOMETOKEN" } ``` {% /tabitem %} {% tabitem label="Nx < 17" %} ```json "tasksRunnerOptions": { "default": { "runner": "nx-cloud", "options": { "accessToken": "SOMETOKEN" } } } ``` {% /tabitem %} {% /tabs %} ## Cacheable operations Targets can be marked as cacheable either in the `targetDefaults` in `nx.json` or in the project configuration by setting `"cache": true`. With this option enabled they can be cached and distributed using Nx Cloud. ## Timeouts By default, Nx Cloud requests will time out after 10 seconds. `NX_CLOUD_NO_TIMEOUTS` disables the timeout. ```shell NX_CLOUD_NO_TIMEOUTS=true nx run-many -t build ``` ## Logging Setting `NX_VERBOSE_LOGGING=true` when running a command will emit a large amount of metadata It will print information about what artifacts are being downloaded and uploaded, as well as information about the hashes of every computation. This can be useful for debugging unexpected cache misses, and issues with on-prem setups. `NX_VERBOSE_LOGGING=true` will also print detailed information about distributed task execution, such as what commands were sent where, etc. `NX_VERBOSE_LOGGING` is often enabled in CI globally while debugging your CI setups. ## Enabling end-to-end encryption All communication with Nx Cloud's API and cache is completed over HTTPS, but you can optionally enable e2e encryption by providing a secret key through `nx.json` or the `NX_CLOUD_ENCRYPTION_KEY` environment variable. {% tabs %} {% tabitem label="Nx >= 17" %} In `nx.json`, add the `nxCloudEncryptionKey` property. It will look something like this: ```json { "nxCloudEncryptionKey": "cheddar" } ``` {% /tabitem %} {% tabitem label="Nx < 17" %} In `nx.json`, locate the `taskRunnerOptions` property. Under its "options" property, you can add another property called `encryptionKey`. This is what will be used to encrypt your artifacts. It will look something like this: ```json { "tasksRunnerOptions": { "default": { "runner": "nx-cloud", "options": { "accessToken": "SOMETOKEN", // Add the following property with your secret key "encryptionKey": "cheddar" } } } } ``` {% /tabitem %} {% /tabs %} To instead use an environment variable to provide your secret key, run any Nx command as follows: ```shell NX_CLOUD_ENCRYPTION_KEY=myEncryptionKey nx build my-project ``` This is an alternative to providing the encryption key through `nx.json`, but functionally it is identical. ## Loading env variables from a file If you create an env file called `nx-cloud.env` at the root of the workspace, the Nx Cloud runner is going to load `NX_CLOUD_ENCRYPTION_KEY` and `NX_CLOUD_AUTH_TOKEN` from it. The file is often added to `.gitignore`. ## Disabling connections to Nx Cloud If your organization has a security reason to never use Nx Cloud, you can cause all methods of setting up Nx Cloud to fail by adding the `neverConnectToCloud` property to `nx.json`. This **does not prevent using Nx Cloud if already connected** nor disables the prompts themselves, as the `nx-cloud` package handles this property to provide maximum compatibility with Nx. To prevent Nx Cloud from being used when running commands, set the `NX_NO_CLOUD=true` environment variable or use the `--no-cloud` arg instead. A side effect of this is that the `nx-cloud` or `@nrwl/nx-cloud` package may still be installed in your workspace. You can safely remove this, and its presence will send no data (telemetry or otherwise) to Nx Cloud. You must be on version `16.0.4` or later of `nx-cloud` or `@nrwl/nx-cloud` for this value to be respected. ```json { // The following will cause all attempts to connect your workspace to Nx Cloud to fail. // This value does not prevent using Nx Cloud if already connected. // Use NX_NO_CLOUD=true env var to prevent using Nx Cloud when running commands "neverConnectToCloud": true } ``` --- ## Configure Inputs for Task Caching When Nx [computes the hash for a given operation](/docs/concepts/how-caching-works), it takes into account the `inputs` of the target. The `inputs` are a list of file sets, runtime inputs, and environment variables that affect the output of the target. If any of the `inputs` change, the cache is invalidated and the target is re-run. Nx errs on the side of caution when using inputs. Ideally, the "perfect" configuration of inputs will allow Nx to never re-run something when it does not need to. In practice though, it is better to play it safe and include more than strictly necessary in the inputs of a task. Forgetting to consider something during computation hash calculation may lead to negative consequences for end users. Start safe and fine-tune your inputs when there are clear opportunities to improve the cache hit rate. For an overview of all the possible [types of inputs](/docs/reference/inputs) and how to reuse sets of inputs as [named inputs](/docs/reference/inputs#named-inputs), see the reference documentation. {% aside type="caution" title="Directory Paths Require Trailing Slash or Glob" %} When specifying a directory as an input, you must use a trailing slash (`/`) or a glob pattern. For example, `{projectRoot}/src/` or `{projectRoot}/src/**/*` will match all files in the `src` directory, but `{projectRoot}/src` (without trailing slash) will not match any files. This differs from `outputs`, which support naked directory paths. {% /aside %} Throughout this recipe, the following project structure of a simple workspace will be used as an example to help understand inputs better. {% graph height="450px" %} ```json { "projects": [ { "name": "myreactapp", "type": "app", "data": { "tags": [] } }, { "name": "shared-ui", "type": "lib", "data": { "tags": [] } } ], "dependencies": { "myreactapp": [ { "source": "myreactapp", "target": "shared-ui", "type": "static" } ], "shared-ui": [] }, "workspaceLayout": { "appsDir": "", "libsDir": "" }, "affectedProjectIds": [], "focus": null, "groupByFolder": false } ``` {% /graph %} ## View the inputs of a task You can view the configuration for a task of a project by adding the `--graph` flag when running the command: ```shell nx build myreactapp --graph ``` This will show the task graph executed by Nx when running the command. Clicking the task will open a tooltip which lists out all of the inputs of the task. A button within the tooltip will also reveal more details about the configuration for the project which the task belongs to. Doing so will show a view such as the one below: {% project_details%} ```json { "project": { "name": "myreactapp", "type": "app", "data": { "root": "apps/myreactapp", "targets": { "build": { "options": { "cwd": "apps/myreactapp", "command": "vite build" }, "cache": true, "dependsOn": ["^build"], "inputs": [ "production", "^production", { "externalDependencies": ["vite"] } ], "outputs": ["{workspaceRoot}/dist/apps/myreactapp"], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["vite"] } }, "serve": { "options": { "cwd": "apps/myreactapp", "command": "vite serve", "continuous": true }, "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["vite"] } }, "preview": { "options": { "cwd": "apps/myreactapp", "command": "vite preview" }, "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["vite"] } }, "serve-static": { "executor": "@nx/web:file-server", "options": { "buildTarget": "build", "continuous": true }, "configurations": {} }, "test": { "options": { "cwd": "apps/myreactapp", "command": "vitest run" }, "cache": true, "inputs": [ "default", "^production", { "externalDependencies": ["vitest"] } ], "outputs": ["{workspaceRoot}/coverage/apps/myreactapp"], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["vite"] } }, "lint": { "cache": true, "options": { "cwd": "apps/myreactapp", "command": "eslint ." }, "inputs": [ "default", "{workspaceRoot}/.eslintrc.json", "{workspaceRoot}/apps/myreactapp/.eslintrc.json", "{workspaceRoot}/tools/eslint-rules/**/*", { "externalDependencies": ["eslint"] } ], "executor": "nx:run-commands", "configurations": {}, "metadata": { "technologies": ["eslint"] } } }, "name": "myreactapp", "$schema": "../../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "apps/myreactapp/src", "projectType": "application", "tags": [], "implicitDependencies": [], "metadata": { "technologies": ["react"] } } }, "sourceMap": { "root": ["apps/myreactapp/project.json", "nx/core/project-json"], "targets": ["apps/myreactapp/project.json", "nx/core/project-json"], "targets.build": ["apps/myreactapp/vite.config.ts", "@nx/vite/plugin"], "targets.build.command": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.build.options": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.build.cache": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.build.dependsOn": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.build.inputs": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.build.outputs": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.build.options.cwd": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.serve": ["apps/myreactapp/vite.config.ts", "@nx/vite/plugin"], "targets.serve.command": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.serve.options": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.serve.options.cwd": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.preview": ["apps/myreactapp/vite.config.ts", "@nx/vite/plugin"], "targets.preview.command": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.preview.options": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.preview.options.cwd": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.serve-static": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.serve-static.executor": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.serve-static.options": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.serve-static.options.buildTarget": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.test": ["apps/myreactapp/vite.config.ts", "@nx/vite/plugin"], "targets.test.command": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.test.options": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.test.cache": ["apps/myreactapp/vite.config.ts", "@nx/vite/plugin"], "targets.test.inputs": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.test.outputs": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.test.options.cwd": [ "apps/myreactapp/vite.config.ts", "@nx/vite/plugin" ], "targets.lint": ["apps/myreactapp/project.json", "@nx/eslint/plugin"], "targets.lint.command": [ "apps/myreactapp/project.json", "@nx/eslint/plugin" ], "targets.lint.cache": ["apps/myreactapp/project.json", "@nx/eslint/plugin"], "targets.lint.options": [ "apps/myreactapp/project.json", "@nx/eslint/plugin" ], "targets.lint.inputs": [ "apps/myreactapp/project.json", "@nx/eslint/plugin" ], "targets.lint.options.cwd": [ "apps/myreactapp/project.json", "@nx/eslint/plugin" ], "name": ["apps/myreactapp/project.json", "nx/core/project-json"], "$schema": ["apps/myreactapp/project.json", "nx/core/project-json"], "sourceRoot": ["apps/myreactapp/project.json", "nx/core/project-json"], "projectType": ["apps/myreactapp/project.json", "nx/core/project-json"], "tags": ["apps/myreactapp/project.json", "nx/core/project-json"] } } ``` {% /project_details %} Nx Console has a button which will show a preview of this screen when a project level configuration file (`project.json` or `package.json`) is opened in the IDE. Read more at [Nx Console Project Details View](/docs/kb/console-project-details). Another way of accessing this information is to run `nx show project myreactapp --web` and the view above will be opened in a browser. Use this tool to help understand what inputs are being used by Nx in your workspace. {% aside title="Note" type="note" %} If no `inputs` are specified at all, Nx will default to looking at all files of a project and its dependencies. This is a rather cautious approach. This might cause Nx to re-run a task in some cases where the cache could have been used instead but it will always give you correct output. {% /aside %} ## Configure inputs The tasks you run in your workspace will likely already have `inputs` defined. Be sure to [view the existing inputs](#viewing-the-inputs-of-a-task) and start from there. Inputs of a task are configured in the `inputs` array on the target. This can be done in several different places: - Nx Plugins often [infer inputs for tasks](/docs/concepts/mental-model#inferred-tasks) which run other tools. - In doing so, they will also define some reasonable defaults for the `inputs` of those tasks. - The `inputs` array in the `targetDefaults` for a set of targets in `nx.json`. - The `inputs` array for a specific target in the project configuration file. {% aside title="Copy the existing inputs before modifying inputs for a task" %} To override the `inputs` of a task, start by copying over the entire array shown when [viewing the project details](#viewing-the-inputs-of-a-task) and then add/modify/remove inputs as needed. {% /aside %} As you configure `inputs`, keep the project details screen open and it will refresh as changes are made. Check to make sure that the intended configuration is shown. ### Workspace level inputs [Target Defaults](/docs/reference/nx-json#target-defaults) defined in `nx.json` apply to a set of targets. Defining `inputs` here one time will apply to a set of similar targets. ```jsonc {% meta="{5}" %} // nx.json { "targetDefaults": { "build": { "inputs": ["production", "^production"], }, }, } ``` The above specifies that all targets with the name `build` will use the `inputs` specified. This configuration will override any `inputs` inferred by Nx Plugins as you have more direct control in your `nx.json` than the behavior of the Nx Plugin. The configuration defined here completely overwrites any `inputs` inferred by Nx Plugins and is not merged in any way. This configuration may be overridden by configuration in project-specific configuration files. ### Project level inputs Defining `inputs` of a target in `project.json` or `package.json` will apply only to tasks of the specific project. {% tabs %} {% tabitem label="project.json" %} ```jsonc {% meta="{6}" %} // apps/myreactapp/project.json { "name": "myreactapp", "targets": { "build": { "inputs": ["production", "^production"], }, }, } ``` {% /tabitem %} {% tabitem label="package.json" %} ```jsonc {% meta="{9}" %} // apps/myreactapp/package.json { "name": "myreactapp", "dependencies": {}, "devDependencies": {}, ... "nx": { "targets": { "build": { "inputs": ["production", "^production"] } ... } } } ``` {% /tabitem %} {% /tabs %} The above specifies that the `build` target of the `myreactapp` project will use the `inputs` specified. This configuration will override any `inputs` inferred by Nx Plugins as well as any `inputs` defined in the [Target Defaults](/docs/reference/nx-json#target-defaults) in the `nx.json` file as this is more specific than those other methods of configuring `inputs`. The configuration defined here completely overwrites any `inputs` inferred by Nx Plugins or in target defaults and is not merged in any way. ## Common inputs ### Test and config files Often, projects include some files with runtime behavior and other files for unit testing. When running the `build` task, we do not want Nx to consider test files so updating the test files does not invalidate the cache for `build` tasks. Plugins which define compile or bundling tasks such as `@nx/webpack/plugin` and `@nx/vite/plugin` will use the following inputs: ```jsonc "inputs": [ "production", // All files in a project excluding test files "^production" // Inputs of a dependencies which may affect behavior of projects which depend on them ] ``` Plugins which define testing tasks such as `@nx/cypress/plugin`, `@nx/playwright/plugin`, `@nx/jest/plugin` and `@nx/vite/plugin` will infer the following inputs for tasks: ```jsonc "inputs": [ "default", // All files in a project including test files "^production" // Inputs of a dependencies which may affect behavior of projects which depend on them ] ``` Given the above configurations, exclude the test and config files from the `production` named input: ```jsonc {% meta="{5-9}" %} // nx.json { "namedInputs": { "default": ["{projectRoot}/**/*", "sharedGlobals"], "production": [ "default", "!{projectRoot}/jest.config.ts", "!{projectRoot}/**/?(*.)+(spec|test).ts", ], }, } ``` With the above named inputs, Nx will behave in the following way: - When only test files are changed, Nx will restore previous compilation results from the cache and re-run the tests for the projects containing the test files - When any production files are changed, Nx will re-run the tests for the project as well as any projects which depend on it ### Specifying dependency file patterns directly Instead of defining a named input and referencing it with `^`, you can directly specify file patterns to consider from dependency projects using the `^{projectRoot}` syntax: ```jsonc // nx.json { "targetDefaults": { "build": { "inputs": [ "production", "^{projectRoot}/src/**/*.ts", // Only consider .ts source files from dependencies ], }, }, } ``` This is useful when you want fine-grained control over which files from dependencies affect a task's cache without creating a named input. For example, a `build` task might only need to consider `.ts` files from its dependencies rather than all production files. You can also use the object format with the `dependencies` property: ```jsonc { "inputs": [ "production", { "fileset": "{projectRoot}/src/**/*.ts", "dependencies": true }, ], } ``` ### Consider the version of a language for all tasks Many times, the version of the programming language being used will affect the behavior of all tasks for the workspace. A runtime input can be added to the `sharedGlobals` named input to consider it for the hash of every task. For example, to consider the version of Node.js in the hash of every task, add `node --version` as an input. ```jsonc {% meta="{5}" %} // nx.json { "namedInputs": { "default": ["{projectRoot}/**/*", "sharedGlobals"], "sharedGlobals": [{ "runtime": "node --version" }], }, } ``` --- ## Configure Outputs for Task Caching Whenever Nx runs a cacheable task, it will store the results of that task in the cache. When Nx runs the task again, if the [inputs for that task](/docs/kb/configure-inputs) have not changed, it will restore the results from the cache instead of spending the time to run the task again. ## Types of outputs ### Terminal output The terminal output of a task is replayed whenever a task is pulled from cache. Nx will always cache the terminal output of tasks which are cached. ### Output files Targets can define which files are produced when a task is run. Nx will cache these files so that it can restore them when the task is pulled from cache. These outputs files can be specified in several ways: ```jsonc "outputs": [ "{projectRoot}/dist/libs/mylib", // A directory "{workspaceRoot}/dist/{projectRoot}", // A directory based on the project's root "{workspaceRoot}/dist/{projectName}", // A directory based on the project's name "{workspaceRoot}/test-results.xml", // A file "{projectRoot}/dist/libs/mylib/**/*.js", // A glob pattern matching a set of files "{options.outputPath}", // A path defined in the options of a task ] ``` All outputs explicitly specifying paths must be prefixed with either `{projectRoot}` or `{workspaceRoot}` to distinguish where the path is resolved from. `{workspaceRoot}` should only appear in the beginning of an `output` but `{projectRoot}` and `{projectName}` can be specified later in the `output` to interpolate the root or name of the project into the output location. Outputs can also be determined from the `options` of running a task via the `{options.[propertyName]}` syntax. This is useful when an option for the task determines the output location and could be modified when the task is run. This path is resolved from the root of the workspace. If an output file or directory does not exist, it will be ignored. ## View outputs of a task The outputs of a task can be viewed by adding the `--graph` flag to the command: ```shell nx build myapp --graph ``` This will open the task graph in the browser. Clicking on a task in the graph will open a tooltip with a link to see details about the project. View the project's configuration to see a list of the outputs which are defined for each target. ## Configure outputs The tasks you run in your workspace will likely already have `outputs` defined. Be sure to [view the existing outputs](#viewing-outputs-of-a-task) and start from there. Nx Plugins often [infer outputs for tasks](/docs/concepts/mental-model#inferred-tasks) which run other tools. Nx Plugins will look at the configuration files and/or command-line-arguments of the tools in your workspace to understand the outputs of running those tools. In most cases, this inference will be inline with the outputs of the tool. Nx will reflect changes to the configuration or command-line arguments of your tools without any additional changes. In some cases, Nx plugins may not infer the outputs of a task as you expect, they can be configured in the `outputs` array on the target. This can be done in several different places: - The `outputs` array in the `targetDefaults` for a set of targets in `nx.json`. - The `outputs` array for a specific target in the project configuration file. {% aside title="Copy the existing outputs before modifying outputs for a task" %} To override the `outputs` of a task, start by copying over the entire array shown when [viewing the project details](#viewing-the-outputs-of-a-task) and then add/modify/remove outputs as needed. {% /aside %} As you configure `outputs`, keep the project details screen open and it will refresh as changes are made. Check to make sure that the intended configuration is shown. ### Workspace level outputs [Target Defaults](/docs/reference/nx-json#target-defaults) defined in `nx.json` apply to a set of targets. Defining `outputs` here one time will apply to a set of similar targets. ```jsonc {% meta="{5}" %} // nx.json { "targetDefaults": { "build": { "outputs": ["{projectRoot}/dist"], }, }, } ``` The above specifies Nx will cache the `dist` directory under all project roots for all targets with the name `build`. This configuration will override any `outputs` inferred by Nx Plugins as you have more direct control in your `nx.json` than the behavior of the Nx Plugin. The configuration defined here completely overwrites any `outputs` inferred by Nx Plugins and is not merged in any way. This configuration may be overwritten by configuration in project-specific configuration files. {% aside title="Warning" type="caution" %} Specifying the same output location for multiple tasks often causes unintentional behavior. While sometimes this is intentional, try and ensure that a set of targets will yield unique output locations for the tasks belonging to different projects. Use the `{projectRoot}` and `{projectName}` notation to include unique characteristics of a project in the output. {% /aside %} ### Project level outputs Defining `outputs` of a target in `project.json` or `package.json` will apply only to tasks of the specific project. {% tabs %} {% tabitem label="project.json" %} ```jsonc {% meta="{6}" %} // apps/myreactapp/project.json { "name": "myreactapp", "targets": { "build": { "outputs": ["{projectRoot}/dist"], }, }, } ``` {% /tabitem %} {% tabitem label="package.json" %} The `package.json` file may include configuration for a specific Nx project. Defining `outputs` of a target here will apply only to tasks of the specific project. ```jsonc {% meta="{10}" %} // apps/myreactapp/package.json { "name": "myreactapp", "dependencies": {}, "devDependencies": {}, ... "nx": { "targets": { "build": { "outputs": ["{projectRoot}/dist"] } ... } } } ``` {% /tabitem %} {% /tabs %} The above specifies that the `build` target of the `myreactapp` project will use the `outputs` specified. This configuration will override any `outputs` inferred by Nx Plugins as well as any `outputs` defined in the [Target Defaults](/docs/reference/nx-json#target-defaults) in the `nx.json` file as this is more specific than those other methods of configuring `outputs`. The configuration defined here completely overwrites any `outputs` inferred by Nx Plugins or in target defaults and is not merged in any way. --- ## Configure Vite on your Nx workspace {% aside type="note" title="Use our generator!" %} It is recommended that you use the [`@nx/vite:configuration`](/docs/technologies/build-tools/vite/generators#configuration) generator to set up [Vite](https://vite.dev/) for your new or existing projects. {% /aside %} The `@nx/vite` plugin generators take care of configuring Vite for you. However, you may need to set up Vite manually in some cases. This guide explains how you can configure Vite in your Nx workspace. ## TypeScript paths Use the [`vite-tsconfig-paths`](https://www.npmjs.com/package/vite-tsconfig-paths) plugin so Vite resolves the TypeScript path aliases declared in your workspace's base `tsconfig`: ```ts // vite.config.ts import { defineConfig } from 'vite'; import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ plugins: [tsconfigPaths()], }); ``` The inferred `@nx/vite/plugin` automatically ensures each project extends the workspace base `tsconfig`, so the community plugin handles path resolution end-to-end. {% aside type="caution" title="`nxViteTsPaths` is deprecated" %} The `nxViteTsPaths()` plugin from `@nx/vite/plugins/nx-tsconfig-paths.plugin` is deprecated and will be removed in Nx v24. It still works in v23, but using it logs a deprecation warning. Swap it for `tsconfigPaths()` from `vite-tsconfig-paths` as shown above. {% /aside %} ## Framework plugins If you are using React, you need to use the [`@vitejs/plugin-react` plugin](https://www.npmjs.com/package/@vitejs/plugin-react). If you're using Vue, you need to use the [`@vitejs/plugin-vue` plugin](https://www.npmjs.com/package/@vitejs/plugin-vue). ## Set the `root` path Make sure to set the `root: __dirname,` property on your config object. This is necessary to make sure that the paths are resolved correctly in your monorepo. ## Set the build `outDir` path Make sure you set the `outDir` property on your `build` object. Set the path as relative to the workspace root, so for example if your project is located in `apps/my-app`, set the `outDir` to `../../dist/apps/my-app`. If your project is located in `my-app`, set the `outDir` to `../dist/my-app`, etc. ## DTS plugin If you are building a library, you need to use the [`vite-plugin-dts` plugin](https://www.npmjs.com/package/vite-plugin-dts) to generate the `.d.ts` files for your library. ### Skip diagnostics If you are building a library, you can set the `skipDiagnostics` option to `true` to speed up the build. This means that type diagnostic will be skipped during the build process. However, if there are some files with type errors which interrupt the build process, these files will not be emitted and `.d.ts` declaration files will not be generated. If you choose to skip diagnostics, here is what your `'vite-plugin-dts'` plugin setup will look like: ```ts // libs/my-lib/vite.config.ts ... import dts from 'vite-plugin-dts'; import { join } from 'path'; ... ... export default defineConfig({ plugins: [ ..., dts({ entryRoot: 'src', tsConfigFilePath: join(__dirname, 'tsconfig.lib.json'), skipDiagnostics: true, }), ``` ### Do not skip diagnostics If you are building a library, and you want to make sure that all the files are type checked, you can set the `skipDiagnostics` option to `false` to make sure that all the files are type checked. This means that type diagnostic will be run during the build process. If you choose to enable diagnostics, here is what your `'vite-plugin-dts'` plugin setup will look like: ```ts // libs/my-lib/vite.config.ts ... import dts from 'vite-plugin-dts'; ... ... export default defineConfig({ plugins: [ ..., dts({ root: '../../', entryRoot: 'libs/my-lib/src', tsConfigFilePath: 'libs/my-lib/tsconfig.lib.json', include: ['libs/my-lib/src/**/*.ts'], outputDir: 'dist/libs/my-lib', skipDiagnostics: false, }), ``` You can read more about the configuration options in the [`vite-plugin-dts` plugin documentation](https://www.npmjs.com/package/vite-plugin-dts). ## Copying assets For most cases, drop static assets into Vite's [`publicDir`](https://vite.dev/config/shared-options.html#publicdir) and they will be copied to the output folder automatically. When you need glob-based copying (for example, picking up `*.md` files from your project root), use [`vite-plugin-static-copy`](https://www.npmjs.com/package/vite-plugin-static-copy): ```ts // vite.config.ts /// import { defineConfig } from 'vite'; import tsconfigPaths from 'vite-tsconfig-paths'; import { viteStaticCopy } from 'vite-plugin-static-copy'; // ... export default defineConfig({ root: __dirname, cacheDir: '../../node_modules/.vite/libs/testlib', plugins: [ tsconfigPaths(), viteStaticCopy({ targets: [{ src: '*.md', dest: '.' }], }), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json'), }), ], // ... }); ``` {% aside type="caution" title="`nxCopyAssetsPlugin` is deprecated" %} The `nxCopyAssetsPlugin` plugin from `@nx/vite/plugins/nx-copy-assets.plugin` is deprecated and will be removed in Nx v24. It still works in v23, but using it logs a deprecation warning. Use Vite's native `publicDir` option or `vite-plugin-static-copy` as shown above. {% /aside %} ## For testing If you're using `vitest`, make sure your `test` object in your `vite.config.ts` file looks like this: ```ts ... test: { globals: true, cache: { dir: '../node_modules/.vitest/', }, environment: 'jsdom', include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], reporters: ['default'], coverage: { reportsDirectory: '../coverage/', provider: 'v8', }, }, ... ``` Note how we're specifying `reporters` and `environment`. ## What your `vite.config.ts` looks like ### For applications Add a `vite.config.ts` file to the root of your project. If you are not using React, you can skip adding the `react` plugin, of course. ```ts // apps/my-app/vite.config.ts /// import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ root: __dirname, build: { outDir: '../../dist/apps/my-app', reportCompressedSize: true, commonjsOptions: { transformMixedEsModules: true, }, }, cacheDir: '../../node_modules/.vite/apps/my-app', server: { port: 4200, host: 'localhost', }, preview: { port: 4300, host: 'localhost', }, plugins: [react(), tsconfigPaths()], test: { reporters: ['default'], coverage: { reportsDirectory: '../../coverage/apps/my-app', provider: 'v8', }, globals: true, cache: { dir: '../../node_modules/.vitest/apps/my-app', }, environment: 'jsdom', include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], }, }); ``` ### For libraries If you are setting up a library (rather than an application) to use Vite, your `vite.config.ts` file should look like this: ```ts // libs/my-lib/vite.config.ts /// import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import dts from 'vite-plugin-dts'; import * as path from 'path'; import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ root: __dirname, cacheDir: '../node_modules/.vite/my-lib', plugins: [ react(), tsconfigPaths(), dts({ entryRoot: 'src', tsConfigFilePath: path.join(__dirname, 'tsconfig.lib.json'), skipDiagnostics: true, }), ], // Configuration for building your library. // See: https://vite.dev/guide/build.html#library-mode build: { outDir: '../dist/libs/my-lib', reportCompressedSize: true, commonjsOptions: { transformMixedEsModules: true, }, lib: { // Could also be a dictionary or array of multiple entry points. entry: 'src/index.ts', name: 'my-lib', fileName: 'index', // Change this to the formats you want to support. // Don't forget to update your package.json as well. formats: ['es'], }, rollupOptions: { // External packages that should not be bundled into your library. external: ['react', 'react-dom', 'react/jsx-runtime'], }, }, test: { globals: true, cache: { dir: '../node_modules/.vitest/libs/my-lib', }, environment: 'jsdom', include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], reporters: ['default'], coverage: { reportsDirectory: '../coverage/libs/my-lib', provider: 'v8', }, }, }); ``` In that config file, you can configure Vite as you would normally do. For more information, see the [Vite documentation](https://vite.dev/config/). ## Set up file replacements You can use the `replaceFiles()` plugin (`@nx/vite/plugins/rollup-replace-files.plugin`) to replace files in your build. You can import the plugin from `@nx/vite/plugins/rollup-replace-files.plugin`. And you can set it up like this: ```ts // apps/my-app/vite.config.ts ... import { replaceFiles } from '@nx/vite/plugins/rollup-replace-files.plugin'; export default defineConfig({ ... plugins: [ ... replaceFiles([ { replace: 'apps/my-app/src/environments/environment.ts', with: 'apps/my-app/src/environments/environment.prod.ts', }, ]), ], ... }); ``` --- ## Configuring ESLint with TypeScript ESLint is powerful linter by itself, able to work on the syntax of your source files and assert things about based on the rules you configure. It gets even more powerful, however, when TypeScript type-checker is layered on top of it when analyzing TypeScript files, which is something that `@typescript-eslint` allows us to do. By default, Nx sets up your ESLint configs with performance in mind. Creating the TypeScript `Program`s that back the type-checker is expensive compared to pure syntax analysis, so only turn typed linting on for a project once you need rules that require type information. Leave it off in your workspace's root config. How you turn it on depends on the config format. Flat configs use `parserOptions.projectService`, which lets typescript-eslint find the right tsconfig for each file. Legacy `.eslintrc` configs are JSON, so they can't express the `tsconfigRootDir` value Nx pairs with the project service, and they keep using `parserOptions.project`. Let's take an example of an ESLint config that Nx might generate for you out of the box for a Next.js project called `tuskdesk`: {% tabs syncKey="eslint-config-preference" %} {% tabitem label="Flat Config" %} ```javascript // apps/tuskdesk/eslint.config.mjs import { FlatCompat } from '@eslint/eslintrc'; import js from '@eslint/js'; import nxPlugin from '@nx/eslint-plugin'; import reactPlugin from '@nx/react/eslint-plugin'; const compat = new FlatCompat({ baseDirectory: import.meta.dirname, recommendedConfig: js.configs.recommended, }); export default [ ...reactPlugin, ...compat.config({ extends: ['../../eslint.config.mjs'] }), { ignores: ['!**/*'] }, { files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], rules: {}, }, { files: ['**/*.ts', '**/*.tsx'], rules: {}, }, { files: ['**/*.js', '**/*.jsx'], rules: {}, }, ]; ``` {% /tabitem %} {% tabitem label="Legacy (.eslintrc.json)" %} ```jsonc // apps/tuskdesk/.eslintrc.json { "extends": ["plugin:@nx/react", "../../.eslintrc.json"], "ignorePatterns": ["!**/*"], "overrides": [ { "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], "rules": {}, }, { "files": ["*.ts", "*.tsx"], "rules": {}, }, { "files": ["*.js", "*.jsx"], "rules": {}, }, ], } ``` {% /tabitem %} {% /tabs %} Here we have _no_ typed linting configured, which is appropriate because we aren't using any rules which require type information. If we now come in and add a rule which does require type information, for example `@typescript-eslint/await-thenable`, our config will look as follows: {% tabs syncKey="eslint-config-preference" %} {% tabitem label="Flat Config" %} ```javascript // apps/tuskdesk/eslint.config.mjs import { FlatCompat } from '@eslint/eslintrc'; import js from '@eslint/js'; import nxPlugin from '@nx/eslint-plugin'; import reactPlugin from '@nx/react/eslint-plugin'; const compat = new FlatCompat({ baseDirectory: import.meta.dirname, recommendedConfig: js.configs.recommended, }); export default [ ...reactPlugin, ...compat.config({ extends: ['../../eslint.config.mjs'] }), { ignores: ['!**/*'] }, { files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], rules: { // This rule requires the TypeScript type checker to be present when it runs '@typescript-eslint/await-thenable': 'error', }, }, { files: ['**/*.ts', '**/*.tsx'], rules: {}, }, { files: ['**/*.js', '**/*.jsx'], rules: {}, }, ]; ``` {% /tabitem %} {% tabitem label="Legacy (.eslintrc.json)" %} ```jsonc // apps/tuskdesk/.eslintrc.json { "extends": ["plugin:@nx/react", "../../.eslintrc.json"], "ignorePatterns": ["!**/*"], "overrides": [ { "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], "rules": { // This rule requires the TypeScript type checker to be present when it runs "@typescript-eslint/await-thenable": "error", }, }, { "files": ["*.ts", "*.tsx"], "rules": {}, }, { "files": ["*.js", "*.jsx"], "rules": {}, }, ], } ``` {% /tabitem %} {% /tabs %} Now if we try and run `nx lint tuskdesk` we will get an error ```text {% title="nx lint tuskdesk" frame="terminal" %} > nx run tuskdesk:lint Linting "tuskdesk"... You have used a rule which requires type information, but don't have parserOptions set to generate type information for this file. See https://tseslint.com/typed-linting for enabling linting with type information. ``` The solution is to update our config once more, this time to configure typed linting for the project: {% tabs syncKey="eslint-config-preference" %} {% tabitem label="Flat Config" %} ```javascript // apps/tuskdesk/eslint.config.mjs import { FlatCompat } from '@eslint/eslintrc'; import js from '@eslint/js'; import nxPlugin from '@nx/eslint-plugin'; import reactPlugin from '@nx/react/eslint-plugin'; const compat = new FlatCompat({ baseDirectory: import.meta.dirname, recommendedConfig: js.configs.recommended, }); export default [ ...reactPlugin, ...compat.config({ extends: ['../../eslint.config.mjs'] }), { ignores: ['!**/*'] }, { files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], languageOptions: { // The project service lets typescript-eslint find the tsconfig files for us and create the type-checker behind the scenes when we run linting parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, rules: { '@typescript-eslint/await-thenable': 'error', }, }, { files: ['**/*.ts', '**/*.tsx'], rules: {}, }, { files: ['**/*.js', '**/*.jsx'], rules: {}, }, ]; ``` {% /tabitem %} {% tabitem label="Legacy (.eslintrc.json)" %} ```jsonc // apps/tuskdesk/.eslintrc.json { "extends": ["plugin:@nx/react", "../../.eslintrc.json"], "ignorePatterns": ["!**/*"], "overrides": [ { "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], // We set parserOptions.project for the project to allow TypeScript to create the type-checker behind the scenes when we run linting "parserOptions": { "project": ["apps/tuskdesk/tsconfig.*?.json"], }, "rules": { "@typescript-eslint/await-thenable": "error", }, }, { "files": ["*.ts", "*.tsx"], "rules": {}, }, { "files": ["*.js", "*.jsx"], "rules": {}, }, ], } ``` {% /tabitem %} {% /tabs %} And that's it! Now any rules requiring type information will run correctly when we run `nx lint tuskdesk`. `import.meta` isn't available in CommonJS, so in a `.cjs` or `.cts` flat config use `tsconfigRootDir: __dirname` instead. A `.mts` config uses `import.meta.dirname`, like the `.mjs` example above. {% aside type="caution" title="Using Next.js with a legacy config" %} When you set `parserOptions.project` by hand, adapt the path to match your project's real path. If you apply the above to a Next.js application, you should also change the glob pattern at the end to be `tsconfig(.*)?.json`. E.g. if `tuskdesk` had been a Next.js app, we would have written: `"project": ["apps/tuskdesk/tsconfig(.*)?.json"]` Flat configs using `projectService` don't need this. The project service finds the tsconfig files itself. {% /aside %} ## Letting Nx configure it for you Generators that set up linting for a project accept `--enableTypedLinting`, which writes the block above for you in whichever format your workspace uses: ```shell nx g @nx/react:app apps/tuskdesk --enableTypedLinting ``` A generator skips the step when your project's own config already configures typed linting, whether through `projectService` or `parserOptions.project`, so re-running one won't produce a conflicting block. The block it writes sets `project` to `null` alongside `projectService`: ```javascript parserOptions: { projectService: true, // `projectService` conflicts with a `parserOptions.project` set by any config // merged into this one. Remove this once you know none of them set it. project: null, tsconfigRootDir: import.meta.dirname, }, ``` ESLint merges `parserOptions` across every config entry that matches a file, and typescript-eslint rejects a merged `parserOptions.project` next to `projectService`. Your config can pick one up from anywhere it extends, spreads in, or composes, so the block neutralizes it rather than guessing. Once you've checked that nothing your config pulls in sets `parserOptions.project`, you can delete the line. {% aside type="note" title="Deprecated flag" %} `--setParserOptionsProject` is the former name of this flag and still works, but it's deprecated and will be removed in Nx v24. Use `--enableTypedLinting` instead. {% /aside %} --- ## How Storybook is configured on your Nx workspace {% aside type="note" title="Best practices" %} Read our [Using Storybook in a Nx workspace - Best practices](/docs/kb/best-practices) guide! {% /aside %} Nx does not generate a root Storybook directory and shared root Storybook configurations. Instead, it only generates a Storybook configuration for each project in your workspace. You may still manually create a root Storybook configuration file, if it is needed for your use case. You can read all about creating and using a root Storybook configuration in our guide [How to configure Webpack and Vite for Storybook](/docs/kb/custom-builder-configs). This change means that each of your projects that has Storybook configured has only one, project-level, project-specific `.storybook/main.js|ts` file that it relies on, without importing settings from external files. This simplifies the configuration process, makes it easier for the user to understand the configuration that each project is using, and also makes it easier to maintain, relying on automatic migration scripts, for example. This does not change much for the user, as the configuration process is still the same, and you may still manually create a root Storybook configuration file, if it is needed for your use case. ## Project-specific configuration You will notice that all project-specific configuration files start with an empty `addons` array, since Storybook 9 folded the previously essential addons into the core `storybook` package. Depending on the project type, the settings differ in regards to the builder used, the stories import glob pattern, and, in the case of projects using Vite, the path to the `vite.config.ts` file or the root of the project, for correct path resolution. All these are automatically generated by Nx, so you don't have to worry about them. ## Root Storybook configuration If you need to create a root Storybook configuration file, you can do so by creating a `.storybook/main.js|ts` file in your workspace root. Then, you may import this file in your project-level Storybook configuration files. However, this is rarely needed in most cases, since most settings for builders like Webpack and Vite, for example, are already built-in into the Storybook builders. ## Read our guides for Configuring Storybook You can read all our guides for configuring Storybook in our [Storybook guides page](/docs/technologies/test-tools/storybook/guides). Please also make sure to read our [Using Storybook in a Nx workspace - Best practices](/docs/kb/best-practices) guide, where you can find some best practices for using Storybook in a Nx workspace. --- ## Nx Console Generate Command The `Generate` action allows you to choose a generator and then opens a form listing out all the options for that generator. As you make changes to the form, the generator is executed in `--dry-run` mode in a terminal so you can preview the results of running the generator in real time. {% youtube src="https://www.youtube.com/embed/-nUr66MWRiE" title="Nx Console Generate UI Form" /%} **From the Command Palette** You can also launch the `Generate` action from the Command Palette (`⇧⌘P`) by selecting `nx: generate (ui)`. {% youtube src="https://www.youtube.com/embed/Sk2XjFwF8Zo" title="Nx Console Generate UI from Command Palette" /%} You can even construct the generator options while staying entirely within the Command Palette. Use `⇧⌘P` to open the Command Palette, then select `nx: generate`. After choosing a generator, select any of the listed options to modify the generator command. When you're satisfied with the constructed command, choose the `Execute` command at the top of the list. {% youtube src="https://www.youtube.com/embed/q5NTTqRYq9c" title="Nx Console Generate with Command Palette" /%} --- ## Nx Console Migrate UI The Nx Console Migrate UI provides a visual, guided way to apply migrations in your Nx workspace. This tool simplifies the process of updating your workspace by offering an easy-to-use interface that walks you through each step of the migration process. ## Starting the migration The Migrate UI is available for VSCode and Cursor editors. Make sure you have the [Nx Console extension](/docs/getting-started/editor-setup) installed before you continue. When an update to Nx is available, a badge will appear on the Nx Console icon in your Activity Bar. Bring up the Nx Console view (Hint: type in `> Show Nx Console` in the Command Palette), and you'll see a `Nx Migrate` section in the sidebar. Clicking `Start Migration` starts the migration process. ![](../../../assets/guides/nx-console/console-migrate-1-start.avif) By default, clicking the migration button starts the migration process by upgrading to the recommended Nx version — the latest version of the next major release. This method ensures you upgrade one major version at a time in order to [avoid breakages](/docs/guides/tips-n-tricks/advanced-update#one-major-version-at-a-time-small-steps). To customize the version, click the pencil icon to provide a specific version to update to. You may also provide additional CLI options such as `--include`. ![](../../../assets/guides/nx-console/console-migrate-2-customize-version.avif) {% aside type="note" title="Ensure clean git status" %} If you have uncommitted changes, stash or commit them before initiating a migration. Otherwise, the migration button will be disabled. {% /aside %} Once you start the migration, Nx Console runs the `nx migrate` command to update your dependency versions and generate a `migrations.json` file. You'll be prompted to inspect the changes made to your `package.json` before installing them and proceeding. ![](../../../assets/guides/nx-console/console-migrate-3-confirm.avif) ## The migration process After confirming the `package.json` changes, the Migrate UI opens and guides you step-by-step through each migration action. Each migration will be executed in the order as they appear in `migration.json`. If a migration results in file changes, you will be prompted to review the changes before continuing. You can either `Accept` or `Undo` the migration. ![](../../../assets/guides/nx-console/console-migrate-4-approve.avif) If a migration step encounters an error, the process pauses so you can inspect the error details. ![](../../../assets/guides/nx-console/console-migrate-5-error.avif) You can click through to view the migration source code, giving you the opportunity to patch it for your specific use-case or make necessary adjustments to your repository before rerunning the migration. Alternatively, you may choose to skip a problematic migration. ### Migrations that use an AI prompt Some migrations ship an AI prompt instead of (or in addition to) a deterministic script - see [what's in a migration](/docs/features/automate-updating-dependencies#whats-in-a-migration). The Migrate UI marks these with an **AI** badge and handles them without running an agent itself: - **Prompt-only** migrations can't run automatically. The card shows **"AI prompt pending"** with a hint pointing at the prompt file, and **View Source** opens that prompt. Apply the prompt yourself - for example, with your AI agent - then click **Mark as Run** to record it and move on. - **Hybrid** migrations run their generator automatically, then the card shows **Generator complete** alongside **AI prompt pending**. Click **Approve Changes** to accept the generator's edits and advance, or **Mark as Run** if the generator made no changes. Your acknowledgment is remembered across editor reloads, so reopening a session brings you back to any prompt migrations you still need to finish. {% aside type="note" title="Requires a recent Nx and Nx Console" %} The AI badge and prompt handling require Nx 23.0.0-beta.24 or later and an up-to-date Nx Console extension. On older versions the Migrate UI still works without these controls. {% /aside %} ## Finalizing the migration When all migrations are done, or you don't want to run further migrations, you can finish the process by clicking the Finish button. By default, this will squash all commits created during the migration together, but you can opt into preserving them. You will be prompted for the git commit message, once you enter it the `migrations.json` file is removed and the migration process is finished. ![](../../../assets/guides/nx-console/console-migrate-6-finish.avif) --- ## Nx Console & Nx Cloud Integration Nx Console for VSCode is integrated with Nx Cloud to help you stay on top of your CI Pipelines without leaving the editor. If your workspace is connected to Nx Cloud, you will have access to a new view in the Nx Console sidebar that provides at-a-glance information about your running and recent CI pipeline executions. ![Nx Console Nx Cloud View](../../../assets/guides/nx-console/cloud-view.png) {% aside type="note" %} Nx Console will only show information about CI Pipelines from the last hour and triggered from branches that you have modified locally. If you want to see information about other pipelines, use the Nx Cloud application at [cloud.nx.app](https://cloud.nx.app?utm_source=nx-dev&utm_medium=website&utm_campaign=console-nx-cloud). {% /aside %} ## Notifications In addition to the view, you will receive notifications when a pipeline completes or a task in it fails. ![Nx Console Nx Cloud Notifications](../../../assets/guides/nx-console/cloud-notification.png) You can click on the buttons to view the results directly in Nx Cloud or open the Pull Request in the browser. To only be notified on failure or turn off notifications altogether, you can change the `nxConsole.nxCloudNotifications` setting. ## JetBrains This feature is only available in VSCode but coming soon to JetBrains. For now, you can see whether you're connected to Nx Cloud and navigate directly to the Nx Cloud application from the Nx Console Toolwindow. --- ## Nx Console Project Details View Nx Console provides seamless integration with the [Project Details View](/docs/features/explore-graph#explore-projects-in-your-workspace). You can learn more about your project, run tasks or navigate the task graph with just a few clicks! ![console-pdv-example.png](../../../assets/guides/nx-console/console-pdv-example.png) You can access the integrated Project Details View in multiple ways: - By clicking on the Preview icon to the top right of your `project.json`, `package.json` or any file that modifies targets (for example `jest.config.ts` or `cypress.config.ts`) - By using the codelenses in any of these files - By running the `Nx: Open Project Details to Side` action while any file in a project is open In addition to viewing the Project Details View, Codelenses in tooling configuration files (like `jest.config.ts`) allow you to run targets via Nx with a single click. If you would like to disable the Codelens feature, you can do so easily: - In VSCode, simply turn off the `nxConsole.enableCodeLens` setting - In JetBrains IDEs, right-click a Codelens and select ``Hide `Code Vision: Nx Config Files` Inlay Hints `` --- ## Nx Console Run Command You can construct the executor command options while staying entirely within the Command Palette. Use `⇧⌘P` to open the Command Palette, then select `Nx: Run`. After choosing a project, select a target and any of the listed options to modify the executor command options. When you're satisfied with the constructed command, choose the `Execute` command at the top of the list. You can also use `Nx: Run Target` to select a target first and then a matching project. {% youtube src="https://www.youtube.com/embed/CsUkSyQcxwQ" title="Nx Console Run from Command Palette" /%} --- ## Nx Console Telemetry To ensure that we focus on creating features that benefit your day-to-day workflow, we collect some data from the Nx Console extensions. ## Collected data Here's the information we collect for each extension. ### User data > None of the information that we ask for is used to track any personal information | Property | Description | | ----------- | ----------------------------------------------------------------------------------------- | | Client ID | These are retrieved by APIs provided by each editor. We do not generate this information. | | User ID | We use the same value as the Client ID | | Session ID | Generated UUID | | OS | What operating system are you using? | | Editor | What editor are you using? Visual Studio Code, Intellij, etc | | App Version | What version of the extension is being used? | ### Event data | Property | Description | | ------------------- | ---------------------------------- | | Extension Activated | Extension activation timings | | Action Triggered | Nx Generate, Nx Run, Nx Graph, etc | ## Visual studio code For Visual Studio Code, we use the global telemetry setting provided by the editor. This is controlled by the `telemetry.telemetryLevel` setting #### How to disable telemetry for visual studio code Setting `telemetry.telemetryLevel` to `off` will disable telemetry for Nx Console in Visual Studio Code. Read more about the telemetry settings in Visual Studio Code [here](https://code.visualstudio.com/docs/getstarted/telemetry#_disable-telemetry-reporting) ## Jetbrains (IntelliJ, webstorm, etc) When the plugin is first installed, we will prompt you to opt in or out of reporting telemetry. #### How to disable telemetry for jetbrains editors To turn off telemetry after opting in, go to **Settings** > **Tools** > **Nx Console** > Uncheck **Enable Telemetry** --- ## Module Federation Consumer and Provider (v23+) Nx v23 introduces a new generator surface in `@nx/react` (`@nx/react:consumer` and `@nx/react:provider`) that replaces the v22 `host` and `remote` generators. Shared logic (federation-name validation, port defaults, version pins) lives in `@nx/module-federation` so framework-specific generators can build on it. The new model has two roles: - **Provider** - an app that exposes a federated component. - **Consumer** - an app that loads federated components at runtime via a hardcoded `PROVIDERS` list in `src/mf.ts`. Both generators are React-only. The bundler is chosen at generation time and cannot be changed later (the bundler config is too different to switch in place). ## Pick a bundler The generator emits a plain bundler config (no Nx wrapper). Pick one at generation time: | Bundler | Pick it when | | ---------------- | -------------------------------------------------------------------------- | | `vite` (default) | Fastest dev iteration; smallest config surface; widest ecosystem. | | `rsbuild` | You want webpack-class production builds with a small config surface. | | `rspack` | You want full webpack-API compatibility and explicit control of the build. | ## Generate a provider ```shell nx g @nx/react:provider apps/my-provider --bundler=vite ``` Generated tree: ```text apps/my-provider/ ├── package.json # name, scripts, MF + framework deps ├── vite.config.mts # (or rsbuild/rspack equivalent) with federation plugin ├── index.html ├── src/ │ ├── index.ts # `import('./bootstrap')` indirection (required) │ ├── bootstrap.tsx # createRoot + render │ └── App.tsx # the federated component, default export └── tsconfig.json ``` The federation plugin exposes `./App` (or `--exposeName=`). The expose name stays as you type it for the public MF key (consumers reference `/`, so `cart-widget` is fine); the generated React component and its filename are normalized to PascalCase (`CartWidget`) so the emitted TypeScript is valid. Consumers register the provider by its `remoteEntry.js` URL. The provider runs standalone on the configured port (default `5101` for Vite, `3101` for Rsbuild, `8101` for Rspack). ## Generate a consumer ```shell nx g @nx/react:consumer apps/my-consumer --bundler=vite --providerNames=my-provider ``` Generated tree: ```text apps/my-consumer/ ├── package.json ├── vite.config.mts # NO build-time `remotes:` block ├── index.html ├── src/ │ ├── index.ts # `import('./bootstrap')` │ ├── bootstrap.tsx # createRoot + render │ ├── App.tsx # imports + renders one lazy remote per PROVIDERS entry │ └── mf.ts # hardcoded PROVIDERS list + registerRemotes + lazyProvider helper └── tsconfig.json ``` When `--providerNames=p1,p2,p3` is passed, the consumer generator also scaffolds a sibling `@nx/react:provider` app per entry (at `apps/p1`, `apps/p2`, `apps/p3`) and wires their `remoteEntry.js` URLs into the consumer's `PROVIDERS` list. Each provider's `serve` target depends on the consumer's `serve`, so `nx serve p1` brings the consumer up alongside the provider. Omit the flag and the consumer ships a placeholder `my-provider` entry in `PROVIDERS` (no actual provider project is generated). ## How dynamic federation works The consumer's `src/mf.ts` holds the remote list inline: ```ts // `name` is the provider's federation container name (derived from its // project name, so it can differ from `alias` - e.g. `myCart` -> `my_cart`). // `alias` is the key you loadRemote() with. `entry` is the remoteEntry.js URL. const PROVIDERS: Array<{ alias: string; name: string; entry: string }> = [ { alias: 'my-provider', name: 'my_provider', entry: 'http://localhost:5101/remoteEntry.js', }, ]; registerRemotes( // For vite providers only - vite emits ESM remoteEntry.js. The generator // omits `type` for rspack/rsbuild (UMD) so the runtime auto-detects. PROVIDERS.map((remote) => ({ ...remote, type: 'module' })) ); export function lazyProvider(alias, exposeName) { /* lazy + loadRemote */ } ``` Edit `PROVIDERS` to point at different providers. The entry is each provider's `remoteEntry.js` URL - the entry every bundler emits at dev + build time. (The richer [`mf-manifest.json`](https://module-federation.io/configure/manifest-fields) works for production builds and for rspack/rsbuild dev, but `@module-federation/vite` only emits it at build time, so the generator uses `remoteEntry.js` for a consistent dev experience.) The generated `App.tsx` renders each provider inside a `ProviderBoundary` (an inline class that combines `Suspense` with an error boundary, so one unreachable provider can't unmount the whole tree): ```tsx import { lazyProvider } from './mf'; // ...ProviderBoundary class omitted... const ProviderMyProvider = lazyProvider('my-provider', 'App'); export function App() { return (

my-consumer

); } ``` Wrap each `` in your router of choice (TanStack Router, React Router, etc.) if you need routing. ## What changed in v23 The following surfaces are deprecated in v23 and will be removed in v24: - `@nx/react:host`, `@nx/react:remote`, `@nx/react:federate-module` - `@nx/angular:host`, `@nx/angular:remote`, `@nx/angular:setup-mf`, `@nx/angular:federate-module` - `@nx/react:module-federation-dev-server`, `@nx/react:module-federation-ssr-dev-server`, `@nx/react:module-federation-static-server` - `@nx/angular:module-federation-dev-server`, `@nx/angular:module-federation-dev-ssr` - `@nx/rspack:module-federation-dev-server`, `@nx/rspack:module-federation-ssr-dev-server`, `@nx/rspack:module-federation-static-server` The most user-visible change: **`nx serve ` no longer auto-builds and serves all remotes.** With dynamic federation, the relationship is inverted - serve a provider and Nx brings its consumer along (`provider.serve.dependsOn = [':serve']`). Missing providers don't crash the consumer; they reject at `loadRemote` time and the generated `ProviderBoundary` renders an inline fallback. Angular Module Federation in Nx is no longer supported. Use [`@angular-architects/native-federation`](https://www.npmjs.com/package/@angular-architects/native-federation) for the supported Angular path going forward. ## Migrate from `host` / `remote` There is **no automated codemod**. Existing setups vary widely (custom executors, host orchestration, SSR variants) and a generator would not land cleanly. Migrate manually using the steps below, or paste the AI prompt at the end of this section into Cursor / Claude Code / Copilot to perform the rewrite on your own codebase. ### Manual steps 1. Generate a fresh `consumer` for each existing host and a `provider` for each existing remote, using the same bundler you were on (or upgrade to Vite). Use a temporary directory so nothing overwrites the originals. 2. Delete `module-federation.config.ts` from each app. 3. Replace the bundler config in each app with the generated one. Port any custom `withModuleFederation` wrapping to inline plugin options. 4. Convert each consumer's static `remotes:` list into entries in `src/mf.ts`'s `PROVIDERS` constant (URLs point at each provider's `remoteEntry.js`). 5. Rewrite `import('remote-name/Module')` calls to `lazyProvider('remote-name', 'Module')` from `src/mf.ts`. 6. Drop the `customWebpackConfig` block from each `project.json`. Replace the `serve` target with one that runs `vite` / `rsbuild` / `rspack` directly via `nx:run-commands`. 7. On each provider's `serve` target, add `dependsOn: [':serve']` if you want `nx serve ` to spin up the consumer alongside. ### AI prompt Paste this into your AI assistant of choice, adjusted with your project paths: ```markdown You are migrating an Nx workspace from the deprecated `@nx/react:host` / `@nx/react:remote` generators to the new `@nx/react:consumer` / `@nx/react:provider` generators (Nx v23+). For each `host` app in `apps/`: 1. Delete `module-federation.config.ts`. 2. Replace the bundler config with a plain `vite.config.ts` / `rsbuild.config.ts` / `rspack.config.ts` that uses `@module-federation/vite` (vite), `@module-federation/rsbuild-plugin` (rsbuild), or `@module-federation/enhanced/rspack` (rspack) directly. No `withModuleFederation` wrappers. 3. Create `src/mf.ts` with a `PROVIDERS` constant - an array of `{ alias, name, entry }` where `name` is the provider's federation container name (derived from its project name, so it can differ from `alias`, e.g. `myCart` -> `my_cart`) and `entry` is its `remoteEntry.js` URL. At module init, call `registerRemotes` from `@module-federation/runtime` with one entry per provider (set `type: 'module'` for vite providers; omit it for rspack/rsbuild). Export `lazyProvider(alias, exposeName)` that returns `React.lazy(() => loadRemote(...))`. 4. Replace all `import('remote-name/Module')` calls with `lazyProvider('remote-name', 'Module')`. 5. Drop the `customWebpackConfig` block from `project.json`. Replace `serve` and `build` targets with `nx:run-commands` invocations of the bundler directly. For each `remote` app, do the equivalent provider conversion: drop the federation config file, write a plain bundler config with the federation plugin exposing the same modules, and emit a standalone `index.html` + `src/index.ts` -> `src/bootstrap.tsx` indirection. Add `serve.dependsOn: [':serve']` to the provider's project.json if you want `nx serve ` to also start the consumer. Do not orchestrate remotes from the host. Each app is served independently; missing remotes render via the consumer's Suspense + ErrorBoundary. ``` ## SSR SSR is not first-classed in the new generators. The deprecated `module-federation-ssr-dev-server` executors are gone. If you need SSR with federation, see the upstream Module Federation [SSR guide](https://module-federation.io/) and wire it yourself on top of the generated consumer skeleton. ## Reference A worked reference workspace covering all three bundlers, dynamic federation, and Angular Native Federation lives at the [mf-examples repository](https://github.com/nrwl/mf-examples) (see `apps/nx-react-vite/` for the canonical Nx-wired setup). --- ## Migrate to Inferred Tasks In this recipe, you'll learn how to migrate an existing Nx workspace from using executors in `project.json` to using [inferred tasks](/docs/concepts/mental-model#inferred-tasks). The main benefits of migrating to inferred tasks are - reducing the amount of configuration needed in `project.json` - inferring the correct cache settings based on the tool configuration files - [splitting tasks (Atomizer)](/docs/features/ci-features/split-e2e-tasks) for plugins that support it {% youtube src="https://youtu.be/wADNsVItnsM" title="Inferred Tasks" /%} For the best experience, we recommend that you [migrate](/docs/features/automate-updating-dependencies) to the latest Nx version before continuing. ```shell npx nx migrate latest ``` ## Enable inferred tasks for older workspaces If your workspace was upgraded from an Nx version that predates inferred tasks, a migration may have set `useInferencePlugins` to `false` in `nx.json`. When `useInferencePlugins` is `false`: 1. A newly generated project will have all targets defined with executors, not with inferred tasks. 2. Running `nx add @nx/some-plugin` will not register the plugin in `nx.json`, so that plugin will not infer tasks. Remove the property, or set it to `true`, before you migrate your projects. ## Migrate all plugins You can use the `infer-targets` generator to quickly migrate all available plugins to use inferred tasks. See the sections below for more details on the individual plugins' migration processes. ```shell npx nx g infer-targets ``` The generator will automatically detect all available `convert-to-inferred` generators and run the ones you choose. If you only want to try it on a single project, pass the `--project` option. ## Migrate a single plugin Most of the official plugins come with a `convert-to-inferred` generator. This generator will - register the inference plugin in the `plugins` section of `nx.json` - migrate executor options into the tool's configuration files (where applicable) - centralize configuration that is shared across the migrated projects into `targetDefaults` in `nx.json` (whole-workspace migrations only, see below) - clean up `project.json` to keep only the configuration that deviates from what is inferred or centralized - remove `targetDefaults` entries keyed by a migrated executor once no target resolves through that executor (whole-workspace migrations only) To get started, run `nx g convert-to-inferred`, and you'll be prompted to choose a plugin to migrate. ```text {% title="npx nx g convert-to-inferred" frame="terminal" %} ? Which generator would you like to use? … @nx/eslint:convert-to-inferred @nx/playwright:convert-to-inferred @nx/vite:convert-to-inferred None of the above ``` {% aside type="note" title="Third-party plugins" %} For third-party plugins that provide `convert-to-inferred` generators, you should pick the `None of the above` option and type in the name of the package manually. Alternatively, you can also provide the package explicitly with `nx g :convert-to-inferred`. {% /aside %} We recommend that you check that the configurations are correct before continuing to the next plugin. If you only want to try it on a single project, pass the `--project` option. ## Understand the migration process The `convert-to-inferred` generator removes uses of executors from the corresponding plugin. For example, if `@nx/vite` is migrated, then uses of [`@nx/vite:build`](/docs/technologies/build-tools/vite/executors#build), [`@nx/vite:dev-server`](/docs/technologies/build-tools/vite/executors#dev-server), [`@nx/vite:preview-server`](/docs/technologies/build-tools/vite/executors#preview-server), and [`@nx/vite:test`](/docs/technologies/build-tools/vite/executors#test) executors will be removed. Target and configuration names are maintained for each project in their `project.json` files. A target may be removed from `project.json` entirely when nothing is left to store there: either everything is inferred, or the remaining customized options and configurations were shared across the migrated projects and moved into `targetDefaults` in `nx.json` (whole-workspace migrations only). What stays in `project.json` is the per-project deviation. To get the full project details (including all inferred tasks), run: ```shell npx nx show project ``` For example, if we migrated the `@nx/vite` plugin for a single app (i.e. `nx g @nx/vite:convert-to-inferred --project demo`), then running `nx show project demo` will show a screen similar to the following. {% project_details title="Test" height="300px" %} ```json { "project": { "name": "demo", "data": { "root": " apps/demo", "projectType": "application", "targets": { "serve": { "executor": "nx:run-commands", "options": { "command": "vite dev", "continuous": true } }, "build": { "executor": "nx:run-commands", "inputs": ["production", "^production"], "outputs": ["{projectRoot}/dist"], "options": { "command": "vite build" } } } } }, "sourceMap": { "targets": ["apps/demo/vite.config.ts", "@nx/vite"], "targets.serve": ["apps/demo/vite.config.ts", "@nx/vite"], "targets.build": ["apps/demo/vite.config.ts", "@nx/vite"] } } ``` {% /project_details %} You'll notice that the `serve` and `build` tasks are running the [Vite CLI](https://vite.dev/guide/cli.html) and there are no references to Nx executors. Since the targets directly invoke the Vite CLI, any options that may be passed to it can be passed via Nx commands. e.g. `nx serve demo --cors --port 8888` enables CORs and uses port `8888` using [Vite CLI options](https://vite.dev/guide/cli.html#options) The same CLI setup applies to other plugins as well. - `@nx/cypress` calls the [Cypress CLI](https://docs.cypress.io/guides/guides/command-line) - `@nx/playwright` calls the [Playwright CLI](https://playwright.dev/docs/test-cli) - `@nx/webpack` calls the [Webpack CLI](https://webpack.js.org/api/cli/) - etc. Read the recipe on [passing args to commands](/docs/kb/pass-args-to-commands) for more information. ### When inference does not produce the expected target The migration reads each executor target, then asks the plugin to infer the equivalent task from the tool's configuration files. If the plugin finds your project but does not infer a task with the name the migration expects, the migration stops with an error like `The nx plugin found a project inside but did not infer a "" target for it`. See the [Troubleshooting](#troubleshooting) section below for how to resolve this error and the related `The nx plugin did not find a project inside ...` error. ### Configuration file changes There may also be changes to the configuration files used by the underlying tool. The changes come with comments to explain them, and may also provide next steps for you to take. One common change is to add support for different configuration options. For example, if we have an existing Vite app with the following build target: ```json // project.json "build": { "executor": "@nx/vite:build", "options": { "mode": "development" }, "defaultConfiguration": "production", "configurations": { "development": {}, "production": {}, "ci": {} } } ``` Where we have `development`, `production`, and `ci` configurations. Then running `nx g @nx/vite:convert-to-inferred` will result in these lines added to `vite.config.ts`. ```ts {% meta="{7-16}" %} // vite.config.ts /// import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; // These options were migrated by @nx/vite:convert-to-inferred from the project.json file. const configValues = { default: {}, development: {}, production: {}, ci: {} }; // Determine the correct configValue to use based on the configuration const nxConfiguration = process.env.NX_TASK_TARGET_CONFIGURATION ?? 'default'; const options = { ...configValues.default, ...(configValues[nxConfiguration] ?? {}), }; export default defineConfig({ root: __dirname, cacheDir: '../../node_modules/.vite/apps/demo', // ... }); ``` The configuration changes ensure that passing `--configuration` still work for the target. Differences in options can be added to the `configValues` object, and the right value is determined using the `NX_TASK_TARGET_CONFIGURATION` [environment variable](/docs/reference/environment-variables). Again, there may be other types of changes so read the comments to understand them. ### Register the Plugin with Nx Lastly, you can inspect the `nx.json` file to see a new `plugins` entry. For `@nx/vite`, there should be an entry like this: ```json // nx.json { "plugin": "@nx/vite/plugin", "options": { "buildTargetName": "build", "serveTargetName": "serve", "previewTargetName": "preview", "testTargetName": "test", "serveStaticTargetName": "serve-static" } } ``` You may change the target name options to change how Nx adds them to the project. For example, if you use `"serveTargetName": "dev"` then you would run `nx dev demo` rather than `nx serve demo` for your Vite project. ### Centralize shared configuration in `targetDefaults` When you migrate the whole workspace (that is, without `--project`), the generator also looks for configuration that is identical across every migrated project for a target and lifts it into `targetDefaults` in `nx.json`, so it lives in one place instead of being repeated in each `project.json`. The entry is scoped to the plugin's targets with a `filter`, so it only applies to the targets this plugin infers: ```json // nx.json { "targetDefaults": { "build": [ { "filter": { "plugin": "@nx/vite/plugin" }, "options": { "config": "./vite.config.ts" } } ] } } ``` Each project's `project.json` then keeps only the options that differ from this shared default. Configuration a project overrides in its own `project.json` still wins over the centralized default. Some projects are excluded from centralization: when a target's identity is authored outside the plugin (a `project.json` `executor` or `command`, or a `package.json` script or `nx.targets` entry), the centralized default does not resolve for that target. Those projects keep their full per-project configuration, and the generator prints a warning naming them. When a `package.json` script with the target's name, or an `nx.targets` entry next to a `project.json`, authors the identity, the generator also leaves the target unconverted, because removing its executor would let that entry take the target over. Rename or exclude the script, or remove that `nx.targets` entry, then rerun the generator to convert it. When multiple targets in one project map different target names to the same plugin option, for example two `@nx/jest:jest` targets in one project, a whole-workspace migration converts one of them, leaves the conflicting targets unchanged, and prints a warning naming them. Once the converted target is inferred, rerun the migration to convert the next one. A single-project migration (`--project`) stops with an error instead, because one plugin registration can hold only one value for that option. Centralization is also skipped when other plugins are registered after the migrated plugin in `nx.json`. A plugin registered later can take over a target's identity, and the shared default would then stop applying. The generator prints a warning and keeps the full per-project configuration in that case. Centralization is skipped for a target when adding the shared entry would change which existing `targetDefaults` apply to it. For example, an exact target-name entry takes precedence over a glob entry such as `build-*`, so adding one could stop the glob entry's configuration from applying to the migrated targets. The generator prints a warning and keeps the full per-project configuration for that target. When you convert several plugins in one run of the `infer-targets` generator, the generator defers centralization until every conversion has run and registered its plugin, then centralizes the shared configuration for the whole batch in a single pass. Because that pass sees the finished plugin list, registrations added by later conversions in the same run do not block earlier ones, and every conversion in the batch is eligible to centralize. Each conversion is still subject to the skip conditions above and to the verification step below, evaluated against the final state of `nx.json`. A plugin registered earlier than the migrated plugin can remain the source of a target when it infers the same target for the same project with the same executor. The generator cannot detect this case, and the centralized default does not apply to that target. Review the configuration when two inference plugins produce the same target name and executor for the same project. For `nx:run-commands` and `nx:run-script` targets, also compare the command or script. After centralizing, the generator verifies that every migrated target still resolves to the same configuration. If the centralized default would also reach a project the generator did not migrate, or the verification cannot confirm the result is equivalent, the generator restores full per-project configuration for the affected targets and prints a warning. This step is skipped for single-project (`--project`) migrations, since centralizing one project's configuration would leak it onto its siblings. ## Verify the migration The migrations maintain the same targets and configurations for each project, thus to verify it you should run the affected targets. For example - for `@nx/vite` you should check the `build`, `serve`, and `test` targets - for `@nx/playwright` you should check the `e2e` targets - for `@nx/eslint` you should check the `lint` target - etc. Remember that the target names are defined in the plugin configuration in `nx.json`. Make sure that the tasks are all passing before migrating another plugin. ## Enable atomizer (task splitting) These plugins come with the [Atomizer](/docs/features/ci-features/split-e2e-tasks) feature. - `@nx/cypress` - `@nx/jest` - `@nx/gradle` - `@nx/playwright` The Atomizer splits potentially slow tasks into separate tasks per file. This feature along with [task distribution](/docs/features/ci-features/distribute-task-execution) can speed up CI by distributing the split tasks among many agents. To enable Atomizer, make sure that you are [connected to Nx Cloud](/docs/kb/setup-ci), and that you have distribution enabled in CI. Some plugins require extra configuration to enable Atomizer, so check the [individual plugin documentation page](/docs/plugin-registry) for more details. {% call_to_action title="Connect to Nx Cloud" icon="nxcloud" description="Enable task distribution and Atomizer" url="/docs/kb/setup-ci" /%} ## Troubleshooting If you run into any issues during the migration, refer to the [troubleshooting guide](/docs/kb/troubleshoot-convert-to-inferred). --- ## createConfig - @nx/angular-rspack ```shell import { createConfig } from '@nx/angular-rspack'; ``` The `createConfig` function is used to create an Rspack configuration object setup for Angular applications. It takes an optional `Configuration` object as an argument, which allows for customization of the Rspack configuration. ```ts async function createConfig( defaultOptions: { options: AngularRspackPluginOptions; rspackConfigOverrides?: Partial; }, configurations: Record< string, { options: Partial; rspackConfigOverrides?: Partial; } > = {}, configEnvVar = 'NGRS_CONFIG' ); ``` --- ## Examples {% tabs %} {% tabitem label="Server-Side Rendering (SSR)" %} The following example shows how to create a configuration for a SSR application: ```ts // myapp/rspack.config.ts import { createConfig } from '@nx/angular-rspack'; export default createConfig({ options: { browser: './src/main.ts', server: './src/main.server.ts', ssrEntry: './src/server.ts', }, }); ``` {% /tabitem %} {% tabitem label="Client-Side Rendering (CSR)" %} The following example shows how to create a configuration for a CSR application: ```ts // myapp/rspack.config.ts import { createConfig } from '@nx/angular-rspack'; export default createConfig({ options: { browser: './src/main.ts', }, }); ``` {% /tabitem %} {% tabitem label="Modify Rspack Configuration" %} The following example shows how to modify the base Rspack configuration: ```ts // myapp/rspack.config.ts import { createConfig } from '@nx/angular-rspack'; export default createConfig({ options: { browser: './src/main.ts', server: './src/main.server.ts', ssrEntry: './src/server.ts', }, rspackConfigOverrides: { mode: 'development', }, }); ``` {% /tabitem %} {% tabitem label="File Replacements" %} The following example shows how to use file replacements: ```ts // myapp/rspack.config.ts import { createConfig } from '@nx/angular-rspack'; export default createConfig({ options: { browser: './src/main.ts', server: './src/main.server.ts', ssrEntry: './src/server.ts', fileReplacements: [ { replace: './src/environments/environment.ts', with: './src/environments/environment.prod.ts', }, ], }, }); ``` {% /tabitem %} {% /tabs %} --- ## AngularRspackPluginOptions The `AngularRspackPluginOptions` object is an object that contains the following properties: ```ts export interface AngularRspackPluginOptions extends PluginUnsupportedOptions { aot?: boolean; assets?: AssetElement[]; browser?: string; commonChunk?: boolean; devServer?: DevServerOptions; extractLicenses?: boolean; fileReplacements?: FileReplacement[]; index?: IndexElement; inlineStyleLanguage?: InlineStyleLanguage; namedChunks?: boolean; optimization?: boolean | OptimizationOptions; outputHashing?: OutputHashing; outputPath?: string | (Required> & Partial); polyfills?: string[]; root?: string; scripts?: ScriptOrStyleEntry[]; server?: string; skipTypeChecking?: boolean; sourceMap?: boolean | Partial; ssr?: | boolean | { entry: string; platform?: 'node' | 'neutral'; // Deprecated. Use `platform` instead. experimentalPlatform?: 'node' | 'neutral'; }; stylePreprocessorOptions?: StylePreprocessorOptions; styles?: ScriptOrStyleEntry[]; tsConfig?: string; useTsProjectReferences?: boolean; vendorChunk?: boolean; } export interface DevServerOptions extends DevServerUnsupportedOptions { port?: number; ssl?: boolean; sslKey?: string; sslCert?: string; proxyConfig?: string; } export interface OptimizationOptions { scripts?: boolean; styles?: boolean; fonts?: boolean; } export type OutputHashing = 'none' | 'all' | 'media' | 'bundles'; export type HashFormat = { chunk: string; extract: string; file: string; script: string; }; export interface OutputPath { base: string; browser: string; server: string; media: string; } export type AssetExpandedDefinition = { glob: string; input: string; ignore?: string[]; output?: string; }; export type AssetElement = AssetExpandedDefinition | string; export type NormalizedAssetElement = AssetExpandedDefinition & { output: string; }; export type ScriptOrStyleEntry = | string | { input: string; bundleName?: string; inject?: boolean; }; export type GlobalEntry = { name: string; files: string[]; initial: boolean; }; export type IndexExpandedDefinition = { input: string; output?: string; preloadInitial?: boolean; }; export type IndexElement = IndexExpandedDefinition | string | false; export type IndexHtmlTransform = (content: string) => Promise; export type NormalizedIndexElement = | (IndexExpandedDefinition & { insertionOrder: [string, boolean][]; transformer: IndexHtmlTransform | undefined; }) | false; export interface SourceMap { scripts: boolean; styles: boolean; hidden: boolean; vendor: boolean; } export type InlineStyleExtension = 'css' | 'scss' | 'sass' | 'less'; export interface FileReplacement { replace: string; with: string; } export interface StylePreprocessorOptions { includePaths?: string[]; sass?: Sass; } export interface Sass { fatalDeprecations?: DeprecationOrId[]; futureDeprecations?: DeprecationOrId[]; silenceDeprecations?: DeprecationOrId[]; } ``` --- ### aot `boolean` `default: true` Enables or disables Ahead-of-Time compilation for Angular applications. ### assets `AssetElement[]` Array of static assets to include in the build output. Can be either a string path or an object with glob patterns. ### browser `string` The entry point file for the browser bundle (e.g., 'src/main.ts'). ### commonChunk `boolean` `default: true` Controls whether to create a separate bundle containing shared code between multiple chunks. ### devServer `DevServerOptions` Configuration options for the development server including port, SSL settings, and proxy configuration. ### extractLicenses `boolean` `default: false` When true, extracts all license information from dependencies into a separate file. ### fileReplacements `FileReplacement[]` List of files to be replaced during the build process, typically used for environment-specific configurations. ### index `IndexElement` Configuration for the index.html file. Can be a string path, an object with specific settings, or false to disable. ### inlineStyleLanguage `InlineStyleLanguage` Specifies the default language to use for inline styles in components. ### namedChunks `boolean` `default: true` When true, generates named chunks instead of numerical IDs. ### optimization `boolean | OptimizationOptions` `default: true` Controls build optimization settings for scripts, styles, and fonts. ### outputHashing `OutputHashing` `default: 'none'` Defines the hashing strategy for output files. Can be 'none', 'all', 'media', or 'bundles'. ### outputPath `string | OutputPath` Specifies the output directory for built files. Can be a string or an object defining paths for browser, server, and media files. ### polyfills `string[]` Array of polyfill files to include in the build. ### root `string` The root directory of the project where the rspack.config.ts file is located. ### scripts `ScriptOrStyleEntry[]` Array of global scripts to include in the build, with options for bundling and injection. ### server `string` The entry point file for the server bundle in SSR applications. ### skipTypeChecking `boolean` `default: false` When true, skips TypeScript type checking during the build process. ### sourceMap `boolean | Partial` `default: true` Controls generation of source maps for debugging. Can be boolean or detailed configuration object. ### ssr `boolean | { entry: string; platform?: 'node' | 'neutral' }` Configuration for Server-Side Rendering. Can be boolean or object with specific SSR settings. The `experimentalPlatform` option is deprecated; use `platform` instead. ### stylePreprocessorOptions `StylePreprocessorOptions` Options for style preprocessors, including include paths and Sass-specific configurations. ### styles `ScriptOrStyleEntry[]` Array of global styles to include in the build, with options for bundling and injection. ### tsConfig `string` Path to the TypeScript configuration file. ### useTsProjectReferences `boolean` `default: false` Enables usage of TypeScript project references. ### vendorChunk `boolean` `default: true` When true, creates a separate bundle for vendor (third-party) code. --- ## Creating an Install Package {% youtube src="https://www.youtube.com/embed/ocllb5KEXZk" title="Build your own CLI" width="100%" /%} Starting a new project should be as seamless as possible. In the JavaScript ecosystem, the idea of bootstrapping a new project with a single command has become a must-have for providing a good DX. So much that all the major package managers have a dedicated feature already built-in: if you publish a package named `create-{x}`, it can be invoked via any of the following: - npx create-{x} - yarn create {x} - npm init {x} - pnpm init {x} These packages are used to set up a new project in some form. Customizing your initial project setup is already possible with an [Nx Preset generator](/docs/kb/create-preset). By creating and shipping a generator named `preset` in your Nx plugin, you can then pass it via the `--preset` flag to the `create-nx-workspace` command: ```shell npx create-nx-workspace --preset my-plugin ``` This allows you to take full control over the shape of the generated Nx workspace. You might however want to have your own `create-{x}` package, whether that is for marketing purposes, branding or better discoverability. You can have such a `create-{x}` package generated for you. ## Generating a "Create package" There are a few methods to create a package that will work with `create-nx-workspace`'s public API to setup a new workspace that uses your Nx plugin. You can setup a new Nx plugin workspace and immediately pass the `--create-package-name`: ```shell npx create-nx-plugin my-plugin --create-package-name create-my-plugin ``` Alternatively, if you already have an existing Nx plugin workspace, you can run the following generator to set up a new create package: ```shell nx g create-package create-my-plugin --project my-plugin ``` ## Customize your create package You'll have 2 packages that are relevant in your workspace: - The create package (e.g. `create-my-plugin`) - The plugin package (e.g. `my-plugin`) Let's take a look at the code that was scaffolded out for your `create-my-plugin` package: ```typescript // packages/create-my-plugin/bin/index.ts #!/usr/bin/env node import { createWorkspace } from 'create-nx-workspace'; async function main() { const name = process.argv[2]; // TODO: use libraries like yargs, @clack/prompts, or another prompting library to set your workspace name if (!name) { throw new Error('Please provide a name for the workspace'); } console.log(`Creating the workspace: ${name}`); // This assumes "my-plugin" and "create-my-plugin" are at the same version // eslint-disable-next-line @typescript-eslint/no-var-requires const presetVersion = require('../package.json').version; // TODO: update below to customize the workspace const { directory } = await createWorkspace(`my-plugin@${presetVersion}`, { name, nxCloud: 'skip', packageManager: 'npm', }); console.log(`Successfully created the workspace: ${directory}.`); } main(); ``` This is a plain node script at this point, and you can use any dependencies you wish to handle things like prompting or argument parsing. Keeping dependencies small and splitting out the command line tool from the Nx plugin is recommended, and will help keep your CLI feeling fast and snappy. Note the following code snippet: ```typescript const { directory } = await createWorkspace(`my-plugin@${presetVersion}`, { name, nxCloud: 'skip', packageManager: 'npm', }); ``` This will invoke the `my-plugin` package's `preset` generator, which will contain the logic for setting up the workspace. This preset generator will be invoked when running either `npx create-nx-workspace --preset my-plugin` or `npx create-my-plugin`. For more information about customizing your preset, see: [Creating a Preset](/docs/kb/create-preset). ## Testing your create package Because your `create-my-plugin` package will install your plugin package at runtime, both packages must be published in order to run them and see the results. To test your packages without making them publicly available, a `local-registry` target should be present on project in your workspace's root. ```jsonc // project.json { ... "targets": { "local-registry": { "executor": "@nx/js:verdaccio", "options": { "port": 4873, "config": ".verdaccio/config.yml", "storage": "tmp/local-registry/storage" } } } } ``` _(If you don't have such a `local-registry` target, refer to the following [docs page to generate one](/docs/technologies/typescript/generators#setup-verdaccio))_ By running ```shell npx nx local-registry ``` ...a local instance of [Verdaccio](https://verdaccio.org/) will be launched at http://localhost:4873 and the NPM, Yarn and PNPM registries will be configured to point to it. This means that you can safely publish, without hitting npm, and test as if you were an end user of your package. {% aside type="note" title="Registry Cleanup & Reset" %} Note, after terminating the terminal window where the `nx local-registry` command is running (e.g. using `CTRL+c` or `CMD+c`) the registry will be stopped, previously installed packages will be cleaned up and the npm/yarn/pnpm registries will be restored to their original state, pointing to the real NPM servers again. {% /aside %} Next, you can **publish** your packages to your new local registry. All of the generated packages can use `nx release` to publish whatever is in your `build` output folder, so you can simply run: ```shell npx nx run-many --targets build npx nx release version 1.0.0 npx nx release publish --tag latest ``` Once the packages are published, you should be able to test the behavior of your "create package" as follows: ```shell npx create-my-plugin test-workspace ``` ## Writing and running e2e tests When setting up the workspace, you should also have gotten a `my-plugin-e2e` package. This package contains the e2e tests for your plugin, and can be run with the following command: ```shell npx nx e2e my-plugin-e2e ``` Have a look at some of the example tests that were generated for you. When running these tests, - the local registry will be started automatically - a new version of the packages will be deployed - then your test commands will be run (usually triggering processes that setup the workspace, just like the user would type into a command line interface) - once the test commands have finished, the local registry will be stopped again and cleaned up ## Publishing your create package Your plugin and create package will both need to be published to NPM to be useable. Publishing your packages is exactly the same as described [previously](#testing-your-create-package), except that you don't run the `local-registry` task so that the `publish` task will publish to the real NPM servers. ## Further reading - [Blog post: Create your own create-react-app CLI](https://nx.dev/blog/create-your-own-create-react-app-cli) --- ## Create a Custom Plugin Preset When you create a new nx workspace, you run the command: [`npx create-nx-workspace`](/docs/reference/create-nx-workspace). This command accepts a `--preset` option, for example: `npx create-nx-workspace --preset=react-standalone`. This preset option is pointing to a special generator function (remember, a generator is a function that simplifies an entire code generation script into a single function) that Nx will call when this `npx create-nx-workspace` command is run, that will generate your initial workspace. {% youtube src="https://www.youtube.com/embed/yGUrF0-uqaU" title="Develop a Nx Preset for your Nx Plugin" /%} ## What is a preset? A preset is a special [generator](/docs/features/generate-code) shipped as part of an Nx Plugin package. All first-party Nx presets are built into Nx itself, but you can [create your own plugin](/docs/kb/add-language-support) and create a generator with the magic name: `preset`. Once you've [published your plugin](/docs/kb/publish-plugin) on npm, you can now run the `create-nx-workspace` command with the preset option set to the name of your published package. To use a concrete example, let's look at the [`qwik-nx`](https://www.npmjs.com/package/qwik-nx) Nx community plugin. They include a [preset generator](https://github.com/qwikifiers/qwik-nx/tree/main/packages/qwik-nx/src/generators/preset) that you can use to create a new Nx workspace with Qwik support. ```shell npx create-nx-workspace --preset=qwik-nx ``` ## Create a new Nx plugin If you **don't** have an existing plugin you can create one by running ```shell npx create-nx-plugin my-org --pluginName my-plugin ``` ## Creating a "Preset" generator To create our preset inside of our plugin we can run ```shell nx generate @nx/plugin:generator packages/happynrwl/src/generators/preset ``` {% aside type="caution" title="Double check" %} The word `preset` is required for the name of this generator {% /aside %} You should have a similar structure to this: {% filetree %} - happynrwl/ - e2e/ - jest.config.js - jest.preset.js - nx.json - package-lock.json - package.json - packages/ - happynrwl/ - src/ - executors/ - generators/ - happynrwl/ - preset/ <-- Here - index.ts - tools/ - tsconfig.base.json/ {% /filetree %} After the command is finished, the preset generator is created under the folder named **preset**. The **generator.ts** provides an entry point to the generator. This file contains a function that is called to perform manipulations on a tree that represents the file system. The **schema.json** provides a description of the generator, available options, validation information, and default values. Here is the sample generator function which you can customize to meet your needs. ```typescript // generator.ts export default async function (tree: Tree, options: PresetGeneratorSchema) { const normalizedOptions = normalizeOptions(tree, options); addProjectConfiguration(tree, normalizedOptions.projectName, { root: normalizedOptions.projectRoot, projectType: 'application', sourceRoot: `${normalizedOptions.projectRoot}/src`, targets: { exec: { executor: 'nx:run-commands', options: { command: `node ${projectRoot}/src/index.js`, }, }, }, tags: normalizedOptions.parsedTags, }); addFiles(tree, normalizedOptions); await formatFiles(tree); } ``` To get an in-depth guide on customizing/running or debugging your generator see [local generators](/docs/kb/local-generators). ## Usage Before you are able to use your newly created preset you must package and publish it to a registry. After you have published your plugin to a registry you can now use your preset when creating a new workspace ```shell npx create-nx-workspace my-workspace --preset=my-plugin-name ``` --- ## createServer - @nx/angular-rspack/ssr ```shell import { createServer } from '@nx/angular-rspack/ssr'; ``` The `createServer` function is used to setup Angular `CommonEngine` using an `express` server. It takes the bootstrap function as an argument, which is the function that bootstraps the Angular server application. This is usually `main.server.ts`. It returns `RsbuildAngularServer` which contains the server instance to allow further modifications as well as the listen method to start the server. ```ts function createServer( bootstrap: any, opts?: RspackAngularServerOptions ): RspackAngularServer; ``` --- ## Examples {% tabs %} {% tabitem label="Standard Express Server Usage" %} The following example shows how to create a standard express server: ```ts // myapp/src/server.ts import { createServer } from '@nx/angular-rspack/ssr'; import bootstrap from './main.server'; const server = createServer(bootstrap); /** Add your custom server logic here * * For example, you can add a custom static file server: * * server.app.use('/static', express.static(staticFolder)); * * Or add additional api routes: * * server.app.get('/api/hello', (req, res) => { * res.send('Hello World!'); * }); * * Or add additional middleware: * * server.app.use((req, res, next) => { * res.send('Hello World!'); * }); */ server.listen(); ``` {% /tabitem %} {% /tabs %} --- ## RspackAngularServer ```ts export interface RspackAngularServer { app: express.Express; listen: (port?: number) => void; } ``` --- ### `app` `express.Express` The express application instance. ### `listen` `(port?: number) => void` Starts the express application on the specified port. If no port is provided, the default port (4000) is used. --- ## RspackAngularServerOptions ```ts export interface RspackAngularServerOptions { serverDistFolder?: string; browserDistFolder?: string; indexHtml?: string; } ``` --- ### `serverDistFolder` `string` The folder where the server bundle is located. Defaults to the `dist/server` folder. ### `browserDistFolder` `string` The folder where the browser bundle is located. Defaults to the `dist/browser` folder. ### `indexHtml` `string` The path to the index.html file. Defaults to the `index.html` file in the `browserDistFolder`. --- ## Create a Sync Generator Sync generators are generators that are used to ensure that your file system is in the correct state before a task is run or the CI process is started. From a technical perspective, a sync generator is no different from any other generator, but it has some additional performance considerations and needs to be registered in a particular way. {% aside type="caution" title="Disable the Nx Daemon during development" %} When developing the Nx sync generator, disable the [Nx Daemon](/docs/reference/nx-daemon) by setting `NX_DAEMON=false`. The daemon caches your plugin code, so changes to your plugin won't be reflected until the daemon restarts. {% /aside %} ## Create a new sync generator You can create a new sync generator by hand or use the built-in generator that Nx provides via the `@nx/plugin` package. ### Step 1: Add @nx/plugin Make sure you have `@nx/plugin` installed or add it to your workspace: ```shell nx add @nx/plugin ``` ### Step 2: Create a local plugin Create a new local plugin where we can add our new sync generator. You can also add it to an existing local plugin if you already have one. In that case you can skip this step. ```shell nx g @nx/plugin:plugin tools/my-plugin ``` ### Step 3: Scaffold a new sync generator Create a sync generator the same way you would [create any generator](/docs/kb/local-generators). ```shell nx g @nx/plugin:generator --path=tools/my-plugin/src/generators/my-sync-generator ``` ## Implement a global sync generator Global sync generators are executed when the `nx sync` or `nx sync:check` command is explicitly run by a user or in a script. They are not associated with an individual task or project and typically update root-level configuration files. A sync generator should be able to run without any required options, so update the schema accordingly: ```jsonc // tools/my-plugin/src/generators/my-sync-generator/schema.json { "$schema": "https://json-schema.org/schema", "$id": "MySyncGenerator", "title": "", "type": "object", "properties": {}, "required": [], } ``` Also update the TypeScript interface to match: ```ts // tools/my-plugin/src/generators/my-sync-generator/schema.d.ts export interface MySyncGeneratorSchema {} ``` Sync generators can optionally return an `outOfSyncMessage` to display to users when the sync generator needs to be run. ```ts // tools/my-plugin/src/generators/my-sync-generator/my-sync-generator.ts import { Tree } from '@nx/devkit'; import type { SyncGeneratorResult } from 'nx/src/utils/sync-generators'; export async function mySyncGenerator( tree: Tree ): Promise { if ( !tree.exists('/legal-message.txt') || tree.read('/legal-message.txt').toString() !== 'This is an important legal message.' ) { tree.write('/legal-message.txt', 'This is an important legal message.'); } return { outOfSyncMessage: 'The legal-message.txt file needs to be created', }; } export default mySyncGenerator; ``` ### Register a global sync generator Global sync generators are registered in the `nx.json` file like this: ```jsonc // nx.json { "sync": { "globalGenerators": ["@myprg/my-plugin:my-sync-generator"], }, } ``` {% aside type="caution" title="Verify the name of your plugin" %} You might have to adjust the name of your plugin based on your specific workspace scope. Verify the name in `tools/my-plugin/package.json`. If your package.json has a different name, adjust the `nx.json` configuration accordingly. {% /aside %} Now `my-sync-generator` will be executed any time the `nx sync` command is run. ## Implement a task sync generator that uses the project graph Task sync generators are run before a particular task and are used to ensure that the files are in the correct state for the task to be run. The primary use case for this is to set up configuration files based on the project graph. To read from the project graph, use the [`createProjectGraphAsync`](/docs/reference/devkit/createProjectGraphAsync) from the `@nx/devkit` package. Create a generator in the same way as a global sync generator and then read the project graph like this: ```ts // tools/my-plugin/src/generators/my-sync-generator/my-sync-generator.ts import { Tree, createProjectGraphAsync, joinPathFragments } from '@nx/devkit'; import type { SyncGeneratorResult } from 'nx/src/utils/sync-generators'; export async function mySyncGenerator( tree: Tree ): Promise { const projectGraph = await createProjectGraphAsync(); Object.values(projectGraph.nodes).forEach((project) => { tree.write( joinPathFragments(project.data.root, 'license.txt'), `${project.name} uses the Acme Corp license.` ); }); return { outOfSyncMessage: 'Some projects are missing a license.txt file.', }; } export default mySyncGenerator; ``` ### Register a task sync generator To register a generator as a sync generator for a particular task, add the generator to the `syncGenerators` property of the task configuration. {% aside type="note" title="Important: Package.json Configuration" %} For projects using [inferred targets](/docs/concepts/mental-model#inferred-tasks) (no project.json file), the sync generators must be registered inside the `nx` property in package.json, not at the root level. {% /aside %} {% tabs %} {% tabitem label="package.json" %} ```jsonc // apps/my-app/package.json { "name": "my-app", ... "nx": { "targets": { "build": { "syncGenerators": ["my-plugin:my-sync-generator"] } } } } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // apps/my-app/project.json { "targets": { "build": { "syncGenerators": ["my-plugin:my-sync-generator"], }, }, } ``` {% /tabitem %} {% /tabs %} {% aside type="caution" title="Verify the name of your plugin" %} You might have to adjust the name of your plugin based on your specific workspace scope. Verify the name in `tools/my-plugin/package.json`. If the name there is `@myorg/my-plugin` you have to register it as: ```jsonc { "syncGenerators": ["@myorg/my-plugin:my-sync-generator"], } ``` {% /aside %} With this configuration in place, running `nx build my-app` will first run `my-sync-generator` and then run the `build` task. The `my-sync-generator` and any other task or global sync generators will be run when `nx sync` or `nx sync:check` is run. ## Performance and DX considerations Task sync generators will block the execution of the task while they are running and both global and task sync generators will block the CI pipeline until the `nx sync:check` command finishes. Because of this, make sure to keep in mind the following performance tips: - Make the generator idempotent. Running the generator multiple times in a row should have the same impact as running the generator a single time. - Only write to the file system when a file is actually changed. Avoid reformatting files that have not been actually modified. Nx will identify the workspace as out of sync if there's any file change after the sync generator is run. - Make sure to provide an informative `outOfSyncMessage` so that developers know what to do to unblock their tasks. Do whatever you can to make your sync generators as fast and user-friendly as possible, because users will be running them over and over again without even realizing it. --- ## CreateNodes API Compatibility This is a reference for knowing how Nx versions and the `createNodes`/`createNodesV2` APIs interact. If you plan on supporting multiple Nx versions with a custom plugin, then it's important to know which APIs to use. ## Which createNodes version does Nx call? The following table shows which export Nx will call based on the Nx version: | Nx Version | Calls `createNodes` | Calls `createNodesV2` | Nx Call Preference | | ------------- | ------------------- | --------------------- | ------------------------------------------------------------ | | 17.x - 19.1.x | Yes | No | Only v1 supported | | 19.2.x - 20.x | Yes (fallback) | Yes (preferred) | Prefers v2, falls back to v1 | | 21.x | No | Yes | Only v2 supported | | 22.x | Yes (v2 signature) | Yes | Both use v2 signature | | 23.x+ | Yes (v2 signature) | Yes (fallback) | Prefers `createNodes`; `createNodesV2` is a deprecated alias | ## Which Nx versions does my plugin support? > Note this is the same information as above, but presented as a lookup table for plugin authors. If you're a plugin author, this table shows which Nx versions your plugin will support based on which exports you provide: | Plugin Exports | Nx 17-19.1 | Nx 19.2-20 | Nx 21-21.x | Nx 22+ | | ----------------------------------------- | ---------------- | ---------------- | ---------------- | ---------------- | | Only `createNodes` (v1) | ✅ Supported | ✅ Supported | ❌ Not Supported | ❌ Not Supported | | Only `createNodesV2` | ❌ Not Supported | ✅ Supported | ✅ Supported | ✅ Supported | | Both `createNodes` (v1) & `createNodesV2` | ✅ Supported | ✅ Supported | ✅ Supported | ✅ Supported | | Both with v2 signature (Nx 22+) | ❌ Not Supported | ❌ Not Supported | ✅ Supported | ✅ Supported | ## Recommended implementation pattern ### Plugin Support for Nx 21 and later For plugins targeting **Nx 21 and later**, the recommended pattern is to export both `createNodes` and `createNodesV2` using the same v2 implementation: ```typescript // my-plugin/index.ts import { CreateNodesV2, CreateNodesContextV2, createNodesFromFiles, } from '@nx/devkit'; export interface MyPluginOptions { // your options } // Export createNodes with v2 signature export const createNodes: CreateNodesV2 = [ '**/some-config.json', async (configFiles, options, context) => { return await createNodesFromFiles( (configFile, options, context) => createNodesInternal(configFile, options, context), configFiles, options, context ); }, ]; // Re-export as createNodesV2 export const createNodesV2 = createNodes; async function createNodesInternal( configFilePath: string, options: MyPluginOptions, context: CreateNodesContextV2 ) { // Your plugin logic here return { projects: { // ... }, }; } ``` This pattern ensures your plugin works with both Nx 21 and Nx 22+. ### Plugin support for Nx 17 through Nx 20 If you need to support Nx versions 17-20, you'll need to provide separate implementations. In Nx 22 the type for v1 of the create nodes api are removed, you can inline the type to maintain type safety. ```typescript // my-plugin/index.ts import { CreateNodesV2, CreateNodesContextV2, CreateNodesResult, createNodesFromFiles, } from '@nx/devkit'; // inlined types for backwards compat to v1 of createNodes // removed in Nx 22 export interface OldCreateNodesContext extends CreateNodesContextV2 { /** * The subset of configuration files which match the createNodes pattern */ readonly configFiles: readonly string[]; } type OldCreateNodes = readonly [ projectFilePattern: string, createNodesFunction: OldCreateNodesFunction, ]; export type OldCreateNodesFunction = ( projectConfigurationFile: string, options: T | undefined, context: OldCreateNodesContext ) => CreateNodesResult | Promise; export interface MyPluginOptions { // your options } // V1 API for Nx 17-20 export const createNodes: OldCreateNodes = [ '**/my-config.json', (configFile, options, context: OldCreateNodesContext) => { // V1 implementation - processes one file at a time return createNodesInternal(configFile, options, context); }, ]; // V2 API for Nx 19.2+ export const createNodesV2: CreateNodesV2 = [ '**/my-config.json', async (configFiles, options, context: CreateNodesContextV2) => { return await createNodesFromFiles( (configFile, options, context) => createNodesInternal(configFile, options, context), configFiles, options, context ); }, ]; function createNodesInternal( configFilePath: string, options: MyPluginOptions, context: OldCreateNodesContext | CreateNodesContextV2 ) { // Shared logic that works with both APIs return { projects: { // ... }, }; } ``` ## Future deprecation timeline Nx is standardizing on the v2 API. Here's the planned timeline: - **Nx 22**: Both `createNodes` and `createNodesV2` can be exported with v2 signature. `createNodes` re-exported as `createNodesV2`. - **Nx 23**: The `createNodesV2` export will be marked as deprecated in TypeScript types. Use `createNodes` with v2 signature instead. ## Related documentation - [Multi-language support](/docs/features/multi-language-support) - How plugins create projects, tasks, and dependencies - [Add Language Support to Nx](/docs/kb/add-language-support) - Walkthrough for creating a complete plugin - [CreateNodesV2 API Reference](/docs/reference/devkit/CreateNodesV2) - Detailed API documentation --- ## Creating Files with a Generator Generators provide an API for managing files within your workspace. You can use generators to do things such as create, update, move, and delete files. Files with static or dynamic content can also be created. The generator below shows you how to generate a library, and then scaffold out additional files with the newly created library. First, you define a folder to store your static or dynamic templates used to generated files. This is commonly done in a `files` folder. ```text happynrwl/ ├── apps/ ├── libs/ │ └── my-plugin │ └── src │ └── generators │ └── my-generator/ │ ├── files │ │ └── NOTES.md │ ├── index.ts │ └── schema.json ├── nx.json ├── package.json └── tsconfig.base.json ``` The files can use EJS syntax to substitute variables and logic. See the [EJS Docs](https://ejs.co/) to see more information about how to write these template files. Example NOTES.md: ```markdown Hello, my name is <%= name %>! ``` Next, update the `index.ts` file for the generator, and generate the new files. ```typescript // index.ts import { Tree, formatFiles, installPackagesTask, generateFiles, joinPathFragments, readProjectConfiguration, } from '@nx/devkit'; import { libraryGenerator } from '@nx/js'; export default async function (tree: Tree, schema: any) { await libraryGenerator(tree, { name: schema.name, directory: `libs/${schema.name}`, }); const libraryRoot = readProjectConfiguration(tree, schema.name).root; generateFiles( tree, // the virtual file system joinPathFragments(__dirname, './files'), // path to the file templates libraryRoot, // destination path of the files schema // config object to replace variable in file templates ); await formatFiles(tree); return () => { installPackagesTask(tree); }; } ``` The exported function first creates the library, then creates the additional files in the new library's folder. Next, run the generator: {% aside type="caution" title="Always do a dry-run" %} Use the `-d` or `--dry-run` flag to see your changes without applying them. This will let you see what the command will do to your workspace. {% /aside %} ```shell nx generate my-generator mylib ``` The following information will be displayed. ```text {% title="nx generate my-generator mylib" %} CREATE libs/mylib/README.md CREATE libs/mylib/.babelrc CREATE libs/mylib/src/index.ts CREATE libs/mylib/src/lib/mylib.spec.ts CREATE libs/mylib/src/lib/mylib.ts CREATE libs/mylib/tsconfig.json CREATE libs/mylib/tsconfig.lib.json UPDATE tsconfig.base.json UPDATE nx.json CREATE libs/mylib/.eslintrc.json CREATE libs/mylib/jest.config.ts CREATE libs/mylib/tsconfig.spec.json UPDATE jest.config.ts CREATE libs/mylib/NOTES.md ``` `libs/mylib/NOTES.md` will contain the content with substituted variables: ```markdown Hello, my name is mylib! ``` ## Dynamic file names If you want the generated file or folder name to contain variable values, use `__variable__`. So `NOTES-for-__name__.md` would be resolved to `NOTES_for_mylib.md` in the above example. ## Overwrite mode By default, generators overwrite files when they already exist. You can customize this behaviour with an optional argument to `generateFiles` that can take one of three values: - `OverwriteStrategy.Overwrite` (default): all generated files are created and overwrite existing target files if any. - `OverwriteStrategy.KeepExisting`: generated files are created only when target file does not exist. Existing target files are kept as is. - `OverwriteStrategy.ThrowIfExisting`: if a target file already exists, an exception is thrown. Suitable when a pristine target environment is expected. ## EJS syntax quickstart The [EJS syntax](https://ejs.co/) can do much more than replace variable names with values. Here are some common techniques. 1. Pass a function into the template: ```typescript // template file This is my <%= uppercase(name) %> ``` ```typescript // typescript file function uppercase(val: string) { return val.toUpperCase(); } // later generateFiles(tree, join(__dirname, './files'), libraryRoot, { uppercase, name: schema.name, }); ``` 2. Use javascript for control flow in the template: ```typescript <% if(shortVersion) { %> This is the short version. <% } else { for(let x=0; x This text will be repeated <%= numRepetitions %> times. <% } // end for loop } // end else block %> ``` ```typescript // typescript file generateFiles(tree, join(__dirname, './files'), libraryRoot, { shortVersion: false, numRepetitions: 3, }); ``` --- ## How to configure Webpack and Vite for Storybook Storybook allows you to customize the `webpack` configuration and your `vite` configuration. For that, it offers two fields you can add in your `.storybook/main.js|ts` file, called `webpackFinal` and `viteFinal`. These fields are functions that take the default configuration as an argument, and return the modified configuration. You can read more about them in the [Storybook documentation for `webpack`](https://storybook.js.org/docs/react/builders/webpack#extending-storybooks-webpack-config) and the [Storybook documentation for `vite`](https://storybook.js.org/docs/react/builders/vite#configuration). You can use these fields in your Nx workspace Storybook configurations normally, following the Storybook docs. However, let's see how you can create a global configuration for every project in your workspace, and how you can override it for specific projects. ## Global configuration If you want to add a global configuration for Webpack or Vite in your workspace, you may create a `.storybook/main.js` file at the root of your workspace. In that root `.storybook/main.js|ts` file, you can add the `webpackFinal` or `viteFinal` field, and return the modified configuration. This will be applied to every project in your workspace. ### `webpack` and `webpackFinal` The `webpackFinal` field would look like this: ```ts // .storybook/main.ts webpackFinal: async (config, { configType }) => { // Make whatever fine-grained changes you need that should apply to all storybook configs // Return the altered config return config; }, ``` ### `vite` and `viteFinal` The `viteFinal` field would look like this: ```ts // .storybook/main.ts async viteFinal(config, { configType }) { if (configType === 'DEVELOPMENT') { // Your development configuration goes here } if (configType === 'PRODUCTION') { // Your production configuration goes here. } return mergeConfig(config, { // Your environment configuration here }); }, ``` In the `viteFinal` case, you would have to import the `mergeConfig` function from `vite`. So, on the top of your root `.storybook/main.js|ts` file, you would have to add: ```ts // .storybook/main.ts import { mergeConfig } from 'vite'; ``` ## Project-specific configuration ### `webpack` and `webpackFinal` You can customize the `webpack` configuration for a specific project by adding a `webpackFinal` field in your project-specific `.storybook/main.js|ts` file, like this: ```ts // apps/my-react-webpack-app/.storybook/main.ts import type { StorybookConfig } from '@storybook/react-webpack5'; const config: StorybookConfig = { stories: ..., addons: ..., framework: { name: '@storybook/react-webpack5', options: {}, }, webpackFinal: async (config, { configType }) => { // add your own webpack tweaks if needed return config; }, }; export default config; ``` If you are using a global, root-level, `webpack` configuration in your project, you can customize or extend that for a specific project by importing and extending the root configuration. ### `vite` and `viteFinal` You can customize the `vite` configuration for a specific project by adding a `viteFinal` field in your project-specific `.storybook/main.js|ts` file, following the same pattern as shown above for webpack configuration. --- ## Custom images for Nx Agents {% aside type="caution" title="Nx Enterprise" %} Using a custom base image for Nx Agents is a feature of Nx Enterprise plan for Nx Cloud. If you're interested in Nx Enterprise, we'd love to [chat with you about enterprise](https://nx.dev/enterprise) {% /aside %} Images need to have a base setup in order to run properly with Nx Agents. There are two ways to go about setting up your base image. It's recommended to extend our base image. If you cannot extend our image, manually configure your image with the required steps. {% tabs %} {% tabitem label="Extend image (recommended)" %} **Do not** override the `USER`, `WORKDIR` or `ENTRYPOINT` from the base image. ```yaml //nx-agent-base-image.dockerfile FROM us-east1-docker.pkg.dev/nxcloudoperations/nx-cloud-enterprise-public/nx-agents-base-images:ubuntu22.04-node24.14-v1 as base # Add any steps you need such as installing required software RUN sudo apt-get install.... RUN sudo chmod .... ``` {% /tabitem %} {% tabitem label="Manual setup" %} > It's required to use Ubuntu as the base ```yaml // nx-agent-base-image.dockerfile FROM ubuntu:22.04 as base # Create workflow user RUN groupadd -g 10001 workflows RUN useradd -m -u 10000 -g 10001 workflows RUN mkdir /home/workflows/workspace RUN mkdir -p /home/workflows/.npm-global/lib /home/workflows/.npm-global/bin RUN mkdir -p /home/workflows/.nvm RUN chown -R 10000:10001 /home/workflows ENV PATH=$PATH:/home/workflows/.npm-global/bin # Docker COPY --from=docker:24.0.2 /usr/local/bin /usr/local/bin COPY --from=docker:24.0.2 /usr/local/libexec /usr/local/libexec USER workflows WORKDIR /home/workflows/workspace ENTRYPOINT [ "/home/workflows/executor-binary/nx-cloud-workflow-executor" ] ``` {% /tabitem %} {% /tabs %} ## Checklist 1. Image is publicly accessible to the Nx Cloud instance being used. - If your image must remain behind a private registry, please contact your DPE to discuss potential solutions. 2. `USER`, `WORKDIR` and `ENTRYPOINT` are not overridden - If manually setting they are correctly configured as outlined in the _Manual_ step above. 3. Image is used in a [custom launch template](/docs/kb/launch-templates#launchtemplatestemplatenameimage) via its fully qualified path --- ## Creating Custom Steps While Nx Cloud provides several pre-built steps, you can create custom steps to suit your specific needs. Custom steps are defined by creating a new YAML file. ## Defining a custom step To create a custom step, follow these steps: 1\. **Create a New YAML File**: Create a new YAML file in your `.nx/workflows` directory (e.g., `custom-step.yaml`) {% aside type="note" title="Custom step file location" %} Custom steps do not need to be in the `.nx/workflows` directory. However, they must be reachable through one of the [step reference forms](#step-reference-forms). You have a few options for organizing your custom steps: - **Same Repository as your Nx Workspace**: Reference the step with a `file://` path, or with the GitHub shorthand if the repository is public on GitHub. - **Create a Dedicated Repository**: Create a separate repository to store a collection of custom steps. This is useful for sharing steps across multiple projects or teams, or if your main Nx workspace is not on GitHub or is private. {% /aside %} 2\. **Define the Step Template**: Below is an example of a custom step definition: ```yaml // .nx/workflows/custom-step.yaml name: 'Custom Step' description: 'This is a custom step that does XYZ.' definition: using: 'node' main: './scripts/custom-script.js' post: './scripts/post-custom-script.js' inputs: - name: 'input1' description: 'Description for input1' default: 'default_value' required: true - name: 'input2' description: 'Description for input2' required: false ``` ### Explanation of fields - **name**: - The name of the custom step. - **description**: - A description of what the custom step does. - **definition**: - **using**: The runtime environment. Supported values are `node` and `aggregate`. - **node**: If `using` is set to `node`, then only `main` and `post` can be used. - **aggregate**: If `using` is set to `aggregate`, then only `steps` can be used. - **main**: Path to the main script to run (only if `using` is `node`). - **post**: Path to the post script to run (only if `using` is `node`). - runs before the agent is cleaned up - **steps**: A list of sub-steps to be executed (only if `using` is `aggregate`). Steps follow the same definition as [launch templates](/docs/kb/launch-templates#launch-template-structure). - **inputs**: - **name**: The name of the input. - **description**: A description of the input. - **default**: Default value for the input. - **required**: Whether the input is required. ### Using inputs in scripts If your custom step has inputs, they can be accessed in scripts or JavaScript files using environment variables. Each input is prefixed with `NX_CLOUD_INPUT_`. For example, if you have an input called `input1`, you can access it in a JavaScript file like this: ```javascript const input1 = process.env.NX_CLOUD_INPUT_input1; console.log(`The value of input1 is: ${input1}`); ``` ### Using custom steps in launch templates Once you've defined your custom step, you can use it in your launch templates by referencing the custom step file. Here's an example: {% aside type="note" title="Custom Step location" %} When specifying the location for the custom step, you must include a branch or tag (e.g., `main`). Using a direct URL to the repository without a branch or tag will not work. {% /aside %} ```yaml // .nx/workflows/agents.yaml launch-templates: custom-template: resource-class: 'docker_linux_amd64/medium' image: 'ubuntu22.04-node24.14-v1' init-steps: - name: Custom Step uses: 'your-org/your-repo/main/.nx/workflows/custom-steps.yaml' env: CUSTOM_VAR: 'custom_value' inputs: input1: 'value1' input2: 'value2' ``` {% aside type="note" title="Recommendation on Using Inputs vs. Env" %} While you can use both `env` and `inputs` to pass values to custom steps, it is recommended to use `inputs` as they offer validation support, whereas `env` does not. {% /aside %} ### Step reference forms The `uses` field accepts three reference forms: the GitHub shorthand shown above, workspace-local `file://` paths, and full HTTPS URLs. The GitHub shorthand (`your-org/your-repo/main/path/to/step.yaml`) is fetched from `raw.githubusercontent.com` and requires a branch or tag. Vendoring steps into your repository or hosting them yourself means agent startup doesn't depend on that endpoint's availability, and works for workspaces not hosted on GitHub. #### Workspace-local files Reference a step file committed to your workspace with a `file://` URL. Note the triple slash. The path is resolved from the workspace root. ```yaml // .nx/workflows/agents.yaml launch-templates: custom-template: resource-class: 'docker_linux_amd64/medium' image: 'ubuntu22.04-node24.14-v1' init-steps: - uses: 'nrwl/nx-cloud-workflows/v6/workflow-steps/checkout/main.yaml' - name: Custom Step uses: 'file:///.nx/workflows/custom-step.yaml' ``` At validation time, Nx Cloud reads the file from your repository at the triggering commit. At runtime, the agent reads it from the checked-out workspace. The file only exists after the repository checkout, so the checkout step itself can't use a `file://` reference. Keep checkout as a remote step or an inline `script`. #### Full URLs Reference a step file on any HTTPS endpoint that returns the raw YAML body, for example a GitLab raw file URL or an internal file server. The URL is fetched as-is with an HTTP GET. ```yaml // .nx/workflows/agents.yaml launch-templates: custom-template: resource-class: 'docker_linux_amd64/medium' image: 'ubuntu22.04-node24.14-v1' init-steps: - name: Custom Step uses: 'https://your-host.example.com/steps/custom-step.yaml' ``` Whichever form you use, scripts referenced by a step file (such as `main` and `post`) resolve relative to the step file's own location: the same repository and ref for remote steps, workspace-relative for `file://` steps. ### Validating custom steps Just like launch templates, you should validate your custom steps to ensure they are defined correctly. In your CI pipeline, use the `nx-cloud validate` command with the `--step-file` flag to ensure nobody merges in invalid changes to your step: ```shell nx-cloud validate --workflow-file=./.nx/workflows/custom-steps.yaml --step-file ``` Ensure your custom steps are committed to your source control repository before running the validation. By defining and organizing custom steps in this way, you can create highly tailored workflows that meet the specific needs of your projects and CI/CD pipelines. --- ## Custom Workspace ESLint Rules Custom ESLint rules allow you to enforce team-specific conventions, project architectural patterns, and codebase-specific best practices that aren't covered by existing ESLint plugins. Nx provides two main approaches for creating and using custom ESLint rules in your workspace. {% youtube src="https://youtu.be/VHCCge9NZqs" title="Create custom workspace lint rules with your AI agent" /%} ## Understanding the two approaches There are two main approaches for custom ESLint rules in Nx workspaces: 1. **Package Manager Workspaces (npm/yarn/pnpm/bun)**: Create a dedicated ESLint plugin package (e.g., `nx g @nx/js:lib packages/eslint-rules`) that's symlinked via your package manager. Import rules as you would any npm package. 2. **`loadWorkspaceRules` Utility**: Use the `@nx/eslint-plugin` utility to load rules from any directory. This works whether or not you use package manager workspaces. Choose the approach that fits your workspace setup: | Scenario | Recommended Approach | | ------------------------------------ | -------------------------- | | Using npm/yarn/pnpm/bun workspaces | Package Manager Workspaces | | Not using package manager workspaces | `loadWorkspaceRules` | | Want rules as a publishable package | Package Manager Workspaces | | TypeScript rules with minimal config | `loadWorkspaceRules` | ## Approach 1: package manager workspaces If your workspace uses npm, yarn, pnpm, or bun workspaces, you can create custom ESLint rules as a regular package. This approach treats your custom rules like any other internal dependency. ### Step 1: create the ESLint plugin package Create a new library for your ESLint plugin: ```shell nx g @nx/js:lib packages/eslint-rules ``` {% aside type="note" %} If you don't use `nx g @nx/js:lib` to create your package, make sure your package is included in your workspace configuration (e.g., `packages/*` in `pnpm-workspace.yaml` or the `workspaces` field in `package.json`). {% /aside %} ### Step 2: Structure the Plugin Your plugin package should export rules following the ESLint plugin format: ```typescript // packages/eslint-rules/src/index.ts import { noFooConst, RULE_NAME as noFooConstName } from './rules/no-foo-const'; export default { rules: { [noFooConstName]: noFooConst, }, }; ``` ### Step 3: Create a Rule Each rule should follow the ESLint rule structure. Using `@typescript-eslint/utils` provides excellent TypeScript support: ```typescript // packages/eslint-rules/src/rules/no-foo-const.ts import { ESLintUtils } from '@typescript-eslint/utils'; export const RULE_NAME = 'no-foo-const'; export const noFooConst = ESLintUtils.RuleCreator(() => __filename)({ name: RULE_NAME, meta: { type: 'problem', docs: { description: 'Disallow variables named "foo"', }, schema: [], messages: { noFoo: 'Variables named "foo" are not allowed.', }, }, defaultOptions: [], create(context) { return { VariableDeclarator(node) { if (node.id.type === 'Identifier' && node.id.name === 'foo') { context.report({ node: node.id, messageId: 'noFoo', }); } }, }; }, }); ``` ### Step 4: install and use the plugin After creating your plugin package, install dependencies to ensure it's symlinked: ```shell npm install # or: yarn install # or: pnpm install # or: bun install ``` Then use the plugin in your ESLint configuration: {% tabs syncKey="eslint-config-preference" %} {% tabitem label="Flat Config" %} ```javascript // eslint.config.mjs import eslintRules from '@acme/eslint-rules'; export default [ { plugins: { '@acme/eslint-rules': eslintRules, }, rules: { '@acme/eslint-rules/no-foo-const': 'error', }, }, ]; ``` {% /tabitem %} {% tabitem label="Legacy (.eslintrc.json)" %} ```json { "plugins": ["@acme/eslint-rules"], "rules": { "@acme/eslint-rules/no-foo-const": "error" } } ``` {% /tabitem %} {% /tabs %} ### Running TypeScript rules When using TypeScript for your ESLint rules, the TypeScript code must be transpiled or interpreted before ESLint can use it. There are several options: Node.js 22.6+ supports TypeScript natively through type stripping. As of Node 22.18.0 and Node 24, this is enabled by default. For older versions in the 22.x series, enable it with: ```shell NODE_OPTIONS="--experimental-strip-types" nx lint myproject ``` Alternatively, if you do not want to use Node.js strip-types feature, the `tsx` package provides fast TypeScript execution with ESM support: ```shell npm install -D tsx ``` Then you can register `tsx` in your `eslint.config.mjs` file prior to importing the custom rules. ```js import { register } from 'tsx/esm/api'; const unregister = register(); const eslintRules = await import('@acme/eslint-rules'); export default [ { plugins: { '@acme/eslint-rules': eslintRules, }, rules: { '@acme/eslint-rules/no-foo-const': 'error', }, }, ]; // cleanup unregister(); ``` See the [ESM Register API](https://tsx.is/dev-api/register-esm) docs for `tsx` for more information. ## Approach 2: Using loadWorkspaceRules The `loadWorkspaceRules` utility from `@nx/eslint-plugin` lets you load ESLint rules from any directory in your workspace. This is particularly useful when: - You're not using package manager workspaces - You want rules in a non-standard location - You need automatic TypeScript transpilation ### Basic usage ```javascript // eslint.config.mjs import baseConfig from './eslint.base.config.mjs'; import { loadWorkspaceRules } from '@nx/eslint-plugin'; // Load rules from a directory relative to workspace root const customRules = await loadWorkspaceRules('tools/my-eslint-rules'); export default [ ...baseConfig, { files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], plugins: { custom: { rules: customRules }, }, rules: { 'custom/my-custom-rule': 'error', }, }, ]; ``` ### How `loadWorkspaceRules` works The utility: 1. Accepts a directory path (relative to workspace root or absolute) 2. Looks for an `index.ts`, `index.mts`, `index.cts`, `index.js`, `index.mjs`, or `index.cjs` file 3. Automatically finds and uses a `tsconfig.json` for TypeScript transpilation 4. Returns the exported rules object ### Specifying a custom `tsconfig` You can provide a specific `tsconfig.json` path: ```javascript const customRules = await loadWorkspaceRules( 'tools/my-eslint-rules', 'tools/my-eslint-rules/tsconfig.lib.json' ); ``` If not provided, `loadWorkspaceRules` searches for `tsconfig.json` starting from the rules directory and traversing up to the workspace root. ### Example: Project-specific rules You can load rules from within a project: ```javascript // apps/my-app/eslint.config.mjs import baseConfig from '../../eslint.base.config.mjs'; import { loadWorkspaceRules } from '@nx/eslint-plugin'; // Load rules specific to this project const projectRules = await loadWorkspaceRules('apps/my-app/eslint-rules'); export default [ ...baseConfig, { files: ['**/*.ts'], plugins: { project: { rules: projectRules }, }, rules: { 'project/component-naming': 'error', }, }, ]; ``` ## Related resources - [ESLint Developer Guide](https://eslint.org/docs/developer-guide/working-with-rules) - [TypeScript ESLint Custom Rules](https://typescript-eslint.io/developers/custom-rules) --- ## Cypress Component Testing > Component testing requires Cypress v10 and above. > See our [guide for more information](/docs/kb/cypress-v11-migration) to migrate to Cypress v10. Unlike [E2E testing](/docs/technologies/test-tools/cypress/introduction), component testing does not create a new project. Instead, Cypress component testing is added directly to a project, like [Jest](/docs/technologies/test-tools/jest/introduction) ## Add component testing to a project > Currently only [@nx/react](/docs/technologies/react/generators#cypress-component-configuration), [@nx/angular](/docs/technologies/angular/generators#cypress-component-configuration), and [@nx/next](/docs/technologies/react/next/generators#cypress-component-configuration) plugins support component testing Use the `cypress-component-configuration` generator from the respective plugin to add component testing to a project. ```shell nx g @nx/react:cypress-component-configuration --project=your-project nx g @nx/angular:cypress-component-configuration --project=your-project nx g @nx/next:cypress-component-configuration --project=your-project ``` You can optionally pass in `--generate-tests` to create component tests for all components within the library. Component testing supports both applications and libraries. By default, the generator attempts to find the build target for you based on the project's dependent apps. But you can manually specify the build target to use via the `--build-target` option. Note, in most cases, the build target will be from a different project than the one being configured. The only case where the build targets are from the same project is when the component tests are being added to an application. > Note: The [@nx/next:cypress-component-configuration generator](/docs/technologies/react/next/generators#cypress-component-configuration) doesn't require a build target ```shell nx g @nx/react:cypress-component-configuration --project=your-project --build-target=my-react-app:build nx g @nx/angular:cypress-component-configuration --project=your-project --build-target=my-ng-app:build ``` The build target option can be changed later via updating the `devServerTarget` option in the `component-test` target. {% aside type="caution" title="Executor Options" %} When using component testing make sure to set `skipServe: true` in the component test target options, otherwise `@nx/cypress` will attempt to run the build first which can slow down your component tests. `skipServe: true` is automatically set when using the `cypress-component-configuration` generator. {% /aside %} ## Configuration When using the `cypress-component-configuration` generator, a helper function is used in the `cypress.config.ts` to setup the ideal settings for your project. If you need to add additional configuration properties, you can spread the returned object from the helper function. ```ts // cypress.config.ts export default defineConfig({ component: { ...nxComponentTestingPreset(__filename), // add your own config here }, }); ``` ## Testing projects Run `nx component-test your-lib` to execute the component tests with Cypress. By default, Cypress will run in headless mode. You will have the result of all the tests and errors (if any) in your terminal. Screenshots and videos will be accessible in `dist/cypress/libs/your-lib/screenshots` and `dist/cypress/libs/your-lib/videos`. ## Watching for changes (Headed mode) With, `nx component-test your-lib --watch` Cypress will start in headed mode. Where you can see your component being tested. Running Cypress with `--watch` is a great way to iterate on your components since cypress will rerun your tests as you make those changes validating the new behavior. ## Splitting component testing tasks by file {% aside type="note" title="Available since Nx 21.6.1" %} Splitting component testing tasks by file is available since Nx 21.6.1. {% /aside %} Nx provides powerful features for [distributing tasks in CI](/docs/features/ci-features/distribute-task-execution), including [splitting tasks by file](/docs/features/ci-features/split-e2e-tasks) (also known as atomization). The `@nx/cypress` plugin facilitates this for Cypress projects, allowing you to run your tests more efficiently in your Continuous Integration (CI) environment. To enable component testing task splitting, set the `ciComponentTestingTargetName` option of the `@nx/cypress/plugin` in your `nx.json` file. It will look something like this: ```json {% meta="{10}" %} // nx.json { "plugins": [ { "plugin": "@nx/cypress/plugin", "options": { "targetName": "e2e", "ciTargetName": "e2e-ci", "componentTestingTargetName": "component-test", "ciComponentTestingTargetName": "component-test-ci", "openTargetName": "open-cypress" } } ] } ``` The plugin will infer the `component-test-ci` task, which depends on individual component testing tasks for each file. You can then replace the `component-test` task with the `component-test-ci` task in your CI configuration to run your tests in a distributed fashion: ```diff {% meta="lang='yaml'" %} // .github/workflows/ci.yaml - - run: pnpm exec nx affected -t lint test build component-test + - run: pnpm exec nx affected -t lint test build component-test-ci ``` ## More information You can read more on component testing in the [Cypress documentation](https://docs.cypress.io/guides/component-testing/writing-your-first-component-test). --- ## Using setupNodeEvents with Cypress preset The [`setupNodeEvents`](https://docs.cypress.io/guides/references/configuration#setupNodeEvents) function in a Cypress configuration file allows you to tap into the internal behavior of Cypress using the `on` and `config` arguments. ```ts // cypress.config.ts import { defineConfig } from 'cypress'; export default defineConfig({ e2e: { setupNodeEvents(on, config) { // e2e testing node events setup code }, }, }); ``` The Cypress preset that Nx provides (`@nx/cypress/plugins/cypress-preset`) uses `setupNodeEvents` to start the web server. Thus, if you provide your own function, then you must invoke the `setupNodeEvents` function that our preset provides. ```ts {% meta="{20}" %} // cypress.config.ts import { defineConfig } from 'cypress'; import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset'; const preset = nxE2EPreset(__filename, { cypressDir: 'src', bundler: 'vite', webServerCommands: { default: 'nx run my-project:serve', production: 'nx run my-project:preview', }, ciWebServerCommand: 'nx run my-project:serve-static', }); export default defineConfig({ e2e: { ...preset, async setupNodeEvents(on, config) { // This line sets up the web server as provided via `webServerCommands` and `ciWebServerCommand` await preset.setupNodeEvents(on, config); // Register your listeners here }, }, }); ``` {% aside type="note" title="Note on async-await" %} The `setupNodeEvents` function from our Cypress preset returns a promise, so make sure to await the result. {% /aside %} --- ## Migrating to Cypress V11 Cypress v10 introduce new features, like component testing, along with some breaking changes. Nx can help you migrate from v8 or v9 of Cypress to v10 and then to v11. Before continuing, make sure you have all your changes committed and have a clean working tree. You can migrate an E2E project to v11 by running the following command: ```shell nx g @nx/cypress:migrate-to-cypress-11 ``` In general, these are the steps taken to migrate your project: 1. Migrates your existing `cypress.json` configuration to a new `cypress.config.ts` configuration file. - The `pluginsFile` option has been replaced for `setupNodeEvents`. We will import the file and add it to the `setupNodeEvents` config option. Double-check your plugins are working correctly. 2. Rename all test files from `.spec.ts` to `.cy.ts` 3. Rename the `support/index.ts` to `support/e2e.ts` and update any associated imports 4. Rename the `integrations` folder to the `e2e` folder {% aside type="caution" title="Root cypress.json" %} Keeping a root `cypress.json` file, will cause issues with [Cypress trying to load the project](https://github.com/nrwl/nx/issues/11512). Instead, you can create a [root ts file and import it into each project's cypress config file](https://github.com/nrwl/nx/issues/11512#issuecomment-1213420638) to share values across projects. {% /aside %} We take the best effort to make this migration seamless, but there can be edge cases we didn't anticipate. So feel free to [open an issue](https://github.com/nrwl/nx/issues/new?assignees=&labels=type%3A+bug&template=1-bug.md) if you come across any problems. You can also consult the [official Cypress migration guide](https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-version-10-0) if you get stuck and want to manually migrate your projects. --- ## Define Secondary Entry Points for TypeScript Packages If you have a package where you want people to be able to access more than just the `main` file, you can define an `exports` property in the `package.json` file. Like this: ```json // packages/my-lib/package.json { "exports": { "./package.json": "./package.json", ".": "./src/index.js", "./foo": "./src/foo.js", "./bar": "./src/bar.js" } } ``` Then people can access code in your library through any of the provided entry points. ```ts // some-file.ts import myLib from 'my-lib'; import foo from 'my-lib/foo'; import bar from 'my-lib/bar'; ``` ## Setup package.json export fields with Nx Nx helps generate other properties in the `package.json` file, and you can also use Nx to maintain this property. If you're using the `@nx/js:tsc` executor, you can specify the `additionalEntryPoints` and `generateExportsField` options. Here's an example: {% tabs %} {% tabitem label="package.json" %} ```jsonc // packages/my-awesome-lib/package.json { "name": "my-awesome-lib", "nx": { "targets": { "build": { "executor": "@nx/js:tsc", "options": { "main": "packages/my-awesome-lib/src/index.ts", "additionalEntryPoints": [ "packages/my-awesome-lib/src/foo.ts", "packages/my-awesome-lib/src/bar.ts", ], "generateExportsField": true, }, }, }, }, } ``` {% /tabitem %} {% tabitem label="project.json" %} ```jsonc // packages/my-awesome-lib/project.json { "name": "my-awesome-lib", "targets": { "build": { "executor": "@nx/js:tsc", "options": { "main": "packages/my-awesome-lib/src/index.ts", "additionalEntryPoints": [ "packages/my-awesome-lib/src/foo.ts", "packages/my-awesome-lib/src/bar.ts", ], "generateExportsField": true, }, }, }, } ``` {% /tabitem %} {% /tabs %} When building the library, the `@nx/js:tsc` executor automatically adds the correct `exports` definition to the resulting `package.json`. ## Compile to multiple formats You can also compile to multiple formats, if you switch to using the `@nx/rollup:rollup` executor. Read all [the details here](/docs/kb/compile-multiple-formats). --- ## Defining a Task Pipeline Running a specific task like `build` in a monorepo usually involves running multiple commands. If you want to learn more about the concept of a task pipeline and its importance in a monorepo, have a look at [the What is a Task Pipeline page](/docs/concepts/task-pipeline-configuration). {% youtube src="https://youtu.be/_U4hu6SuBaY?si=rSclPBdRh7P_xZ_f" title="Define a task pipeline" /%} ## Define dependencies between tasks You can define dependencies among tasks by using the `dependsOn` property: ```json // nx.json { ... "targetDefaults": { "build": { "dependsOn": ["^build"] } } } ``` ## Per project vs global Task dependencies can be [defined globally](/docs/reference/nx-json#target-defaults) for all projects in `nx.json` file: ```json // nx.json { ... "targetDefaults": { "build": { "dependsOn": ["^build"] } } } ``` Or they can be [defined per-project](/docs/reference/project-configuration#dependson) in the `project.json` or `package.json` files. If for example you have a `prebuild` step for a given project, you can define that relationship as follows: {% tabs %} {% tabitem label="package.json" %} ```json // apps/myapp/package.json { "name": "myapp", "dependencies": {}, "devDependencies": {}, ... "nx": { "targets": { "build": { "dependsOn": [ "prebuild" ] } } } } ``` {% /tabitem %} {% tabitem label="project.json" %} ```json // apps/myreactapp/project.json { "name": "myreactapp", ... "targets": { "prebuild": { "command": "echo Prebuild" }, "build": { "command": "echo Build", "dependsOn": ["prebuild"] } } } ``` {% /tabitem %} {% /tabs %} ## Continuous task dependencies If a task has a dependency that never exits, then the task will never start. To support this scenario, you can mark the dependency as a [continuous task](/docs/reference/project-configuration#continuous). Labeling a task as continuous tells Nx to not wait for the process to exit, and it will be run alongside its dependents. ```json // apps/myapp/project.json { "targets": { "serve": { "continuous": true } } } ``` The `continuous` option is most useful for running development servers. For example, the `e2e` task depends on a continuous `serve` task that starts the server to be tested againts. ## Visualize task dependencies You can also visualize the actual task graph (alongside the projects) using [Nx graph](/docs/features/explore-graph). This can be useful for debugging purposes. To view the task graph in your browser, run: ```shell npx nx graph ``` And then select "Tasks" from the top-left dropdown, choose the target (e.g. `build`, `test`,..) and either show all tasks or select a specific project you're interested in. Here's an example of the `playwright` Nx plugin `build` target (in the [Nx repo](https://github.com/nrwl/nx)). ![Task graph of the Playwright Nx plugin in the nx repo being rendered in the browser](../../../assets/guides/running-tasks/task-graph-playwright-nx.webp) Alternatively you can use the [Nx Console](/docs/getting-started/editor-setup) extension in VSCode or IntelliJ, right-click on the project and select: ![Selecting "Focus task in Nx Graph" from the context menu in VS Code](../../../assets/guides/running-tasks/task-graph-context-menu.webp) It'll then visualize within the IDE: ![Task graph of the Playwright Nx plugin in the nx repo being rendered in VS Code](../../../assets/guides/running-tasks/task-graph-vscode.webp) --- ## Dependency Checks ESLint Rule The `@nx/dependency-checks` ESLint rule enables you to discover mismatches between dependencies specified in a project's `package.json` and the dependencies that your project depends on. If your project is using, for example, the `axios`, but the `package.json` does not specify it as a dependency, your library might not work correctly. This rule helps catch these problems before your users do. The rule uses the project graph to collect all the dependencies of your project, based on the input of your `build` target. It will filter out all the dependencies marked as `devDependencies` in your root `package.json` to ensure dependencies of your compilation pipelines (e.g. dependencies of `webpack.config` or `vite.config`) or test setups are not included in the expected list. We use the version numbers of the installed packages when checking whether the version specifier in `package.json` is correct. We do this because this is the only version for which we can "guarantee" that things work and were tested. If you specify a range outside of that version, that would mean that you are shipping potentially untested code. {% aside type="tip" title="Keep the Package Manager Lock File Up-to-Date" %} The `@nx/dependency-checks` rule requires the presence of an up-to-date lock file in the workspace root to detect installed packages and their versions correctly. If the `package.json` file has changes that are not reflected in the lock file, make sure to perform a package installation. {% /aside %} ## Usage Library generators from `@nx` packages will configure this rule automatically when you opt-in for bundler/build setup. This rule is intended for publishable/buildable libraries, so it will only run if a `build` target is detected in the configuration (this name can be modified - see [options](#options)). ### Manual setup To set it up manually for existing libraries, you need to add the `dependency-checks` rule to your project's ESLint configuration: {% tabs syncKey="eslint-config-preference" %} {% tabitem label="Flat Config" %} ```javascript // /eslint.config.mjs import nxPlugin from '@nx/eslint-plugin'; import jsoncParser from 'jsonc-eslint-parser'; export default [ ...nxPlugin.configs['flat/base'], ...nxPlugin.configs['flat/typescript'], ...nxPlugin.configs['flat/javascript'], { files: ['**/*.json'], languageOptions: { parser: jsoncParser, }, rules: { '@nx/dependency-checks': 'error', }, }, // ... more ESLint config here ]; ``` {% /tabitem %} {% tabitem label="Legacy (.eslintrc.json)" %} ```jsonc // /.eslintrc.json { // ... more ESLint config here "overrides": [ { "files": ["*.json"], "parser": "jsonc-eslint-parser", "rules": { "@nx/dependency-checks": "error", }, }, // ... more ESLint overrides here ], } ``` {% /tabitem %} {% /tabs %} Additionally, you need to adjust your `lintFilePatterns` to include the project's `package.json` file:: ```jsonc {% meta="{8}" %} // /project.json { // ... project.json config "targets": { // ... more targets "lint": { "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"], "options": { "lintFilePatterns": [ "libs/my-lib/**/*.{ts,tsx,js,jsx}", "libs/my-lib/package.json", // add this line ], }, }, }, } ``` ### Overriding defaults Sometimes we intentionally want to add or remove a dependency from our `package.json` despite what the rule suggests. We can use the rule's options to override default behavior: {% tabs syncKey="eslint-config-preference" %} {% tabitem label="Flat Config" %} ```javascript // eslint.config.mjs export default [ // ... other config { files: ['**/*.json'], rules: { '@nx/dependency-checks': [ 'error', { buildTargets: ['build', 'custom-build'], // add non standard build target names checkMissingDependencies: true, // toggle to disable checkObsoleteDependencies: true, // toggle to disable checkVersionMismatches: true, // toggle to disable ignoredDependencies: ['lodash'], // these libs will be omitted from checks ignoredFiles: ['webpack.config.js', 'eslint.config.mjs'], // list of files that should be skipped for check includeTransitiveDependencies: true, // collect dependencies transitively from children useLocalPathsForWorkspaceDependencies: true, // toggle to disable peerDepsVersionStrategy: 'installed', // or 'workspace' to use workspace:* for peer dependencies }, ], }, }, ]; ``` {% /tabitem %} {% tabitem label="Legacy (.eslintrc.json)" %} ```jsonc // .eslintrc.json { "@nx/dependency-checks": [ "error", { "buildTargets": ["build", "custom-build"], // add non standard build target names "checkMissingDependencies": true, // toggle to disable "checkObsoleteDependencies": true, // toggle to disable "checkVersionMismatches": true, // toggle to disable "ignoredDependencies": ["lodash"], // these libs will be omitted from checks "ignoredFiles": ["webpack.config.js", "eslint.config.mjs"], // list of files that should be skipped for check "includeTransitiveDependencies": true, // collect dependencies transitively from children "useLocalPathsForWorkspaceDependencies": true, // toggle to disable "peerDepsVersionStrategy": "installed", // or 'workspace' to use workspace:* for peer dependencies }, ], } ``` {% /tabitem %} {% /tabs %} ## Options | Property | Type | Default | Description | | ------------------------------------- | ---------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | buildTargets | _Array_ | _["build"]_ | List of build target names | | checkMissingDependencies | _boolean_ | _true_ | Disable to skip checking for missing dependencies | | checkObsoleteDependencies | _boolean_ | _true_ | Disable to skip checking for unused dependencies | | checkVersionMismatches | _boolean_ | _true_ | Disable to skip checking if version specifier matches installed version | | ignoredDependencies | _Array_ | _[]_ | List of dependencies to ignore for checks | | ignoredFiles | _Array_ | N/A | List of files to ignore when collecting dependencies. The default value will be set based on the selected executor during the generation. | | includeTransitiveDependencies | _boolean_ | _false_ | Enable to collect dependencies of children projects | | useLocalPathsForWorkspaceDependencies | _boolean_ | _false_ | Set workspace dependencies as relative file:// paths. Useful for monorepos that link via file:// in package.json files. | | peerDepsVersionStrategy | _"installed" \| "workspace"_ | _"installed"_ | Strategy for version specifiers in peer dependencies. Use `"installed"` to use the installed version, or `"workspace"` to use `workspace:*` protocol for monorepo packages. | | runtimeHelpers | _Array_ | _[]_ | List of helper packages used by the built output (e.g. `tslib` when using `tsc` and `importHelpers` is set to `true`). The rule already detects some of them in some scenarios, but this option can be used to detect them when it doesn't happen automatically. | --- ## Dependency Management Strategies When working with a monorepo, one of the key architectural decisions is how to manage dependencies across your projects. This document outlines two main strategies and helps you choose the right approach for your team. The core decision comes down to: 1. Independently maintained dependencies in individual projects 2. A "single version policy", where dependencies are defined once at the root for your entire monorepo **Nx fully supports both strategies - it's your choice and you can change your approach as your needs evolve, you are never locked in**. You can even mix these strategies, using a single version policy for most dependencies while allowing specific projects to maintain their own versions when necessary. Thanks to its smart dependency graph analysis, Nx can trace dependencies used by different projects and can therefore avoid unnecessary cache misses even when a root level lockfile changes, so that is not a concern. Let's examine the trade-offs of each approach, using JavaScript/TypeScript as our primary example (though these principles apply to other languages as well): ## Independently maintained dependencies In this model, each project maintains its own dependency definitions. For JavaScript/TypeScript projects, this means each project has its own `package.json` file specifying runtime dependencies, with development dependencies often still living at the root of the workspace (although they can also be specified at the project level). During builds, each project's bundler includes the necessary dependencies in its final artifact. Dependencies are typically managed using package manager workspaces (npm/yarn/pnpm/bun). While this approach offers flexibility, it can introduce complexity when sharing code between projects. For example, if `project1` and `project2` use different versions of React, what happens when they try and share components? This can lead to runtime issues that are difficult to detect during development and challenging to debug in production. A common pitfall occurs when developers have one version of a dependency in the root `node_modules` but a different version specified in their project's `package.json`. This can result in code that works locally but fails in production where the bundled version is used. **Pros:** - Teams can independently choose and upgrade their dependencies - More immediately clear what dependencies are intended for each project - Easier transition for teams new to monorepos - Modern tooling around e.g. module federation can help mitigate some of the challenges within applications **Cons:** - Complicates deployment when projects share runtime dependencies - Makes code sharing between projects more challenging - Can lead to hard-to-detect runtime conflicts - Increases maintenance and strategy overhead with multiple versions to track ## Single version policy This strategy centralizes all dependency definitions in the root `package.json` file, ensuring consistent versions across your codebase. While individual projects may still maintain their own `package.json` files for development purposes, the root configuration serves as the single source of truth for versions. For building and deployment, you'll need to ensure each project only includes its relevant dependencies. Nx helps manage this through its workspace dependency graph and the `@nx/dependency-checks` ESLint rule, which can automatically detect and fix dependency mismatches between project and root configurations. The main challenge with this approach is coordinating dependency updates across independent teams. When multiple teams work on different, or even the same, applications within the same repo, they need to align on dependency upgrades. While this requires more coordination, it often results in less total work - upgrading a dependency once across all projects is typically more efficient than managing multiple separate upgrades over time. {% aside type="tip" title="Dependency Catalog Support" %} When using pnpm, Yarn, or Bun, use [pnpm catalogs](https://pnpm.io/catalogs), [Yarn catalogs](https://yarnpkg.com/features/catalogs), or [Bun catalogs](https://bun.com/docs/install/catalogs) to maintain single version policy. Define versions in `pnpm-workspace.yaml`, `.yarnrc.yml`, or the root `package.json` `catalog` field respectively, and reference them as `"": "catalog:"` in project `package.json` files (Nx 22+ for pnpm, Nx 22.6+ for Yarn, and Nx 23.2+ for Bun). {% /aside %} **Pros:** - Ensures consistent dependency versions, preventing runtime conflicts - Simplifies code sharing between projects - Makes workspace-wide updates more manageable and easier to track **Cons:** - Requires coordination between teams for dependency updates - May slow down teams that need to move at different velocities - Needs stronger governance and communication processes For details on using Nx dependency graph in your deployment process, see our guide on [preparing applications for deployment via CI](/docs/guides/ci-deployment). --- ## Deploying Next.js Applications to Vercel Your Next.js application should already be ready for deployment to Vercel. ## Configure your Vercel project's settings appropriately ### New Vercel project 1. If you are "importing" your Nx workspace's repository for the first time, make sure you leave the **Root Directory** field **empty**. Do not set it to `./` or any subdirectory. Vercel will use the full repository root, which is required for Nx to work correctly. 2. Ensure the Next.js "Framework Preset" is selected 3. Expand the "Build and Output Settings" and toggle the override switch for the build command. For example, for an application named `tuskdesk` the value will look like this: ```shell npx nx build tuskdesk --prod ``` 4. Toggle the override switch for the output directory. Point it to the `.next` directory inside the built app: ```text {% frame="none" %} apps/tuskdesk/.next ``` Therefore, our full configuration (based on a repo called "nx-workspace" and a project called "tuskdesk") will look like this: ![New Vercel Project](../../../assets/guides/next/next-deploy-vercel-1.png) ### Existing Vercel project If you have an existing project on Vercel then the exact same guidance applies as for the section above, it's just that you will need to update the project's existing settings. When everything is updated appropriately, for our `tuskdesk` example we would see the following in our "General" settings UI: ![Existing Vercel Project](../../../assets/guides/next/next-deploy-vercel-2.png) ## Handling `NEXT_PUBLIC_` environment variables with Nx cache Next.js bakes `NEXT_PUBLIC_*` environment variables into the static bundle at **build time**. If Nx (or Nx Cloud) has a cached build from a previous run, for example from your local development environment, it will restore that cached output rather than running `next build` again. This means the cached bundle may contain development values for your `NEXT_PUBLIC_*` variables even when Vercel has the correct production values configured. The proper solution is to include your `NEXT_PUBLIC_*` environment variables in the `inputs` of your build target. This tells Nx to treat a change in those variable values as a cache miss, ensuring a fresh build is triggered whenever they differ. In your application's `project.json`, extend the build target inputs: ```json { "targets": { "build": { "inputs": [ "default", "^production", { "env": "NEXT_PUBLIC_API_URL" }, { "env": "NEXT_PUBLIC_SUPABASE_URL" } ] } } } ``` Add one `{ "env": "VARIABLE_NAME" }` entry for each `NEXT_PUBLIC_*` variable your application uses. When the value of any listed variable differs from the cached build, Nx will invalidate the cache and rebuild. {% aside type="note" title="Preserving plugin-inferred inputs" %} The `@nx/next` plugin automatically infers certain inputs for Next.js build targets. When you override the `inputs` array, make sure to include `"default"` and `"^production"` so those defaults are preserved. If you are unsure what inputs your build target currently has, run `nx show project ` to inspect the effective configuration. {% /aside %} If you need a quick workaround before configuring inputs, you can pass `--skip-nx-cache` to bypass the cache entirely: ```shell npx nx build tuskdesk --prod --skip-nx-cache ``` Note that this disables all Nx caching for that run, eliminating the performance benefit. The `inputs` approach above is preferred for ongoing deployments. See [Nx Inputs documentation](/docs/reference/inputs#environment-variables) for more details. ## Skipping build if the application is not affected One of the core features of Nx is to run code quality checks and builds only for projects that are affected by recent code changes. We can use [Vercel's ignored build step feature](https://vercel.com/docs/platform/projects#ignored-build-step) to only build our application if it is affected. To build only what's affected, use the `npx nx-ignore ` command under `Project Settings > Git` on Vercel. ![Ignore build step](../../../assets/guides/next/next-deploy-vercel-3-2.png) The `nx-ignore` command uses Nx to detect whether the current commit affects the specified app, and will skip the build if it is not affected. ## Next steps You can continue to set any additional environment variables that may be appropriate for your projects. --- ## Deploying Nuxt applications to Vercel ## Configure your Nuxt application appropriately for Vercel deployment In your application's `nuxt.config.ts` file, ensure that the `nitro` property is configured to use the `vercel` preset, and that the `output.dir` property is set to the appropriate directory. The directory that Vercel expects to find the application's build output (as explained in the [Vercel documentation](https://vercel.com/docs/build-output-api/v3)) in is `.vercel/output` at the root of the repository/workspace. So, if your application is located at `apps/my-app` then the `output.dir` property should be set to `../../.vercel/output`. For example, if your Nx workspace is structured like this: {%filetree %} - apps/ - my-app/ - nuxt.config.ts - src/ - tsconfig.app.json - tsconfig.json - tsconfig.spec.json - nx.json - package-lock.json - package.json - tsconfig.base.json {% /filetree %} Then your `nitro` configuration should look like this: ```ts {% meta="{2-6}" %} // apps/my-app/nuxt.config.ts nitro: { preset: 'vercel', output:{ dir: '../../.vercel/output' } }, ``` For Vercel deployment, it does not really matter what the `buildDir` property in your `nuxt.config.ts` is set to. So, your entire `nuxt.config.ts` file may look something like this: ```ts // apps/my-app/nuxt.config.ts import tsconfigPaths from 'vite-tsconfig-paths'; import { defineNuxtConfig } from 'nuxt/config'; export default defineNuxtConfig({ workspaceDir: '../../', srcDir: 'src', devtools: { enabled: true }, buildDir: '../../dist/apps/my-app/.nuxt', devServer: { host: 'localhost', port: 4200, }, typescript: { typeCheck: true, tsConfig: { extends: './tsconfig.app.json', }, }, imports: { autoImport: false, }, css: ['~/assets/css/styles.css'], vite: { plugins: [tsconfigPaths()], }, nitro: { preset: 'vercel', output: { dir: '../../.vercel/output', }, }, }); ``` ## Configure your Vercel project's settings appropriately ### New Vercel project 1. If you are "importing" your Nx workspace's repository for the first time, make sure you do _not_ choose a root directory as part of the repo selection process (therefore leaving it to be the root of the full repo/workspace) 2. Ensure the Nuxt.js "Framework Preset" is selected 3. Expand the "Build & Development Settings" and toggle the override switch for the build command. For example, for an application named `my-app` the value will look like this: ```shell npx nx build my-app --prod ``` ![New Vercel Project](../../../assets/guides/nuxt/nuxt-deploy-vercel-1.png) ### Existing Vercel project If you have an existing project on Vercel then the exact same guidance applies as for the section above, it's just that you will need to update the project's existing settings. When everything is updated appropriately, for our `my-app` example we would see the following in our "General" settings UI: ![Existing Vercel Project](../../../assets/guides/nuxt/nuxt-deploy-vercel-2.png) ## Skipping build if the application is not affected One of the core features of Nx is to run code quality checks and builds only for projects that are affected by recent code changes. We can use [Vercel's ignored build step feature](https://vercel.com/docs/platform/projects#ignored-build-step) to only build our application if it is affected. To build only what's affected, use the `npx nx-ignore ` command under `Project Settings > Git` on Vercel. ![Ignore build step](../../../assets/guides/nuxt/nuxt-deploy-vercel-3.png) The `nx-ignore` command uses Nx to detect whether the current commit affects the specified app, and will skip the build if it is not affected. ## Next steps Naturally, you can continue on and set any additional Environment Variables etc that may be appropriate for your projects, but we have now covered the key points needed to deploy Nuxt.js projects from Nx workspaces on Vercel! --- ## Pruning Projects for Deployment When deploying Node.js applications to containers, you typically need only production dependencies, not your entire workspace `node_modules`. Pruning generates a standalone `package.json`, a pruned lockfile, and copies any workspace libraries your app depends on. The result is everything you need to run `npm ci` inside a Docker image with only the packages your application uses. To bundle your app into a single file instead (no `node_modules` needed), see [Bundling projects for deployment](/docs/kb/bundling-node-projects). ## When to prune instead of bundle | Approach | Best for | Trade-off | | ------------------------------------------- | --------------------------------------- | --------------------------------------------- | | [Bundling](/docs/kb/bundling-node-projects) | Serverless functions, simple APIs | Single file output, no `node_modules` needed | | Pruning | Docker deployments, native dependencies | Keeps `node_modules` but only production deps | Use pruning when: - Your app has native dependencies (e.g. `bcrypt`, `sharp`) that can't be bundled - You want Docker layer caching, where dependency layers rebuild only when `package.json` changes - You consume workspace libraries as packages rather than bundling them {% aside type="tip" %} New Node applications include prune targets by default. Pass `--docker` to also generate an example Dockerfile: `nx g @nx/node:app --docker`. {% /aside %} ## How pruning works Pruning uses four Nx targets that run in sequence: 1. `build` compiles your application (esbuild, webpack, tsc, etc.). 1. `prune-lockfile` (`@nx/js:prune-lockfile`) reads your project `package.json`, generates a minimal `package.json`, and creates a pruned lockfile containing only production dependencies. 1. `copy-workspace-modules` (`@nx/js:copy-workspace-modules`) copies workspace libraries referenced via `workspace:*` into a `workspace_modules/` directory and rewrites their dependency references to `file:` paths. 1. `prune` (`nx:noop`) depends on both `prune-lockfile` and `copy-workspace-modules`, giving you a single command to run. After running `nx prune my-app`, the build output directory contains: ```text apps/my-app/dist/ ├── main.js # Compiled application ├── package.json # Pruned production dependencies ├── package-lock.json # Pruned lockfile (or yarn.lock / pnpm-lock.yaml) ├── pnpm-workspace.yaml # pnpm only, carries the install settings ├── patches/ # pnpm only, if the workspace uses `pnpm patch` ├── local_path_modules/ # pnpm only, non-workspace file:/link: dependencies └── workspace_modules/ # Only present if you have workspace deps └── @my-org/ └── my-lib/ ├── package.json └── ... ``` ## Set up prune targets Add the following targets to your project's `package.json` or `project.json`: {% tabs syncKey="config" %} {% tabitem label="package.json" %} ```json // apps/my-app/package.json { "name": "@my-org/my-app", "nx": { "targets": { "prune-lockfile": { "dependsOn": ["build"], "cache": true, "executor": "@nx/js:prune-lockfile", "outputs": [ "{workspaceRoot}/apps/my-app/dist/package.json", "{workspaceRoot}/apps/my-app/dist/package-lock.json" ], "options": { "buildTarget": "build" } }, "copy-workspace-modules": { "dependsOn": ["build"], "cache": true, "outputs": ["{workspaceRoot}/apps/my-app/dist/workspace_modules"], "executor": "@nx/js:copy-workspace-modules", "options": { "buildTarget": "build" } }, "prune": { "dependsOn": ["prune-lockfile", "copy-workspace-modules"], "executor": "nx:noop" } } } } ``` {% /tabitem %} {% tabitem label="project.json" %} ```json // apps/my-app/project.json { "name": "@my-org/my-app", "targets": { "prune-lockfile": { "dependsOn": ["build"], "cache": true, "executor": "@nx/js:prune-lockfile", "outputs": [ "{workspaceRoot}/apps/my-app/dist/package.json", "{workspaceRoot}/apps/my-app/dist/package-lock.json" ], "options": { "buildTarget": "build" } }, "copy-workspace-modules": { "dependsOn": ["build"], "cache": true, "outputs": ["{workspaceRoot}/apps/my-app/dist/workspace_modules"], "executor": "@nx/js:copy-workspace-modules", "options": { "buildTarget": "build" } }, "prune": { "dependsOn": ["prune-lockfile", "copy-workspace-modules"], "executor": "nx:noop" } } } ``` {% /tabitem %} {% /tabs %} Replace `package-lock.json` in the `outputs` array with `yarn.lock` or `pnpm-lock.yaml` to match your package manager. {% aside type="note" title="Available since Nx 23.2" %} Nx 23.2 and later emit `pnpm-workspace.yaml` and, when needed, `patches/` and `local_path_modules/`. On older versions, `prune-lockfile` emits only the pruned `package.json` and the pruned lockfile, so the pnpm-specific guidance below does not apply. {% /aside %} The emitted `pnpm-workspace.yaml` carries build-script approvals (`allowBuilds`), `supportedArchitectures`, and any `patchedDependencies`. On pnpm 10 and below it carries none of them, because the emitted `package.json` declares them instead, but the file still ships so that every run overwrites the last one. Copied `.patch` files keep their original subpath under `patches/`. Resolution-time config such as `pnpm.overrides` is dropped from the emitted manifest, because the pruned lockfile already bakes it into its resolutions. A re-resolve inside the deploy directory, such as an install without the lockfile or adding a dependency there, loses those pins. For pnpm, the complete `prune-lockfile` target declares the extra artifacts in `outputs` so a cached run restores them, and the workspace root settings files in `inputs` so revoking a build-script approval does not replay a cached output that still grants it. The approvals and `supportedArchitectures` live only in those two files, and nothing records them in the lockfile. The `runtime` input hashes the pnpm major, which decides whether the settings land in the emitted `pnpm-workspace.yaml` or the emitted `package.json` when the root manifest has no `packageManager` field: ```json // apps/my-app/project.json { "targets": { "prune-lockfile": { "dependsOn": ["build"], "cache": true, "executor": "@nx/js:prune-lockfile", "inputs": [ "default", "^default", "{workspaceRoot}/pnpm-workspace.yaml", "{workspaceRoot}/package.json", { "runtime": "node -e \"try{console.log('pnpm major '+require('child_process').execSync('pnpm --version',{stdio:['ignore','pipe','ignore']}).toString().trim().split('.')[0])}catch{console.log('pnpm major unavailable')}\"" } ], "outputs": [ "{workspaceRoot}/apps/my-app/dist/package.json", "{workspaceRoot}/apps/my-app/dist/pnpm-lock.yaml", "{workspaceRoot}/apps/my-app/dist/pnpm-workspace.yaml", "{workspaceRoot}/apps/my-app/dist/patches", "{workspaceRoot}/apps/my-app/dist/local_path_modules" ], "options": { "buildTarget": "build" } } } } ``` {% aside type="caution" title="Installing the output with --ignore-workspace" %} `pnpm install --ignore-workspace` skips a `pnpm-workspace.yaml` sitting beside the output, so the settings in it never apply. On pnpm 11 and above, a workspace using `pnpm patch` therefore gets an output that installs cleanly without the flag and fails with it, because the lockfile declares patches the install no longer sees: ```text ERR_PNPM_LOCKFILE_CONFIG_MISMATCH Cannot proceed with the frozen installation. The current "patchedDependencies" configuration doesn't match the value found in the lockfile ``` Install that output without the flag. The emitted file declares an empty `packages` list, which already stops pnpm from treating a parent directory as a workspace. A workspace with no approvals, architectures, or patches to carry gets a file holding only that empty list, so the flag has nothing to skip there. {% /aside %} Then run: ```shell nx prune my-app ``` Both `prune-lockfile` and `copy-workspace-modules` set `cache: true`, so subsequent runs are instant when nothing changes. ## Use pruned output in Docker The generated Dockerfile copies the build output and runs `npm install`: ```dockerfile # apps/my-app/Dockerfile FROM docker.io/node:lts-alpine ENV HOST=0.0.0.0 ENV PORT=3000 WORKDIR /app COPY dist . # You can remove this install step if you build with `--bundle` option. # The bundled output will include external dependencies. RUN npm --omit=dev -f install CMD ["node", "main.js"] ``` The `COPY dist .` line works because the Dockerfile lives inside the project directory (`apps/my-app/`), and the build output goes to `apps/my-app/dist/`. The pruned `package.json`, lockfile, and `workspace_modules/` are all inside `dist/`. Build and run: ```shell # Build the app and prune dependencies nx prune my-app # Build the Docker image npx nx docker:build my-app # Run the container nx docker:run my-app -p 3000:3000 ``` ## Legacy build targets that emit deploy output In a pnpm workspace, an explicit build target that enables `generatePackageJson` (webpack, rspack, vite, and esbuild) or `generateLockfile` (next, remix, tsc, and swc) emits the pruned deploy output next to the deployable `package.json`. The Nx 23.2 migration adds the pnpm settings inputs to the targets that enable one of those options at the time it runs. The migration doesn't revisit targets that enable either option later. Check the effective inputs of such a target and add any missing entries yourself. Add them to the layer that supplies the target's effective `inputs`. A target-level `inputs` array replaces the `targetDefaults` array instead of merging with it, so append to the target's own array when it declares one, and otherwise to the `targetDefaults` entry it inherits from. A target that inherits nothing needs the Nx defaults spelled out first: ```json // apps/my-app/project.json { "targets": { "build": { "executor": "@nx/webpack:webpack", "options": { "generatePackageJson": true }, "inputs": [ "default", "^default", "{workspaceRoot}/pnpm-workspace.yaml", { "json": "{workspaceRoot}/package.json", "fields": [ "packageManager", "pnpm.onlyBuiltDependencies", "pnpm.neverBuiltDependencies", "pnpm.allowBuilds", "pnpm.supportedArchitectures", "pnpm.patchedDependencies" ] }, { "runtime": "node -e \"try{console.log('pnpm major '+require('child_process').execSync('pnpm --version',{stdio:['ignore','pipe','ignore']}).toString().trim().split('.')[0])}catch{console.log('pnpm major unavailable')}\"" } ] } } } ``` The `json` input hashes only the fields the deploy output is built from, so dependency bumps in the root `package.json` don't invalidate every build. This doesn't apply to the `prune-lockfile` target above, or to build targets inferred by the `@nx/webpack` and `@nx/rspack` plugins, which declare these inputs themselves unless a project-level or `targetDefaults` entry replaces their `inputs` array. TS Solution Setup rejects `generatePackageJson`, so use the prune workflow instead of re-enabling it. ## Migrate from `generatePackageJson` If you're upgrading to Nx 20+ with TS Solution Setup (the default for new workspaces), the `generatePackageJson` option is no longer supported. You'll see this error: {% aside type="caution" title="Error: generatePackageJson not supported" %} `Setting 'generatePackageJson: true' is not supported with the current TypeScript setup. Update the 'package.json' file at the project root as needed and unset the 'generatePackageJson' option.` {% /aside %} Follow these steps to migrate to the prune workflow: ### Step 1: Move dependencies to your project package.json With TS Solution Setup, each project has its own `package.json`. List all runtime dependencies there: ```json // apps/my-app/package.json { "name": "@my-org/my-app", "dependencies": { "express": "^4.18.0", "@my-org/shared-utils": "workspace:*" } } ``` Use the `workspace:*` protocol for workspace libraries. ### Step 2: Remove `generatePackageJson` from your build configuration {% tabs syncKey="bundler" %} {% tabitem label="esbuild" %} Remove `generatePackageJson` from your esbuild target options: ```json // apps/my-app/package.json { "nx": { "targets": { "build": { "executor": "@nx/esbuild:esbuild", "options": { "platform": "node", "outputPath": "dist/apps/my-app", "format": ["cjs"], "main": "apps/my-app/src/main.ts", "tsConfig": "apps/my-app/tsconfig.app.json" } } } } } ``` {% /tabitem %} {% tabitem label="Webpack" %} Remove `generatePackageJson` from your webpack config: ```js // apps/my-app/webpack.config.js const { NxAppWebpackPlugin } = require('@nx/webpack/app-plugin'); const { join } = require('path'); module.exports = { output: { path: join(__dirname, '../../dist/apps/my-app'), }, plugins: [ new NxAppWebpackPlugin({ target: 'node', compiler: 'tsc', main: './src/main.ts', tsConfig: './tsconfig.app.json', }), ], }; ``` {% /tabitem %} {% tabitem label="Rollup / Vite" %} Remove `generatePackageJson` from your rollup or vite build options. With TS Solution Setup, the project `package.json` is used directly. {% /tabitem %} {% /tabs %} ### Step 3: Add prune targets Add the `prune-lockfile`, `copy-workspace-modules`, and `prune` targets to your project `package.json` as shown in the [set up prune targets](#set-up-prune-targets) section. ### Step 4: Update your Dockerfile Replace references to the old generated `package.json` with the pruned output. See the [use pruned output in Docker](#use-pruned-output-in-docker) section for a recommended Dockerfile structure. --- ## Use the Docker layer cache with Nx Agents The Docker layer cache stores BuildKit cache layers in a registry near your Nx Agents. A later build can pull unchanged layers from this registry instead of building them again. {% aside type="note" title="Requires a dedicated compute cluster" %} Docker layer caching is an Nx Cloud add-on for a [dedicated compute cluster](/docs/features/ci-features/dedicated-compute-cluster). Request the cluster, then request this add-on under [**Settings > Add-ons**](https://cloud.nx.app/go/organization/add-ons). {% /aside %} The layer cache is separate from the registry that stores your final images. Read and write cache layers through `NX_DOCKER_CACHE_REGISTRY`, then push final images to your company registry. ## Set up the Docker builder Layer caching uses the registry backend from Docker BuildKit. Add the Buildx setup step before your build tasks in the [Nx Agents launch template](/docs/kb/launch-templates): ```yaml # .nx/workflows/agents.yaml launch-templates: linux-medium: init-steps: - name: Set up Docker Buildx uses: 'nrwl/nx-cloud-workflows/main/workflow-steps/setup-docker-buildx/main.yaml' ``` This reusable step installs and configures Buildx as the default builder. If you use a different setup, [install Docker Buildx](https://github.com/docker/buildx#linux-packages) on each agent and configure a builder before the build starts. ## Add the cache flags Use `--cache-to` to export new layers and `--cache-from` to import existing layers: ```shell docker build \ --push \ --tag /: \ --cache-to type=registry,ref=${NX_DOCKER_CACHE_REGISTRY}/:main,mode=max \ --cache-from type=registry,ref=${NX_DOCKER_CACHE_REGISTRY}/:main \ --file \ . ``` The command uses these values: - `NX_DOCKER_CACHE_REGISTRY` contains the in-cluster registry address from Nx Cloud. - `` identifies the cache for one image, such as `backend` or `api`. - `mode=max` exports intermediate layers as well as the final layers. - `--push` sends the final image to ``, not to the cache registry. Use a different cache image name for each Docker image that your workspace builds. Your agent must authenticate with the company registry before it uses `--push`. {% aside type="caution" title="Use main for persistent cache layers" %} Nx Cloud keeps layers under the `main` tag. It periodically removes layers under other tags. Use `main` for the long-lived cache that every branch can read. {% /aside %} ## Add a branch cache A branch can read its own recent layers first and use `main` as a fallback: ```shell docker build \ --push \ --tag /: \ --cache-from type=registry,ref=${NX_DOCKER_CACHE_REGISTRY}/: \ --cache-from type=registry,ref=${NX_DOCKER_CACHE_REGISTRY}/:main \ --cache-to type=registry,ref=${NX_DOCKER_CACHE_REGISTRY}/:,mode=max \ . ``` Replace `` with a valid Docker tag derived from the branch name. Use `main` as the branch tag on the main branch so its layers remain available. Nx Cloud can remove other branch tags periodically. ## Test without publishing Omit `--push` while you test the build configuration. Docker still builds the image and reads or writes the layer cache, but it does not push the final image to your company registry. ## Verify cache use Run the same build after the first build warms the cache. The BuildKit output shows `[CACHED]` for reused layers. The performance gain depends on the changed Dockerfile steps and the transfer speed between the agent and registry. Cache import and export still require network and disk input/output. For more cache options, see the [Docker registry cache documentation](https://docs.docker.com/build/cache/backends/registry). --- ## Advanced Angular Micro Frontends with Dynamic Module Federation {% aside type="caution" title="Deprecated: Angular Module Federation" %} The `@nx/angular` `host` and `remote` generators and Module Federation executors used on this page are deprecated in Nx v23 and will be removed in v24. Existing workspaces keep working in the meantime. For the supported Angular micro frontend path, see [Micro Frontends with Angular](/docs/kb/angular-micro-frontends). {% /aside %} Dynamic Module Federation is a technique that allows an application to determine the location of its remote applications at runtime. It helps to achieve the use case of **"Build once, deploy everywhere"**. "Build once, deploy everywhere" is the concept of being able to create a single build artifact of your application and deploy it to multiple environments such as staging and production. The difficulty in achieving this with a Micro Frontend Architecture using Static Module Federation is that our Remote applications will have a different location (or URL) in each environment. Previously, to account for this, we would have had to specify the deployed location of the Remote applications and rebuild the application for the target environment. Walk through how the concept of "Build once, deploy everywhere" can be easily achieved in a Micro Frontend Architecture that uses Dynamic Module Federation. {% aside type="tip" title="Faster builds with Rspack" %} The examples below use webpack. The `@nx/angular:host` and `@nx/angular:remote` generators also accept `--bundler=rspack` to use [Angular Rspack](/docs/technologies/angular/angular-rspack/introduction) for faster, webpack-compatible builds. {% /aside %} ## Aim The aim of this guide is three-fold. We want to be able to: - Set up a Micro Frontend with Static Module Federation - Transform an existing Static Module Federation setup to use Dynamic Federation - Generate a new Micro Frontend application that uses Dynamic Federation ## What we'll build To achieve the aims, we will do the following: - Create a Static Federation Micro Frontend Architecture - Change the **Dashboard** application to use Dynamic Federation - Generate a new **Employee Dashboard** application that will use Dynamic Federation - It should use the existing **Login** application. - It should use a new **Todo** application. ## Final code Here's the source code of the final result for this guide. {% github_repository url="https://github.com/Coly010/nx-ng-dyn-fed" /%} ## First steps ### Create an Nx workspace To start with, we need to create a new Nx Workspace and add the Nx Angular plugin. We can do this easily with: {% tabs %} {% tabitem label="npm" %} ```text {% title="npx create-nx-workspace@latest ng-mf --preset=apps" frame="terminal" %} NX Let's create a new workspace [https://nx.dev/getting-started/intro] ✔ Which CI provider would you like to use? · skip ✔ Would you like remote caching to make your build faster? · skip ``` Next run: ```shell cd ng-mf npx nx add @nx/angular ``` {% /tabitem %} {% tabitem label="yarn" %} ```text {% title="yarn create nx-workspace ng-mf --preset=apps" frame="terminal" %} NX Let's create a new workspace [https://nx.dev/getting-started/intro] ✔ Which CI provider would you like to use? · skip ✔ Would you like remote caching to make your build faster? · skip ``` Next run: ```shell cd ng-mf yarn nx add @nx/angular ``` {% /tabitem %} {% tabitem label="pnpm" %} ```text {% title="pnpx create-nx-workspace@latest ng-mf --preset=apps" frame="terminal" %} NX Let's create a new workspace [https://nx.dev/getting-started/intro] ✔ Which CI provider would you like to use? · skip ✔ Would you like remote caching to make your build faster? · skip ``` Next run: ```shell cd ng-mf pnpx nx add @nx/angular ``` {% /tabitem %} {% /tabs %} ### Creating our applications We need to generate two applications that support Module Federation. We'll start with the **Admin Dashboard** application which will act as a host application for the Micro-Frontends (_MFEs_): ```shell nx g @nx/angular:host apps/dashboard --prefix=ng-mf ``` {% aside type="note" title="Running nx commands" %} The terminal examples will show `nx` being run as if it is installed globally. If you have not installed Nx globally (not required), you can use your package manager to run the `nx` local binary: - NPM: `npx nx ...` - Yarn: `yarn nx ...` - PNPM: `pnpm nx ...` {% /aside %} The `host` generator will create and modify the files needed to set up the Angular application. Now, let's generate the **Login** application as a remote application that will be consumed by the **Dashboard** host application. ```shell nx g @nx/angular:remote apps/login --prefix=ng-mf --host=dashboard ``` Note how we provided the `--host=dashboard` option. This tells the generator that this remote application will be consumed by the **Dashboard** application. The generator performed the following changes to automatically link these two applications together: - Added the remote to the `apps/dashboard/module-federation.config.ts` file - Added a TypeScript path mapping to the root tsconfig file - Added a new route to the `apps/dashboard/src/app/app.routes.ts` file ## What was generated? Let's take a closer look after generating each application. For both applications, the generators did the following: - Created the standard Angular application files - Added a `module-federation.config.ts` file - Added a `webpack.config.ts` and `webpack.prod.config.ts` - Added a `src/bootstrap.ts` file - Moved the code that is normally in `src/main.ts` to `src/bootstrap.ts` - Changed `src/main.ts` to dynamically import `src/bootstrap.ts` _(this is required for the Module Federation to load versions of shared libraries correctly)_ - Updated the `build` target in the `project.json` to use the `@nx/angular:webpack-browser` executor _(this is required to support passing a custom Webpack configuration to the Angular compiler)_ - Updated the `serve` target to use `@nx/angular:dev-server` _(this is required as we first need Webpack to build the application with our custom Webpack configuration)_ The key differences reside within the configuration of the Module Federation Plugin within each application's `module-federation.config.ts`. We can see the following in the **Login** micro frontend configuration: ```ts // apps/login/module-federation.config.ts import { ModuleFederationConfig } from '@nx/module-federation'; const config: ModuleFederationConfig = { name: 'login', exposes: { './Routes': 'apps/login/src/app/remote-entry/entry.routes.ts', }, }; export default config; ``` Taking a look at each property of the configuration in turn: - `name` is the name that Webpack assigns to the remote application. It **must** match the name of the project. - `exposes` is the list of source files that the remote application exposes to consuming shell applications for their own use. This config is then used in the `webpack.config.ts` file: ```ts // apps/login/webpack.config.ts import { withModuleFederation } from '@nx/module-federation/angular'; import config from './module-federation.config'; export default withModuleFederation(config, { dts: false }); ``` We can see the following in the **Dashboard** micro frontend configuration: ```ts // apps/dashboard/module-federation.config.ts import { ModuleFederationConfig } from '@nx/module-federation'; const config: ModuleFederationConfig = { name: 'dashboard', remotes: ['login'], }; export default config; ``` The key difference to note with the **Dashboard** configuration is the `remotes` array. This is where you list the remote applications you want to consume in your host application. You give it a name that you can reference in your code, in this case `login`. Nx will find where it is served. Now that we have our applications generated, let's move on to building out some functionality for each. ## Adding functionality We'll start by building the **Login** application, which will consist of a login form and some very basic and insecure authorization logic. ### User library Let's create a user data-access library that will be shared between the host application and the remote application. This will be used to determine if there is an authenticated user as well as providing logic for authenticating the user. ```shell nx g @nx/angular:lib libs/shared/data-access-user ``` This will scaffold a new library for us to use. We need an Angular Service that we will use to hold state: ```shell nx g @nx/angular:service user --project=data-access-user ``` This will create the `libs/shared/data-access-user/src/lib/user-auth.ts` file. Change its contents to match: ```ts // libs/shared/data-access-user/src/lib/user-auth.ts import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class UserAuth { private isUserLoggedIn = new BehaviorSubject(false); isUserLoggedIn$ = this.isUserLoggedIn.asObservable(); checkCredentials(username: string, password: string) { if (username === 'demo' && password === 'demo') { this.isUserLoggedIn.next(true); } } logout() { this.isUserLoggedIn.next(false); } } ``` Now, export the service in the library's entry point file: ```ts // libs/shared/data-access-user/src/index.ts ... export * from './lib/user.service'; ``` ### Login application Let's set up our `entry.ts` file in the **Login** application so that it renders a login form. We'll import `FormsModule` and inject our `UserService` to allow us to sign the user in: ```ts // apps/login/src/app/remote-entry/entry.ts import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { UserService } from '@ng-mf/data-access-user'; import { inject } from '@angular/core'; @Component({ standalone: true, imports: [CommonModule, FormsModule], selector: 'ng-mf-login-entry', template: ` `, styles: [ ` .login-app { width: 30vw; border: 2px dashed black; padding: 8px; margin: 0 auto; } .login-form { display: flex; align-items: center; flex-direction: column; margin: 0 auto; padding: 8px; } label { display: block; } `, ], }) export class RemoteEntry { private userService = inject(UserService); username = ''; password = ''; isLoggedIn$ = this.userService.isUserLoggedIn$; login() { this.userService.checkCredentials(this.username, this.password); } } ``` {% aside type="note" title="More details" %} This could be improved with things like error handling, but for the purposes of this tutorial, we'll keep it simple. {% /aside %} Now let's serve the application and view it in a browser to check that the form renders correctly. ```shell nx run login:serve ``` We can see if we navigate a browser to `http://localhost:4201` that we see the login form rendered. If we type in the correct username and password _(demo, demo)_, then we can also see the user gets authenticated! Perfect! Our **Login** application is complete. ### Dashboard application Now let's update our **Dashboard** application. We'll hide some content if the user is not authenticated, and present them with the **Login** application where they can log in. For this to work, the state within `UserService` must be shared across both applications. Usually, with Module Federation in Webpack, you have to specify the packages to share between all the applications in your Micro Frontend solution. However, by taking advantage of Nx project graph, Nx will automatically find and share the dependencies of your applications. {% aside type="note" title="Single version policy" %} This helps to enforce a single version policy and reduces the risk of [Micro Frontend Anarchy](https://www.thoughtworks.com/radar/techniques/micro-frontend-anarchy). {% /aside %} Start by deleting the `app.html`, `app.css`, and `nx-welcome.ts` files from the **Dashboard** application. They will not be needed for this tutorial. Next, let's add our logic to the `app.ts` file. Change it to match the following: ```ts // apps/dashboard/src/app/app.ts import { CommonModule } from '@angular/common'; import { Component, inject, OnInit } from '@angular/core'; import { Router, RouterModule } from '@angular/router'; import { UserService } from '@ng-mf/data-access-user'; import { distinctUntilChanged } from 'rxjs/operators'; @Component({ standalone: true, imports: [CommonModule, RouterModule], selector: 'ng-mf-root', template: `
Admin Dashboard
You are authenticated so you can see this content.
`, }) export class App implements OnInit { private router = inject(Router); private userService = inject(UserService); isLoggedIn$ = this.userService.isUserLoggedIn$; ngOnInit() { this.isLoggedIn$ .pipe(distinctUntilChanged()) .subscribe(async (loggedIn) => { // Queue the navigation after initialNavigation blocking is completed setTimeout(() => { if (!loggedIn) { this.router.navigateByUrl('login'); } else { this.router.navigateByUrl(''); } }); }); } } ``` Finally, make sure the application routes are correctly set up: ```ts // apps/dashboard/src/app/app.routes.ts import { Route } from '@angular/router'; import { App } from './app'; export const appRoutes: Route[] = [ { path: 'login', loadChildren: () => import('login/Routes').then((m) => m.remoteRoutes), }, { path: '', component: App, }, ]; ``` We can now run both the **Dashboard** and **Login** applications: ```shell nx serve dashboard --devRemotes=login ``` Navigating to `http://localhost:4200` should show the **Dashboard** application with the **Login** application embedded within it. If you log in, you should see the content change to show that you are authenticated. This concludes the setup required for a Micro Frontend approach using Static Module Federation. {% aside type="caution" title="Do not fret!" %} When serving module federation apps locally in dev mode, there'll be an error output to the console: `import.meta cannot be used outside of a module`. You'll see the error originates from the `styles.js` script. It's a known error output, and as far as our testing has shown, it doesn't cause any breakages. It happens because the Angular compiler attaches the `styles.js` file to the `index.html` in a `