compare-versions vs semver
Comparing Semantic Version Strings in JavaScript Applications
compare-versionssemverSimilar Packages:

Comparing Semantic Version Strings in JavaScript Applications

compare-versions and semver are both widely used npm packages for comparing software version strings, especially those following semantic versioning (SemVer). semver is a comprehensive, standards-compliant toolkit that implements the full SemVer specification (v2.0.0), offering validation, comparison, range matching, and manipulation utilities. compare-versions, by contrast, is a minimal, focused library designed specifically for comparing version strings — including non-standard formats like dates or build numbers — with an emphasis on simplicity and predictable ordering.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
compare-versions063755.5 kB82 years agoMIT
semver05,40697.8 kB44a month agoISC

Comparing Version Strings: compare-versions vs semver

When your frontend app needs to compare software versions — whether to show update prompts, enforce compatibility, or sort changelogs — picking the right library matters. Both compare-versions and semver solve this problem, but they take very different approaches. Let’s break down how they work, where they shine, and what trade-offs you’ll face.

🔢 Core Philosophy: Minimal Comparison vs Full SemVer Compliance

compare-versions treats version comparison as a general-purpose string-sorting problem. It doesn’t assume your input follows any particular standard. It splits strings on dots, compares each segment numerically (falling back to string comparison if needed), and handles common prefixes like v automatically.

// compare-versions: works with almost anything
import compareVersions from 'compare-versions';

compareVersions('1.10.0', '1.2.0');     // returns 1 (1.10 > 1.2)
compareVersions('v2.0.0', '2.0.0');      // returns 0 (equal)
compareVersions('2023.10', '2023.9');   // returns 1

semver, on the other hand, strictly enforces the Semantic Versioning 2.0.0 spec. It will reject invalid SemVer strings and correctly interpret prereleases, build metadata, and numeric precedence as defined by the standard.

// semver: strict SemVer only
import semver from 'semver';

semver.compare('1.10.0', '1.2.0');      // returns 1
semver.compare('v2.0.0', '2.0.0');      // returns 0 (normalizes 'v')
semver.compare('2023.10', '2023.9');    // throws TypeError: Invalid Version

💡 Key insight: compare-versions prioritizes "just make it work"; semver prioritizes "do it by the book."

🧪 Handling Edge Cases: Prereleases, Builds, and Non-Numeric Segments

Prerelease Versions

compare-versions treats prerelease identifiers as plain strings and compares them lexicographically:

// compare-versions
compareVersions('1.0.0-alpha', '1.0.0-beta');  // returns -1
compareVersions('1.0.0-rc.1', '1.0.0-rc.10');  // returns 1 (because '1' > '10' as strings)

This can lead to incorrect ordering for numeric prerelease tags (like rc.10 vs rc.2).

semver correctly parses and compares prerelease identifiers per the SemVer spec — splitting on dots, comparing numerically where possible, and falling back to ASCII sort order:

// semver
semver.compare('1.0.0-alpha', '1.0.0-beta');   // returns -1
semver.compare('1.0.0-rc.1', '1.0.0-rc.10');   // returns -1 (1 < 10 numerically)
semver.compare('1.0.0-rc.2', '1.0.0-rc.10');   // returns -1

Non-SemVer Inputs

If you’re dealing with version-like strings that aren’t true SemVer — say, calendar versions (2023.10.05) or simplified formats (2.1) — compare-versions handles them gracefully:

// compare-versions
compareVersions('2.1', '2.10');        // returns -1 (2.1 < 2.10)
compareVersions('2023.10.5', '2023.10.15'); // returns -1

semver will throw an error unless you preprocess these into valid SemVer (e.g., 2.1.0):

// semver
semver.compare('2.1', '2.10');         // throws TypeError
// You must normalize first:
semver.compare(semver.coerce('2.1'), semver.coerce('2.10')); // returns -1

Note: semver.coerce() can help, but it’s lossy and may not always produce the expected result.

🛠️ Feature Set: One Function vs Full Toolkit

compare-versions exports a single function. That’s it. If you need sorting, you use it as a comparator:

// Sorting with compare-versions
const versions = ['1.10.0', '1.2.0', '2.0.0'];
versions.sort(compareVersions); // ['1.2.0', '1.10.0', '2.0.0']

semver provides a rich API beyond comparison:

  • semver.valid(v) – checks if a string is valid SemVer
  • semver.satisfies(version, range) – tests if a version matches a range like ^1.2.3
  • semver.inc(version, release) – increments a version (e.g., patch, minor)
  • semver.prerelease(v) – extracts prerelease components
// semver: advanced usage
semver.satisfies('1.2.3', '^1.0.0');     // true
semver.satisfies('2.0.0', '^1.0.0');     // false

semver.inc('1.2.3', 'minor');            // '1.3.0'

semver.valid('1.2');                     // null (invalid)
semver.valid('1.2.3');                   // '1.2.3'

If your app needs to check whether a user’s current version falls within a supported range (e.g., for feature flags or update eligibility), semver is the only practical choice.

⚖️ Trade-Offs in Real Projects

When compare-versions Wins

  • You’re building a changelog viewer that displays versions from multiple sources (some SemVer, some not).
  • You need to sort a list of arbitrary version-like strings without validation.
  • Bundle size is critical, and you only need comparison — no extra features.
  • Your team prefers “it just works” over strict compliance.

When semver Wins

  • You’re integrating with npm, package managers, or CI systems that expect real SemVer.
  • You must correctly handle prereleases, build metadata, or version ranges.
  • Your logic depends on accurate numeric comparison of all segments (e.g., 1.10 > 1.2).
  • You need to validate user input or generate new version strings.

🔄 Migration Considerations

Switching from compare-versions to semver isn’t always drop-in. For example:

// This works in compare-versions
compareVersions('1.0', '1.0.0'); // returns 0

// But in semver, you must normalize:
semver.compare('1.0.0', '1.0.0'); // OK
semver.compare('1.0', '1.0.0');   // throws error

You’d need to wrap inputs with semver.coerce() — but be cautious:

semver.coerce('1.0');      // '1.0.0'
semver.coerce('2023.10');  // '2023.10.0'
semver.coerce('v1.2.3');   // '1.2.3'

However, coerce can produce unexpected results with malformed input (e.g., coerce('abc1.2.3def')'1.2.3'), so use it only when you control or trust the input format.

✅ Summary Table

Featurecompare-versionssemver
Input Flexibility✅ Handles non-SemVer, partial versions❌ Strict SemVer only (unless coerced)
Prerelease Handling❌ Lexicographic (may misorder rc.10)✅ Spec-compliant numeric/string comparison
API Scope🔹 Single compare(a, b) function🔸 Full toolkit (validate, range, inc, etc.)

| Use Case Fit | Lightweight comparison of mixed formats | Full SemVer compliance and advanced logic |

💡 Final Recommendation

  • Use compare-versions if your goal is simple, forgiving comparison across unpredictable version formats — especially in UIs that display third-party version data.
  • Use semver if you’re working in a SemVer-native environment (like package management, DevOps tooling, or internal version policies) and need correctness, validation, or range logic.

In short: if you’re just sorting a list of version strings from an API that might include 2.1, v3.0.0-beta, and 2024.05, go with compare-versions. If you’re building a dependency resolver or update checker that must respect ^2.1.0 || >=3.0.0, semver is non-negotiable.

How to Choose: compare-versions vs semver

  • compare-versions:

    Choose compare-versions if you need a lightweight, zero-dependency utility that handles basic version comparisons across a wide variety of formats — including non-SemVer strings like '1.2', '2023.10.05', or even 'v1.0.0-beta'. It’s ideal for scenarios where you only need to sort or compare versions without requiring full SemVer compliance, validation, or advanced features like range matching. Its API is intentionally minimal: just one function that returns -1, 0, or 1.

  • semver:

    Choose semver if your project strictly follows semantic versioning and you need robust, spec-compliant tooling beyond simple comparison — such as validating version strings, checking if a version satisfies a range (e.g., '^1.2.3'), incrementing versions, or parsing prerelease identifiers. It’s the de facto standard in the Node.js ecosystem and integrates deeply with tools like npm. Use it when correctness according to the SemVer spec is critical.

README for compare-versions

compare-versions

Build Status Coverage Status npm bundle size (minified + gzip)

