Caret (^) vs tilde (~) in package.json — what do they mean?
`^1.2.3` allows any 1.x.x ≥ 1.2.3 (compatible-with-1.2.3). `~1.2.3` allows any 1.2.x ≥ 1.2.3 (patch only). Both stop at the next significant boundary; the difference is whether minors are allowed.
npm uses semver for version ranges. The leading symbol controls how loose the range is.
| Range | Allows | Locks |
|---|---|---|
1.2.3 | exactly that | major, minor, patch |
~1.2.3 | >=1.2.3 <1.3.0 | major, minor |
^1.2.3 | >=1.2.3 <2.0.0 | major |
^0.2.3 | >=0.2.3 <0.3.0 (special!) | major + minor (because 0.x is unstable) |
* | anything | nothing |
^ is the npm default and the right starting point for most dependencies — semver promises no breaking changes within a major. ~ is stricter: only patches. Use it for libraries with shaky minor bumps or for tightly-coupled internal packages.
Beyond ^ and ~, the lockfile (package-lock.json / yarn.lock / pnpm-lock.yaml) is what actually pins exact versions across installs. Without a lockfile, ^1.2.3 could resolve to a different version on each npm install, which is what causes "works on my machine" version drift.
Code
Follow-up questions
- •What's the role of package-lock.json given that ranges are loose?
- •How does pnpm or Yarn Berry change the dependency-resolution model?
- •What does `npm ci` do differently from `npm install`?
Common mistakes
- •Committing without a lockfile and getting different installs on CI vs dev.
- •Believing `^0.x.y` allows any 0.x — it's restricted to the same minor.
- •Trusting semver to be perfectly honored — many libraries break minor bumps in practice.
Performance considerations
- •Lockfiles speed installs by skipping resolution — `npm ci` is faster than `npm install` for CI.
Edge cases
- •Pre-releases (`1.2.3-beta.1`) are excluded from `^`/`~` unless you opt in with `--include=prerelease`.
- •Workspaces (monorepos) often pin internal packages to `workspace:*` instead of semver.
Real-world examples
- •Most boilerplates use `^` so dependencies pick up bug fixes; security-critical projects sometimes pin exact versions instead.