Every dependency in a package.json file usually has a tilde (~) or caret (^) in front of its version number, and the two symbols control very different levels of flexibility when you run npm install. Understanding them helps you avoid surprise updates that break your project — or miss important fixes because your versions are locked too tight.
The SemVer Foundation
Both symbols build on Semantic Versioning, where a version number like 2.1.4 breaks down into three parts. The last number refers to a patch release — a backward-compatible bug fix. The middle number is a minor release, adding new features while staying backward compatible. The first number is a major release, which can introduce breaking changes.
Tilde and caret each tell npm how far it’s allowed to move across those three numbers when resolving what to install.
What Tilde (~) Does
Tilde locks in the minor version and only allows patch-level updates. With a full major.minor.patch version, tilde allows only patch updates, keeping the minor version fixed — so ~4.17.21 permits 4.17.22 or 4.17.99, but blocks 4.18.0 or 5.0.0.
There’s a nuance when the minor version isn’t fully specified. ~0.2.3 allows 0.2.4 and 0.2.99 but blocks 0.3.0, while ~2 (with no minor or patch given) allows any 2.x.x release.
What Caret (^) Does
Caret is more permissive — it locks the major version but allows both minor and patch updates. If you see ^1.0.2, it means npm can install 1.0.2 or the latest minor or patch version, such as 1.1.0.
There’s an important exception for pre-1.0 packages. If a package hasn’t reached version 1.0 yet, the caret symbol only grabs patch versions, similar to how tilde behaves on stable packages. That’s because versions below 1.0.0 are considered unstable, so npm treats even minor bumps as potentially breaking.
A Quick Comparison
- Caret (^): updates minor and patch versions, keeps major fixed
- Tilde (~): updates patch versions only, keeps minor fixed
- No symbol: exact version only, no automatic updates
This is the short version — caret updates minor and patch, tilde updates patch only when the minor version is specified, and no symbol means an exact version match.
Why npm Switched Its Default
Tilde used to be npm’s default prefix, but that changed. As of npm version 1.4.3, the caret became the new default prefix for versions written into package.json via commands like npm install --save. If you’d rather go back to the older behavior, you can configure your default save-prefix back to tilde.
Choosing Between Them
Which one fits depends on how much you trust a package’s maintainers to follow SemVer correctly, and how much risk your project can absorb.
Reach for caret when:
- You trust the package to follow semver, want automatic bug fixes and new features, and the package is already stable at 1.0.0 or higher.
Reach for tilde when:
- You want maximum stability, the package has a history of breaking changes in minor releases, you’re working on a production system where stability is critical, or you’re using a package still below version 1.0.0.
Common Pitfalls to Avoid
- Assuming caret allows major version updates — it doesn’t, by design.
- Mixing ranges inconsistently across a team project, which can create conflicting expectations about what “safe to update” means.
- Trusting SemVer blindly — not every package follows it strictly, so testing after any update is still worth doing regardless of which symbol you use.
Join The Discussion
Do you stick with npm’s caret default, or do you deliberately pin dependencies to tilde for extra stability? Share any real incidents where a minor or patch update broke something unexpectedly, and how that changed your approach to versioning. If you manage a team project, it’d also be useful to hear how you keep version range conventions consistent across contributors.