Skip to content

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 boundaries, and this behavior is configurable via the version.updateDependents option.

  • "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.

// Before
{
"name": "project-a",
"version": "1.0.0",
"dependencies": {
"project-b": "2.0.0"
}
}
// 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
}
}

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:

Terminal window
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:

// 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:

{
"release": {
"version": {
"updateDependents": "always",
"applyPreidToDependents": true
}
}
}

With this option enabled and --preid rc set, project-a receives a prepatch bump using the same preid:

// project-a after (applyPreidToDependents: true)
{
"name": "project-a",
"version": "1.0.1-rc.0",
"dependencies": {
"project-b": "2.0.1-rc.0"
}
}

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.<group>.version.applyPreidToDependents to scope the behavior to only certain groups.