Semantic Versioning Comparator

Package managers, changelogs, and release pipelines all lean on one quiet assumption: that 1.10.0 is newer than 1.9.9. String comparison gets that wrong, and so does every hand-rolled version check that splits on dots without parsing numbers. This comparator implements the full semver.org precedence rules, including prerelease field ordering and build-metadata exclusion, so your ordering matches what npm, Cargo, and every spec-compliant registry does.

Compare two versions side by side, test any version against caret, tilde, comparator, hyphen, and OR ranges, or paste a whole list and sort it by real semver order. Everything runs locally in the page.

—
—
Result
—
—

How semver precedence is computed

Each version is parsed into five parts: major, minor, patch, an optional dot-separated prerelease list, and optional build metadata. Comparison walks the numeric triple first; the first non-equal component decides the order, which is why 1.10.0 beats 1.9.9 through integer comparison rather than string ordering.

When the triple is equal, build metadata is discarded and the prerelease lists are compared field by field. Numeric fields compare as numbers, mixed types sort with numbers first, and a shorter list loses when every shared field ties, because a release with more identifiers is considered further along its prerelease cycle. A version with an empty prerelease list (a stable release) always outranks any prerelease of the same triple.

Range satisfaction expands the expression into a set of comparators (greater-than, less-than, equals) and checks the candidate against all of them, with OR groups tried in sequence. Caret and tilde shorthands become an inclusive lower bound and an exclusive upper bound computed from the leftmost non-zero component, which is the same expansion node-semver performs.

Before and after: ordering a messy release list

Input — tags collected from a git repository, unsorted:

10.0.0
2.9.1
2.10.0
2.9.1-rc.2
2.9.1
1.0.0+build.7

Output — ascending semver precedence:

1.0.0+build.7
2.9.1-rc.2
2.9.1        (same triple as rc.2, stable outranks prerelease)
2.10.0       (minor 10 beats minor 9)
10.0.0       (major 10 beats major 2)
string sort would have placed 10.0.0 before 2.9.1

Who compares versions this way

Developers

  • Check whether a library upgrade stays inside your caret range before running a full dependency install that might pull breaking majors.
  • Sort changelog entries and git tags by true precedence instead of alphabetical order when drafting release notes.
  • Reproduce a client bug that only appears on 1.10.x by confirming the version ordering your resolver used.

Production

  • Gate deployments on a minimum version check that treats 1.10.0 as greater than 1.9.9, which lexicographic string comparison gets wrong.
  • Validate Docker image tags and Kubernetes image references against expected ranges during manifest review.
  • Audit release pipelines that compare versions with naive string equality or startsWith checks before they mis-tag an artifact.

Workflow

  • Paste a vendor changelog into the sort tab to build an upgrade path from your current version to the latest stable.
  • Confirm a proposed range string matches the versions your team actually ships before it goes into a package manifest.
  • Explain prerelease ordering to a teammate by walking the compare tab through alpha, beta, and rc tags of the same triple.

Common mistakes when comparing versions

  • Comparing versions as strings. Lexicographic order places 1.10.0 before 1.9.9 because the character 1 sorts below 9. Any gate that uses string greater-than on version numbers will block valid upgrades and allow invalid ones at random.
  • Assuming prereleases satisfy caret ranges. npm refuses to install 2.0.0-rc.1 for a dependency declared as ^1.2.3 or ^2.0.0, because the range targets stable releases. If you want release candidates, declare them explicitly with a range that names the prerelease tuple.
  • Forgetting that build metadata is ignored. Two artifacts tagged 1.4.0+sha.abc and 1.4.0+sha.def are the same version to every semver-aware tool. Deduplicate or rename at the registry layer rather than expecting consumers to distinguish them by precedence.
  • Using tilde where caret was intended. ~1.2.3 never reaches 1.3.0, which surprises teams that expect npm default behavior. If minor updates should flow freely, write ^1.2.3 and let the range checker here confirm the bounds before you commit the manifest.

