Semantic Versioning (SemVer) Explained: Major, Minor, Patch
If you have ever run npm install and seen a dependency jump from 2.3.1 to 3.0.0 and break everything, you have experienced a SemVer violation. Semantic versioning is a contract between package maintainers and their users — when followed correctly, version numbers communicate intent, not just change.
The format: MAJOR.MINOR.PATCH
A SemVer version has exactly three dot-separated numeric components:
| Component | When to increment | Example bump |
|---|---|---|
| MAJOR | Breaking changes — existing code will need updating | 2.4.1 → 3.0.0 |
| MINOR | New backward-compatible features | 2.3.1 → 2.4.0 |
| PATCH | Backward-compatible bug fixes | 2.3.1 → 2.3.2 |
When you increment MAJOR, reset MINOR and PATCH to 0. When you increment MINOR, reset PATCH to 0.
What counts as a breaking change?
A change is breaking if existing consumers of the public API need to change their code to keep working:
- Removing a function, method, class, or exported value
- Renaming a public API (even a typo fix)
- Changing a function's signature (parameter types, order, or return type)
- Changing default behaviour in a way that alters existing results
- Raising the minimum Node.js / runtime version requirement
Adding new optional parameters, adding new exports, or fixing a bug without changing the interface are not breaking changes.
Pre-release versions
Pre-release identifiers follow the patch number with a hyphen:
| Version | Meaning |
|---|---|
1.0.0-alpha.1 | Early unstable release, no API guarantees |
1.0.0-beta.2 | Feature-complete but potentially buggy |
1.0.0-rc.1 | Release candidate — API locked, final testing |
Pre-release versions have lower precedence than the release: 1.0.0-rc.1 < 1.0.0. They are not installed by npm unless you explicitly request them or use @next / @beta dist-tags.
npm version range operators
npm's package.json uses a range syntax to specify which versions are acceptable:
| Operator | Meaning | Example | Installs |
|---|---|---|---|
^ | Compatible — same MAJOR, any higher MINOR/PATCH | ^2.3.1 | >=2.3.1 <3.0.0 |
~ | Approximately — same MAJOR.MINOR, any higher PATCH | ~2.3.1 | >=2.3.1 <2.4.0 |
>=2.0.0 <3.0.0 | Explicit range | >=2.0.0 <3.0.0 | Any v2.x |
2.3.1 | Exact version (pinned) | 2.3.1 | Exactly 2.3.1 |
* | Any version | * | Latest |
^ is the npm default and usually the right choice for application dependencies. Use ~ (or exact pinning) for critical dependencies where unexpected MINOR changes could cause issues.
Version 0.x — special case
When MAJOR is 0 (e.g. 0.4.2), SemVer specifies that the API is not yet stable. Any version bump may be breaking. Treat each 0.x release like a MAJOR bump. The ^ operator reflects this: ^0.4.2 only allows >=0.4.2 <0.5.0, not 0.5.x.
Checking compatibility
# See what version npm would install for a range
npm info react@"^18.0.0" version
# Check if two versions are compatible
npx semver -r "^2.3.1" 2.4.0 # exits 0 (match)
npx semver -r "^2.3.1" 3.0.0 # exits 1 (no match)Try it yourself
Use the free browser-based SemVer Comparator on DevBench — no signup, runs entirely in your browser.
Open SemVer Comparator