Semver Range Checker — Free HTML CSS JS Snippet
Semver Range Checker · Dev · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Semver Range Checker — Caret, Tilde & Comparator Range Matching, Explained

Package managers like npm decide which version to install based on a range expression in package.json — ^2.1.0, ~1.4.2, >=1.2.0 <2.0.0, 1.x || 2.x — and the exact meaning of each syntax is a frequent source of "why did it install that version" confusion. This snippet implements the real semantic versioning comparison and range-matching rules from scratch in vanilla JavaScript, following the same logic node-semver uses.
Parsing a version into comparable parts
parseVersion() matches a string against the semver grammar — major.minor.patch with an optional -prerelease suffix and an optional +build metadata suffix — and returns the three numeric components plus the prerelease identifiers split on .. Build metadata is deliberately parsed but discarded from comparison entirely, exactly as the semver spec requires: two versions differing only in build metadata are considered equal.
Comparing prerelease identifiers correctly, not just alphabetically
The semver spec's prerelease comparison rule is subtle: identifiers consisting only of digits are compared numerically, while any other identifier is compared as a string, and a version with prerelease identifiers always sorts *before* the same version without any (1.0.0-beta is less than 1.0.0). comparePrerelease() implements exactly this — checking each dot-separated identifier with /^\d+$/ to decide whether to compare it as a number or a string, and treating a shorter identifier list as lower precedence per the spec.
Expanding caret and tilde ranges into explicit bounds
Rather than special-casing ^ and ~ throughout the matching logic, expandCaretTilde() converts each into an equivalent explicit >=lower <upper comparator pair up front. Tilde (~1.4.2) allows patch-level changes only, expanding to >=1.4.2 <1.5.0. Caret (^2.1.0) allows changes that do not modify the leftmost non-zero digit — for a version with a nonzero major it expands to >=2.1.0 <3.0.0, but for a zero-major version like ^0.2.3 it correctly narrows to >=0.2.3 <0.3.0, matching npm's "0.x is not yet stable, treat minor bumps as breaking" convention that trips up most hand-rolled semver implementations.
Wildcards, comparator sets, and OR unions
A bare 1.x or 2.* is parsed as a wildcard comparator matching any minor/patch within that major. Space-separated comparators within one range segment are ANDed together (all must match, as in >=1.2.0 <2.0.0), while segments separated by || are ORed (any one segment matching is enough), matching npm's exact range grammar. satisfies() splits on || first, then evaluates each space-separated comparator set independently until one fully matches.
A standalone comparator for direct version-to-version ordering
Below the range checker, a second tool compares two raw versions directly using the same compareVersions() function, useful for answering "is 1.9.0 actually less than 1.10.0" — a classic string-comparison trap, since "1.9.0" < "1.10.0" is false when compared as plain strings but true under real numeric semver ordering.
Build with AI
Build, Understand, Optimize, and Extend It With AI
Paste this snippet's JavaScript into an AI assistant like Claude and ask it to walk through exactly why caret ranges behave differently for a zero major version, and how comparePrerelease() decides between numeric and lexicographic identifier comparison. It is also a solid base to extend: ask for hyphen range support (1.2.3 - 2.3.4), a "next compatible version" suggestion feature, or a batch mode that checks a whole list of installed package versions against their declared ranges at once.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a client-side semver range checker in plain HTML, CSS, and JavaScript, no libraries.
Requirements:
- Two text inputs, a version and a range expression, evaluated live on every input event to report whether the version satisfies the range.
- Implement real semantic versioning parsing: major.minor.patch, an optional -prerelease suffix with dot-separated identifiers, and an optional +build metadata suffix that is parsed but excluded from all comparisons.
- Implement spec-correct version comparison: major, minor, and patch compared numerically; a version with a prerelease suffix always has lower precedence than the same version without one; prerelease identifiers compared numerically when both are all-digit, lexicographically otherwise, with a shorter identifier list ranking lower when it is a strict prefix of a longer one.
- Expand caret (^) ranges into explicit >=lower <upper bounds, correctly handling the zero-major special case (^0.2.3 should only allow patch-level bumps, not minor-level ones), and expand tilde (~) ranges into patch-level-only bounds.
- Support wildcard components (1.x, 2.*), explicit comparator operators (>=, <=, >, <, =), space-separated comparators within one range segment treated as AND, and "||"-separated segments treated as OR.
- Show a clear pass/fail result with a short explanation of which comparator set (if any) matched.
- Include several clickable example chips that load preset version/range pairs demonstrating caret, tilde, AND ranges, OR ranges, and prerelease comparisons.
- Add a separate, simpler two-version comparator section that reports strict ordering (less than, greater than, or equal) between two raw version strings using the same comparison logic.Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1Enter a version and a rangeType any valid semver version and any npm-style range expression — the result updates live.
- 2Read the pass/fail resultA green result means the version satisfies the range; red means it does not, with a short explanation underneath.
- 3Try the example chipsClick any example to load a preset version/range pair, including caret, tilde, AND ranges, OR ranges, and prerelease comparisons.
- 4Use the version comparatorEnter two versions directly in the lower section to see their strict ordering (<, >, or =) without needing a range at all.
- 5Test edge casesTry a zero-major version like ^0.2.3 versus ^2.1.0 to see how caret ranges narrow for pre-1.0 packages.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Yes, it implements the same core rules node-semver uses: caret and tilde expansion, wildcard matching, AND within a comparator set, OR across "||"-separated sets, and spec-correct prerelease precedence. It covers the common range syntax you will find in a real package.json, though it does not implement every obscure edge case of the full node-semver grammar.
Semver treats a zero major version as not yet stable, so caret ranges are intentionally stricter below 1.0.0: ^0.2.3 only allows patch-level changes (>=0.2.3 <0.3.0), while ^2.1.0 allows any change that keeps the major version at 2 (>=2.1.0 <3.0.0). This mirrors npm's actual caret behavior exactly.
Semver compares each of major, minor, and patch as a numeric value, not as a string. 9 < 10 numerically, even though the string "1.9.0" would sort after "1.10.0" under plain lexicographic string comparison — this exact trap is why the standalone comparator tool exists.
Per the semver spec, a version with a prerelease suffix always has lower precedence than the same version without one. Among two prerelease versions, each dot-separated identifier is compared numerically if both sides are all-digit, and lexicographically as a string otherwise, until a difference is found.
Yes. A bare major with an "x", "X", or "*" in the minor or patch position is treated as a wildcard matching any value in that position, consistent with common range syntax found in package.json files.
Yes. Space-separated comparators within one segment are ANDed (e.g. >=1.2.0 <2.0.0), and segments separated by "||" are ORed (e.g. 1.x || 2.x) — both are supported and can be combined in the same range string.