SemVer Calculator
Compare and calculate semantic versions
SemVer range syntax (^, ~, >=, ||) lets package managers resolve compatible versions. Understanding it prevents unexpected breaking changes.
What are SemVer Ranges?
While Semantic Versioning defines how to number releases, SemVer range syntax defines how to express which versions are acceptable as dependencies. Range syntax is the language that package managers like npm, Yarn, Cargo, and Composer use to determine which versions of a dependency are compatible with your project.
When you write "lodash": "^4.17.21" in your package.json, you are declaring a range: “any version of lodash that is greater than or equal to 4.17.21 and less than 5.0.0.” The package manager uses this range to select the best matching version from the registry, balancing between getting the latest bug fixes and avoiding breaking changes.
Understanding range syntax is essential for any developer who manages dependencies. Misconfigured ranges are a common source of “works on my machine” bugs, broken builds, and unexpected production issues.
Caret (^) vs Tilde (~)
The two most commonly used range operators are the caret (^) and tilde (~). They are the default in most package managers, and their behavior differs in a critical way.
Caret (^) — Compatible with Version
The caret allows changes that do not modify the left-most non-zero digit:
| Range | Equivalent | Allows |
|---|---|---|
^1.2.3 | >=1.2.3 <2.0.0 | Minor and patch updates |
^0.2.3 | >=0.2.3 <0.3.0 | Patch updates only |
^0.0.3 | >=0.0.3 <0.0.4 | Nothing (exact match) |
The caret is the default operator in npm. When you run npm install lodash, the version saved to package.json uses the caret prefix (e.g., ^4.17.21). For versions 1.0.0 and above, this is generally safe because SemVer guarantees backward compatibility within the same MAJOR version.
However, for 0.x versions, the caret becomes much stricter. ^0.2.3 only permits patch updates because the MINOR version is the left-most non-zero digit. This protects against the instability that SemVer allows during initial development.
Tilde (~) — Approximately Equivalent
The tilde allows only patch-level changes if a MINOR version is specified:
| Range | Equivalent | Allows |
|---|---|---|
~1.2.3 | >=1.2.3 <1.3.0 | Patch updates only |
~1.2 | >=1.2.0 <1.3.0 | Patch updates only |
~1 | >=1.0.0 <2.0.0 | Minor and patch updates |
The tilde is stricter than the caret for versions >= 1.0.0. While ^1.2.3 would accept 1.9.0, ~1.2.3 would not — it caps at the next MINOR version.
Comparison Operators
Beyond caret and tilde, SemVer range syntax supports explicit comparison operators that give you fine-grained control:
| Operator | Meaning | Example | Resolves To |
|---|---|---|---|
>= | Greater than or equal | >=1.2.3 | 1.2.3 and above (no upper bound) |
> | Greater than | >1.2.3 | Above 1.2.3, not including it |
<= | Less than or equal | <=2.0.0 | 2.0.0 and below |
< | Less than | <2.0.0 | Below 2.0.0, not including it |
= | Exact match | =1.2.3 | Only 1.2.3 |
You can combine operators to define precise ranges:
>=1.2.3 <2.0.0 # Same as ^1.2.3
>=1.0.0 <1.3.0 # 1.0.0 through 1.2.x
Hyphen Ranges
A hyphen between two versions defines an inclusive range:
1.2.3 - 2.3.4 # >=1.2.3 <=2.3.4
1.2 - 2.3.4 # >=1.2.0 <=2.3.4
1.2.3 - 2.3 # >=1.2.3 <2.4.0
When a partial version is used on the right side, missing components are treated as wildcards up to the next significant boundary. 1.2.3 - 2.3 means “up to but not including 2.4.0.”
X-Ranges and Wildcards
Any of the three version components can be replaced with x, X, or * to indicate “any value”:
| Range | Equivalent | Description |
|---|---|---|
* | >=0.0.0 | Any version |
1.x | >=1.0.0 <2.0.0 | Any 1.x.x version |
1.2.x | >=1.2.0 <1.3.0 | Any 1.2.x version |
"" (empty) | >=0.0.0 | Same as * |
X-ranges are useful for expressing intent clearly: 1.x says “I depend on the v1 API” without specifying which minor or patch version.
Combining Ranges with OR (||)
The double-pipe operator (||) combines multiple ranges. A version satisfies the combined range if it matches any of the sub-ranges:
^1.2.3 || ^2.0.0 # 1.x (>=1.2.3) OR 2.x
>=1.0.0 <1.5.0 || >=2.0.0 # 1.0-1.4.x OR 2.0+
This is useful when a library supports multiple major versions of a peer dependency. For example, a React component library might declare "react": "^17.0.0 || ^18.0.0" to support both React 17 and React 18.
SemVer Range Reference Table
| Syntax | Equivalent | npm Default? | Use Case |
|---|---|---|---|
^1.2.3 | >=1.2.3 <2.0.0 | Yes (default) | Accept minor + patch updates |
~1.2.3 | >=1.2.3 <1.3.0 | No | Accept patch updates only |
1.2.3 | =1.2.3 | No | Exact version (pinned) |
>=1.0.0 <2.0.0 | Same | No | Explicit range |
1.2.x | >=1.2.0 <1.3.0 | No | Wildcard patch |
* | >=0.0.0 | No | Any version |
^1.2.3 || ^2.0.0 | Union | No | Multiple major versions |
Common Use Cases
- npm / Yarn dependencies: The
package.jsondependenciesfield uses SemVer ranges to declare acceptable dependency versions. The caret (^) is the default operator. - Peer dependencies: Libraries declare peer dependencies with ranges to support multiple major versions of frameworks (e.g.,
"react": "^17.0.0 || ^18.0.0"). - CI/CD constraints: Build pipelines use ranges to specify tool versions, ensuring consistent builds while still receiving patch updates.
- Lockfiles:
package-lock.jsonandyarn.lockfreeze the exact resolved versions, whilepackage.jsonranges define the acceptable version space. - Monorepo workspace protocols: Monorepo tools (Turborepo, Lerna, pnpm) use SemVer ranges to manage inter-package dependencies within the workspace.
- Container images: Dockerfile
FROMdirectives use version tags that follow SemVer conventions (e.g.,node:18.17for the latest 18.17.x patch).
Try These Examples
Compares version 1.4.2 with 2.0.0. The tool detects a MAJOR version difference, meaning breaking changes were introduced.
1.4.2 Checks if version 1.9.0 satisfies the caret range ^1.2.3 (resolves to >=1.2.3 <2.0.0). Result: satisfies.
1.9.0 Checks if version 1.3.0 satisfies the tilde range ~1.2.3 (resolves to >=1.2.3 <1.3.0). Result: does not satisfy, because tilde only allows patch updates.
1.3.0 Increments version 1.4.2 by a minor bump, producing 1.5.0. The patch number resets to 0.
1.4.2