Best practices for version ranges in dependencies

  • 1.Commit lockfiles so the resolved tree is pinned even when the manifest allows a wide caret range, giving you reproducible builds without giving up compatible updates.
  • 2.Keep pre-1.0 libraries on tilde ranges; minors in that phase are allowed to break by the semver spec, so carets over-promise compatibility.
  • 3.Use explicit comparator sets like >=1.4.0 <1.6.0 in shared infrastructure packages where a surprise minor from a caret range would ripple into every service at once.
  • 4.Run the range tab here against the exact string you are about to publish, with your current version and the next planned release, so mistakes surface in review instead of in CI.

Frequently asked questions

What are the semantic versioning precedence rules?

Semantic versioning orders versions by major, minor, then patch, comparing each numeric part in turn: 1.0.0 is less than 2.0.0, and 1.1.0 is less than 1.2.0. When the three numbers are equal, a version with a prerelease tag sorts before the same version without one, so 1.0.0-rc.1 comes before 1.0.0. Prerelease identifiers compare field by field: numeric identifiers sort numerically, alphanumeric identifiers sort by ASCII order, numeric always beats alphanumeric, and a version with more fields wins when all shared fields are equal, which is why 1.0.0-alpha sorts before 1.0.0-alpha.1 and 1.0.0-alpha.beta sorts before 1.0.0-beta.

Does build metadata affect version comparison?

No. The semver spec states that build metadata (the plus-sign suffix such as 1.0.0+20261016) must be ignored when determining version precedence, because it describes how the artifact was produced rather than what the software contains. This comparator follows that rule, so 1.0.0+build.1 and 1.0.0+build.2 compare equal. Build metadata still matters for artifact registries and lockfiles, which may treat the full string as a unique key even though the semantic versions are identical.

What does the caret in ^1.2.3 allow?

A caret pins the leftmost non-zero component and allows everything to the right that does not change that component. For 1.2.3 it means any release from 1.2.3 up to but not including 2.0.0, so 1.9.4 matches and 2.0.0 does not. For 0.2.3 the caret is stricter because the major is still zero, allowing only 0.2.x patch updates. For 0.0.3 it allows only that exact patch, which matches how npm treats pre-1.0 packages where minor and patch bumps can both introduce breaking changes.

How is the tilde different from the caret?

A tilde pins the minor version when one is specified, allowing only patch-level updates: ~1.2.3 matches 1.2.9 but not 1.3.0. With two components, ~1.2 allows anything in the 1.2 series. Tildes are the more conservative choice for libraries that ship frequent minors with occasional behavioral tweaks, while carets are the npm default because minor bumps are expected to stay compatible under semver.

What do the x-ranges and wildcards mean?

The characters x, X, and * act as wildcards in any position: 1.x matches any 1.y.z release, 1.2.x matches any 1.2.z patch, and * matches everything. They appear mostly in legacy range strings and in peer dependency declarations. This tool treats a bare * or an empty range as any version, and expands partial versions in caret and tilde ranges as if the missing parts were zero, matching the behavior of the node-semver library.

How does the prerelease exclusion rule work?

By default a prerelease version never satisfies a range unless the range itself names a prerelease on the same major.minor.patch tuple. So 1.0.0-alpha does not satisfy >=1.0.0, because that range was written against stable releases, but it does satisfy >=1.0.0-alpha because the tuple matches. This mirrors npm install behavior and prevents a caret range from silently pulling a release candidate into a production dependency tree.

Why do 1.10.0 and 1.9.9 sort the way they do?

Semantic versioning compares numeric components as integers, not as strings. String sorting would place 1.10.0 before 1.9.9 because the character 1 sorts before 9, which is the classic bug in ad-hoc version comparators. This tool parses each component to a number, so 1.9.9 sorts before 1.10.0 and 2.0.0 sorts before 10.0.0, matching what npm, Cargo, and every spec-compliant package manager does.

Related developer tools