FAQ
Q: What is the difference between ^ and ~?
^ (caret) allows updates without modifying the leftmost non-zero version number. For example, ^1.2.3 matches >=1.2.3 <2.0.0, allowing both minor and patch updates. ~ (tilde) is more conservative, only allowing patch-level updates. For example, ~1.2.3 matches >=1.2.3 <1.3.0, accepting only patch version changes. In short, ^ trusts that the library author follows semver conventions (new features are backward-compatible), while ~ only accepts bug fixes.
Q: Why does npm default to ^ instead of exact versions?
npm defaults to ^ to strike a balance between stability and flexibility. While locking to exact versions is the safest approach, it leads to excessive duplicate packages in the dependency tree, inflates node_modules size, and prevents you from automatically receiving security patches. The ^ prefix allows automatic backward-compatible updates, including bug fixes and new features, while package-lock.json ensures version consistency across team members. This strategy assumes library authors follow semver conventions and won't introduce breaking changes in minor versions.
Q: Does ^ behave differently with 0.x.x versions?
Yes, this is a common pitfall in semver. With 0.x.x versions, ^ behaves differently: ^0.2.3 matches >=0.2.3 <0.3.0, effectively the same as ~0.2.3, because 0.x is considered an unstable development phase where any minor change may include breaking modifications. Even more restrictive, ^0.0.3 matches only 0.0.3 itself. This means that when using 0.x libraries, ^ is not as permissive as it is with 1.x and above — you need to pay special attention.
Q: How can I lock dependency versions?
There are several ways to lock versions. The simplest approach is to write an exact version number in package.json, such as "lodash": "4.17.21", without any prefix. You can also use npm install --save-exact or set save-exact=true in .npmrc to default to exact versions. Additionally, package-lock.json (npm) or pnpm-lock.yaml (pnpm) records the exact versions actually installed, ensuring CI/CD and team members use identical dependency versions. It's recommended to commit lock files to version control.
Q: How are pre-release versions (e.g., 1.0.0-beta.1) compared?
Pre-release versions have lower precedence than the corresponding release version. For example, 1.0.0-alpha.1 < 1.0.0-beta.1 < 1.0.0-rc.1 < 1.0.0. Pre-release identifiers are compared left-to-right, segment by segment: numeric segments are compared as integers, string segments are compared lexicographically, and numeric segments have lower precedence than string segments. Note that in range expressions, ^1.0.0 and ~1.0.0 do not match pre-release versions like 1.1.0-beta.1 by default — only pre-releases with the same major.minor.patch as specified in the range will be included.