SemVer 2.0.0: major vs minor vs patch, prereleases, and npm bumps
Published: 2026-09-05
How Semantic Versioning 2.0.0 defines MAJOR.MINOR.PATCH, prerelease and build metadata, precedence rules, and npm-style major/minor/patch bumps—including how matching prereleases graduate.
Semantic Versioning (SemVer) 2.0.0 is a shared contract for software version numbers: three non-negative integers, optional prerelease labels, optional build metadata, and a clear rule for which version is “greater.” When teams follow it, npm install, changelogs, and dependency ranges become predictable instead of tribal knowledge.
This guide walks through the core fields, precedence, and npm-compatible bumps—then points you at a local calculator so you can parse and increment strings without pasting package versions into a random web form.
The shape: MAJOR.MINOR.PATCH
A SemVer string looks like:
1.4.2
1.4.2-alpha.1
1.4.2-rc.1+build.9
v2.0.0
| Field | Meaning in practice |
|---|---|
| MAJOR | Incompatible API / public contract changes |
| MINOR | Backward-compatible new features |
| PATCH | Backward-compatible bug fixes |
Rules that trip people up:
- Exactly three numeric core parts (
1.2alone is not SemVer 2.0.0). - No leading zeros on core numbers (
01.2.3is invalid;0.1.0is fine). - An optional
v/Vprefix is common in git tags; tools often strip it for the canonical formMAJOR.MINOR.PATCH….
SemVer is about compatibility signaling, not calendar date or marketing. A “2.0” that only renames a CSS class still communicates a breaking change to dependents.
Prerelease: -alpha.1, -rc.2, and friends
Anything after a hyphen and before + is the prerelease:
2.0.0-alpha.1
2.0.0-beta
2.0.0-rc.2
Identifiers are dot-separated: letters, digits, and hyphens. Numeric identifiers must not have leading zeros (01 is invalid; 0 and 10 are fine).
Precedence: a release without a prerelease is greater than the same core with a prerelease:
1.0.0-rc.1 < 1.0.0
Among prereleases with the same MAJOR.MINOR.PATCH, compare identifiers left to right. Numeric identifiers compare as numbers; non-numeric compare as strings. A numeric identifier is always lower than a non-numeric one of equal position (1.0.0-1 < 1.0.0-alpha). A longer list wins only after all shared identifiers tie (1.0.0-alpha < 1.0.0-alpha.1).
Use prereleases when you need installable previews (next, beta channels) that sort below the final release of that version.
Build metadata: +… does not change order
After + comes build metadata (CI build ids, git SHAs, etc.):
1.0.0+20130313144700
1.0.0-beta+exp.sha.5114f85
Build metadata is part of the version string for identity and debugging, but must be ignored when comparing precedence. These are equal for ordering:
1.0.0+build.1 = 1.0.0+build.2 = 1.0.0
Many bump tools drop build metadata on increment so the next tag stays clean.
Major, minor, and patch bumps (stable releases)
Starting from a stable version (no prerelease):
| Bump | From 1.2.3 |
Typical intent |
|---|---|---|
| major | 2.0.0 |
Breaking change |
| minor | 1.3.0 |
Compatible feature (patch resets) |
| patch | 1.2.4 |
Compatible fix |
npm’s semver.inc (and LocalTools’ SemVer calculator) graduates a prerelease to the matching stable release when you bump at the “same” level, instead of always ticking the core number up again:
| Current | Bump | Result | Why |
|---|---|---|---|
1.2.3-rc.1 |
patch | 1.2.3 |
Clear prerelease; patch stays |
1.2.0-rc.1 |
minor | 1.2.0 |
Already on that minor; only prerelease left |
1.2.3-rc.1 |
minor | 1.3.0 |
Patch was non-zero → minor increments |
2.0.0-rc.1 |
major | 2.0.0 |
Already on that major with .0.0 |
1.2.3-rc.1 |
major | 2.0.0 |
Not yet on N.0.0 → major increments |
That graduation behavior matches what many release scripts expect when you ship the final 1.2.3 after 1.2.3-rc.*.
Prerelease bumps: prerelease, premajor, preminor, prepatch
| Release type | Idea |
|---|---|
| prerelease | Bump the prerelease counter; if you were on a stable version, bump patch first then attach prerelease |
| prepatch | Next patch, then attach prerelease (1.2.3 → 1.2.4-0 or 1.2.4-alpha.0) |
| preminor | Next minor, patch 0, then prerelease |
| premajor | Next major, minor/patch 0, then prerelease |
An optional identifier (alpha, beta, rc) replaces or restarts the prerelease series:
1.2.3+ prerelease withalpha→1.2.4-alpha.01.2.4-alpha.0+ prerelease withalpha→1.2.4-alpha.1- Switching identifier (e.g. to
beta) starts a new series for that label
Blank identifier usually means a numeric-only prerelease such as -0, -1, …
What SemVer does not cover
- Ranges (
^1.2.3,~1.2.3,>=2 <3,||) — how package managers select versions. Parsing and bumping a single string is separate from evaluating a range. - CalVer or marketing versions (
2024.09,vNext) — different schemes. - Git describe output with commit counts (
1.2.3-4-gabcdef) — not SemVer 2.0.0 unless you normalize it.
If your tool says ranges are out of scope, that is intentional: compare and bump first; let npm / pnpm resolve ranges in the lockfile.
A safe local workflow
- Paste the current version from
package.jsonor a git tag (v1.4.2-rc.1). - Confirm canonical core, prerelease, and build fields.
- Choose the bump that matches your changelog intent (breaking → major, feature → minor, fix → patch; use
pre*for tagged previews). - Compare two candidates when deciding whether a published tag is newer than what is installed—remember build metadata does not decide order.
- Copy the result into the release commit / tag.
All of that can run entirely in the browser so version strings from private packages never need to leave your machine. For the broader privacy model, see Why “local only” matters for developer tools.
Try it locally in your browser
Use the SemVer calculator to:
- Parse strict SemVer 2.0.0 (optional
v/Vprefix stripped in the canonical form). - See major, minor, patch, prerelease, and build broken out.
- Apply major / minor / patch / prerelease / premajor / preminor / prepatch bumps (npm-style; build metadata dropped on bump).
- Compare two versions with correct precedence (build ignored; stable > prerelease).
Related reading
- Why “local only” matters for developer tools — why parse and bump versions in-tab instead of uploading
package.jsonsnippets. - ULIDs for sortable IDs — time-sortable identifiers (not SemVer, but another “ordered string” developers mix up with versions).
- NanoID vs UUID (size and alphabet) — random IDs vs compatibility-signaling version numbers.