Understand ~ and ^ in package.json

Compare npm tilde and caret ranges, including 0.x and prerelease rules, and use a lockfile to record the dependency tree you tested.

Best practices
Illustration of cooperating application modules

~1.2.3 allows patch releases starting at 1.2.3. ^1.2.3 also allows later minor releases within major version 1. Both express acceptable versions; neither guarantees that an upstream release will work with your application.

Compare the ranges

For ordinary stable versions:

Declaration Accepted stable versions
~1.2.3 >=1.2.3 <1.3.0
^1.2.3 >=1.2.3 <2.0.0
1.2.3 Exactly 1.2.3
* or x Any stable version
~1.2 >=1.2.0 <1.3.0
^1 >=1.0.0 <2.0.0

The shorthand “tilde fixes the minor version; caret fixes the major version” works for the first two rows, but breaks down for zero-major versions and incomplete version numbers. For example, ~1 allows stable releases throughout major version 1.

Ranges normally exclude prereleases such as 1.3.0-beta.1. Explicit prerelease comparators have additional matching rules; do not assume a broad-looking range automatically includes every beta release.

Account for version zero

Caret ranges preserve the leftmost nonzero component of a complete version:

  • ^0.2.3 accepts stable versions from 0.2.3 up to, but excluding, 0.3.0.
  • ^0.0.3 accepts only stable version 0.0.3.
  • ~0.2.3 accepts the same stable interval as ^0.2.3.

Missing components change the calculation: ^0.0 accepts stable versions below 0.1.0, rather than pinning one patch. Review the actual range instead of applying the complete-version rule mechanically.

Pair the range with a lockfile

In an application, commit package-lock.json and test dependency updates. A compatible lockfile records exact resolutions, while the manifest declares the range used when resolution is needed, including deliberate updates. Adding one dependency does not imply that npm upgrades every other dependency.

Use npm ci for a clean installation based on the lockfile. It fails if the manifest and lockfile disagree, removes an existing node_modules, and does not rewrite the lockfile to resolve that disagreement. Use the same relevant installation flags that produced the lockfile. A lockfile improves dependency reproducibility; operating systems, native modules, lifecycle scripts, and external services can still affect a build.

For a library, choose dependency and peer-dependency ranges that you actually support. Wider ranges can improve compatibility, but do not eliminate peer conflicts. Pinning a problematic dependency can make updates deliberate; it also makes reviewing and applying fixes your responsibility.

FAQ

Does a lockfile make the range irrelevant? No. It records the current resolution; the range constrains future resolution and updates.

What if a package breaks semantic versioning? Test upgrades, inspect release notes, and narrow or pin the dependency when justified. A patch-only range is not a guarantee against regressions.

What does npm save by default? The usual save-prefix default is ^, but configuration and --save-exact can change that. Check npm config get save-prefix; npm config set save-prefix '~' changes your preference.

Related reading

References

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting