In npm, a caret range such as ^1.0.4 accepts new minor and patch releases, while a tilde range such as ~1.0.4 accepts patch releases only. Neither accepts a new major. The range decides which updates arrive on install, and the lockfile decides which ones you actually got.
What the ranges mean
The npm documentation on semantic versioning lists the syntax. Patch releases are written as 1.0, 1.0.x or ~1.0.4. Minor releases are written as 1, 1.x or ^1.0.4. Major releases are written as an asterisk or x. The example in the docs pairs a caret range on one dependency with a tilde range on another.
Underneath is the semver specification, which says a major version must be incremented if backward incompatible changes are introduced to the public API, and that minor and patch versions reset to zero when major rises. A caret trusts the package author to follow that promise for minor releases. A tilde trusts them less.
| Range | Allows | Does not allow |
|---|---|---|
| Tilde 2.2.0 | 2.2.x patch updates | 2.3.0 and above |
| Caret 1.0.0 | 1.x.y minor and patch updates | 2.0.0 |
| Exact 1.0.4 | Only that version | Anything else |
What the range does not do
A range is not what runs in production. The lockfile pins the resolved version for each package. A fresh install with a lockfile reproduces the locked tree, and a range only matters when you run an update, or when the lockfile is missing or ignored. This is why an updated lockfile in a pull request is a real change that deserves review, even when package.json did not change.
Choosing, by situation
- For application code with a lockfile and a solid test suite, caret is a fine default. Updates are explicit, tested and reviewable.
- For packages where you have seen breakage in minor releases, tilde narrows the surface. It costs you more manual bumps.
- For build tooling and compilers where small changes alter output, consider exact pins and a deliberate update schedule.
- For libraries you publish, prefer ranges for your dependencies that do not force consumers into duplicates. Tight pins in a library push the problem to everyone downstream.
Effects on security fixes
A caret range can let a patched minor release flow in when someone updates the lockfile. A tilde may block a fix that landed in a minor release, so you have to edit the range by hand. Exact pins always need a manual change. None of these removes the need for a routine. Schedule lockfile refreshes, and read what changed.
A major update never arrives through any of them. A fix that exists only in a new major needs a deliberate migration, and that is where the effort in an upgrade program concentrates.
A routine that works with any range
- Refresh the lockfile on a schedule, in its own pull request.
- Run the full tests and a dependency audit on it.
- Review added, removed and bumped packages in the lockfile diff.
- Handle majors as separate, planned work with an owner.
- Record exceptions where you pinned a version on purpose, with a reason and a review date.
Quick check
Does a caret range ever install a new major version?
No. A caret accepts minor and patch releases within the same major.
If my range allows updates, does production change automatically?
Not when you install from a lockfile. The lockfile fixes the versions until you update it.
Safeguard reads manifests and lockfiles, flags known vulnerable versions and shows which are reachable. Try the free scan at gold.safeguard.sh/scan.
Sources
Last reviewed: 19 October 2026.