Compare semver version strings to find greater, equal or lesser. Runs in the browser as well as Node.js/React Native etc. Has no dependencies and is tiny.

Supports the full semver specification including versions with different number of digits like 1.0.0, 1.0, 1 and pre-releases like 1.0.0-alpha. Additionally supports the following variations:

  • Wildcards for minor and patch version like 1.0.x or 1.0.*.
  • Chromium version numbers with 4 parts, e.g. version 25.0.1364.126.
  • Any leading v is ignored, e.g. v1.0 is interpreted as 1.0.
  • Leading zero is ignored, e.g. 1.01.1 is interpreted as 1.1.1.
  • npm version ranges, e.g. 1.2.7 || >=1.2.9 <2.0.0

Install

$ npm install compare-versions

Note: Starting from v5 the main export is now named like so: import { compareVersions } from 'compare-versions'.

Note: Starting from v4 this library includes a ESM version which will automatically be selected by your bundler (webpack, parcel etc). The CJS/UMD version is lib/umd/index.js and the new ESM version is lib/esm/index.js.

Usage

Will return 1 if first version is greater, 0 if versions are equal, and -1 if the second version is greater:

import { compareVersions } from 'compare-versions';

compareVersions('11.1.1', '10.0.0'); //  1
compareVersions('10.0.0', '10.0.0'); //  0
compareVersions('10.0.0', '11.1.1'); // -1

Can also be used for sorting:

const versions = [
  '1.5.19',
  '1.2.3',
  '1.5.5'
]
const sorted = versions.sort(compareVersions);
/*
[
  '1.2.3',
  '1.5.5',
  '1.5.19'
]
*/

"Human Readable" Compare

The alternative compare function accepts an operator which will be more familiar to humans:

import { compare } from 'compare-versions';

compare('10.1.8', '10.0.4', '>');  // true
compare('10.0.1', '10.0.1', '=');  // true
compare('10.1.1', '10.2.2', '<');  // true
compare('10.1.1', '10.2.2', '<='); // true
compare('10.1.1', '10.2.2', '>='); // false

Version ranges

The satisfies function accepts a range to compare, compatible with npm package versioning:

import { satisfies } from 'compare-versions';

satisfies('10.0.1', '~10.0.0');  // true
satisfies('10.1.0', '~10.0.0');  // false
satisfies('10.1.2', '^10.0.0');  // true
satisfies('11.0.0', '^10.0.0');  // false
satisfies('10.1.8', '>10.0.4');  // true
satisfies('10.0.1', '=10.0.1');  // true
satisfies('10.1.1', '<10.2.2');  // true
satisfies('10.1.1', '<=10.2.2'); // true
satisfies('10.1.1', '>=10.2.2'); // false
satisfies('1.4.6', '1.2.7 || >=1.2.9 <2.0.0'); // true
satisfies('1.2.8', '1.2.7 || >=1.2.9 <2.0.0'); // false
satisfies('1.5.1', '1.2.3 - 2.3.4'); // true
satisfies('2.3.5', '1.2.3 - 2.3.4'); // false

Validate version numbers

Applies the same rules used comparing version numbers and returns a boolean:

import { validate } from 'compare-versions';

validate('1.0.0-rc.1'); // true
validate('1.0-rc.1');   // false
validate('foo');        // false

Validate version numbers (strict)

Validate version numbers strictly according to semver.org; 3 integers, no wildcards, no leading zero or "v" etc:

import { validateStrict } from 'compare-versions';

validate('1.0.0');      // true
validate('1.0.0-rc.1'); // true
validate('1.0');        // false
validate('1.x');        // false
validate('v1.02');      // false

Browser

If included directly in the browser, the functions above are available on the global window under the compareVersions object:

<script src=https://unpkg.com/compare-versions/lib/umd/index.js></script>
<script>
  const { compareVersions, compare, satisfies, validate } = window.compareVersions
  console.log(compareVersions('11.0.0', '10.0.0'))
  console.log(compare('11.0.0', '10.0.0', '>'))
  console.log(satisfies('1.2.0', '^1.0.0'))
  console.log(validate('11.0.0'))
  console.log(validateStrict('11.0.0'))
</script>