Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/nrwl/nx/llms.txt

Use this file to discover all available pages before exploring further.

Starting in Nx version 18, plugins can infer tasks for your projects automatically by analyzing existing tool configuration files. Instead of duplicating build, test, and lint configuration in project.json, Nx reads it from the source of truth — the tool’s own config file. This feature is also known as Project Crystal.

How task inference works

Every plugin implements its own inference logic, but all plugins follow the same two-step process:
1

Detect tool configuration in the workspace

The plugin scans the workspace for files associated with a specific tool. For example, @nx/vite/plugin searches for vite.config.ts (or .js, .mts) files. For each config file found, the plugin creates task configuration for the project that owns that file.
2

Create inferred tasks

The plugin generates one or more named targets based on the tool’s configuration. Target names are determined by the plugin’s options in nx.json. For example, @nx/vite/plugin creates build, serve, preview, and test targets by default, with cache settings derived from the values in vite.config.ts.

What gets inferred

When a plugin infers a task, it configures:
PropertyDescription
CommandHow the tool is invoked (e.g., vite build, jest)
CacheWhether results are cached by Nx
InputsWhich files and env vars factor into the cache key
OutputsWhich files are stored in and restored from the cache
Task dependenciesWhich other tasks must complete first (e.g., dependsOn: ["^build"])

Example: @nx/vite infers tasks from vite.config.ts

Given a project with a vite.config.ts file and the @nx/vite/plugin registered in nx.json:
nx.json
{
  "plugins": [
    {
      "plugin": "@nx/vite/plugin",
      "options": {
        "buildTargetName": "build",
        "serveTargetName": "serve",
        "previewTargetName": "preview",
        "testTargetName": "test"
      }
    }
  ]
}
Nx automatically infers the following targets for that project — no project.json target entries required:
{
  "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"
  },
  "serve": {
    "options": { "cwd": "apps/myreactapp", "command": "vite serve", "continuous": true },
    "executor": "nx:run-commands"
  },
  "preview": {
    "options": { "cwd": "apps/myreactapp", "command": "vite preview" },
    "executor": "nx:run-commands"
  },
  "test": {
    "options": { "cwd": "apps/myreactapp", "command": "vitest run" },
    "cache": true,
    "inputs": ["default", "^production", { "externalDependencies": ["vitest"] }],
    "outputs": ["{workspaceRoot}/coverage/apps/myreactapp"],
    "executor": "nx:run-commands"
  }
}

Viewing inferred tasks

To see the full resolved task configuration for a project — including everything inferred by plugins — run:
nx show project myapp --web
This opens a browser view showing all targets, their options, inputs, outputs, and which plugin or config file is responsible for each setting (the source map). You can also list all projects in the workspace:
nx show projects

Plugin order and conflict resolution

Plugins are processed in the order they appear in the plugins array in nx.json. If two plugins try to create a target with the same name for the same project, the plugin listed last wins. For example, if a project has both a vite.config.js and a webpack.config.js, both @nx/vite and @nx/webpack will attempt to create a build target. The one that appears last in plugins takes precedence.

Scoping plugins to specific projects

Use include and exclude glob patterns to control which projects a plugin processes:
nx.json
{
  "plugins": [
    {
      "plugin": "@nx/jest/plugin",
      "include": ["packages/**/*"],
      "exclude": ["**/*-e2e/**/*"]
    }
  ]
}
The glob patterns filter which configuration files the plugin processes — not project names directly. In the example above, @nx/jest/plugin only infers tasks for projects whose jest.config.ts path matches packages/**/* but not **/*-e2e/**/*. This is also useful for registering the same plugin twice with different options for different sets of projects.

Overriding inferred task configuration

Inferred configuration is a starting point, not a hard constraint. You can override it at two levels:
nx.json
{
  "targetDefaults": {
    "build": {
      "inputs": ["{projectRoot}/**/*", "!{projectRoot}/**/*.md"]
    }
  }
}
targetDefaults applies to all projects and overrides inferred configuration for matching target names.
apps/myapp/project.json
{
  "name": "myapp",
  "targets": {
    "build": {
      "options": {
        "sourcemap": true
      }
    }
  }
}
Project-level configuration is merged with (and takes priority over) inferred configuration.

Configuration precedence

From lowest to highest priority:
  1. Inferred task configuration from plugins (registered in nx.json)
  2. targetDefaults in nx.json
  3. Project-level configuration in project.json or package.json

Inferred tasks in existing workspaces

If you upgrade an existing Nx workspace to version 18+, the migration sets useInferencePlugins: false in nx.json automatically. This preserves the existing behavior — newly generated projects continue to use explicit executor-based targets, and nx add @nx/some-plugin does not register the plugin for inference. To adopt inferred tasks in an existing repo, follow the migrating to inferred tasks guide.
Even after fully adopting inferred tasks, project.json remains useful. It is the right place to override inferred options, declare tasks that cannot be inferred from a config file, and define targets that require executor features not available from a plain CLI command.