Regex for a Semantic Version (semver 2.0.0)

The pattern semver.org itself suggests, with its five capture groups explained, a test table built from the specification's own examples, and a second, simpler pattern for release versions only.

Official semver.org pattern (numbered groups)

Rule: A valid version as defined by the Semantic Versioning 2.0.0 grammar: MAJOR.MINOR.PATCH with no leading zeros, optional -prerelease and optional +build.

/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/

Open in tester Loads the pattern with sample text, JavaScript flavor.

Parts of the pattern

(0|[1-9]\d*)
Group 1, major: either "0" or a number that does not start with 0. Repeated for group 2 (minor) and group 3 (patch), separated by escaped dots.
(?:-( … ))?
Optional pre-release: a hyphen, then group 4 holding dot-separated identifiers.
0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*
One pre-release identifier: a numeric identifier without leading zeros, or an alphanumeric one that contains at least one letter or hyphen (so "0a" and "-" are allowed but "0123" is not).
(?:\+( … ))?
Optional build metadata: a plus sign, then group 5 with dot-separated identifiers of letters, digits and hyphens. Leading zeros are allowed here.

Test cases

27 of 27 rows agree with the rule.

Test cases for Official semver.org pattern (numbered groups)
Input Rule says Pattern says Result Note
0.0.4 valid match pass Zero is a valid number.
1.2.3 valid match pass Plain release.
10.20.30 valid match pass Multi-digit numbers.
1.0.0-alpha valid match pass Spec example.
1.0.0-alpha.1 valid match pass Spec example.
1.0.0-0.3.7 valid match pass Spec example: numeric identifiers 0, 3 and 7.
1.0.0-x.7.z.92 valid match pass Spec example.
1.0.0-x-y-z.-- valid match pass Spec example: hyphens are identifier characters.
1.0.0-alpha+001 valid match pass Spec example: build metadata may have leading zeros.
1.0.0+20130313144700 valid match pass Spec example.
1.0.0-beta+exp.sha.5114f85 valid match pass Spec example.
1.0.0+21AF26D3----117B344092BD valid match pass Spec example.
1.0.0-0a valid match pass Alphanumeric identifier that starts with 0 is allowed; the no-leading-zero rule is for numeric identifiers.
1 invalid no match pass Needs MAJOR.MINOR.PATCH.
1.2 invalid no match pass Missing patch.
1.2.3.4 invalid no match pass Four numbers.
01.2.3 invalid no match pass Leading zero in major.
1.02.3 invalid no match pass Leading zero in minor.
1.2.03 invalid no match pass Leading zero in patch.
1.2.3-0123 invalid no match pass Numeric pre-release identifier with a leading zero.
1.2.3- invalid no match pass Empty pre-release.
1.2.3+ invalid no match pass Empty build metadata.
1.0.0-alpha..1 invalid no match pass Empty identifier between dots.
1.0.0-alpha_beta invalid no match pass Underscore is not an allowed identifier character.
v1.2.3 invalid no match pass The semver FAQ: "v1.2.3" is not a semantic version; it is a tag name that contains one.
1.2.3␣ invalid no match pass Trailing space.
1.2.3\n invalid no match pass Trailing newline: rejected by JavaScript's $; PCRE2, Python and Java's $ would accept it.

Release versions only (X.Y.Z)

Rule: MAJOR.MINOR.PATCH with each number "0" or without leading zeros; no pre-release or build metadata.

/^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/

Open in tester Loads the pattern with sample text, JavaScript flavor.

Parts of the pattern

(0|[1-9][0-9]*)
A number with no leading zero, captured as major, minor and patch.

Test cases

11 of 11 rows agree with the rule.

Test cases for Release versions only (X.Y.Z)
Input Rule says Pattern says Result Note
0.0.0 valid match pass All zero.
1.2.3 valid match pass Plain.
12.345.6789 valid match pass Multi-digit.
1.2.3-alpha invalid no match pass Pre-release: valid semver but rejected here by design.
1.2.3+build invalid no match pass Build metadata: valid semver but rejected here by design.
1.2 invalid no match pass Missing patch.
1.2.3.4 invalid no match pass Four parts.
00.1.2 invalid no match pass Leading zero.
1.2.03 invalid no match pass Leading zero.
v1.2.3 invalid no match pass v prefix.
-1.2.3 invalid no match pass Negative number.

How it works

The pattern in the first variant is copied from the Semantic Versioning FAQ ("Is there a suggested regular expression (RegEx) to check a SemVer string?"), which offers two: one with named groups and one with numbered groups that it says is compatible with ECMAScript, PCRE, Python and Go. This page uses the numbered one. The five capture groups are major, minor, patch, pre-release and build metadata.

The grammar rules it encodes come straight from the specification: major, minor and patch are non-negative integers without leading zeros; a pre-release is a hyphen followed by dot-separated identifiers of [0-9A-Za-z-] that must not be empty, where numeric identifiers must not have leading zeros; build metadata is a plus sign followed by dot-separated non-empty identifiers of the same characters (leading zeros allowed).

The trickiest part is the alternation 0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*. A purely numeric identifier must be 0 or start with 1-9. An identifier containing any letter or hyphen is alphanumeric and may start with a digit, including 0: "0a" is valid and "0123" is not. The third alternative requires at least one non-digit, which is how it avoids matching "0123".

Every example version printed in the specification is in the test table (spec section on pre-release and build metadata), together with the near misses listed in the FAQ and the grammar: leading zeros, empty identifiers, missing parts.

Known false positives

  • The pattern checks syntax only. 99999999999999999999.0.0 passes, because the grammar has no upper bound on a numeric identifier. If you convert the parts to numbers in your program, decide what you do about values too large for the type you convert to.
  • It does not say a version is sensible: 0.0.0 is valid, and so is 1.0.0-0 (a pre-release).

Known false negatives

  • Tag names like v1.2.3, version ranges (^1.2.3, >=1.0.0 <2.0.0), and npm-style loose versions (1.2, 1) are rejected. They are not semantic versions: the semver FAQ says "v1.2.3" is a tag name that contains a semantic version.
  • Four-part versions such as 1.2.3.4 (common on Windows) are rejected: semver has exactly three numeric parts.

Flavor notes

  • The pattern avoids lookaround and backreferences, so it compiles in JavaScript, PCRE2 and Go. The specification's FAQ says the numbered-group version works in ECMAScript, PCRE, Python and Go; this site runs it on JavaScript, PCRE2 and Go, and the table is executed there.
  • \d is used in the official pattern. In Python str patterns \d matches any Unicode decimal digit and in PCRE2 with UCP it matches \p{Nd}; for strict ASCII behaviour in those flavors, replace \d with [0-9] (the release-only variant already does).
  • Trailing newline: JavaScript and Go's $ match only at the very end of the input; PCRE2 (default), Python and Java's $ also match before a final newline. Use \z, \Z, fullmatch() or matches() when validating.

Why not regex here?

If you need to compare versions (which is newer, does this satisfy a range), a regex only splits the string. Precedence rules live in the specification (pre-release versions sort lower than the release, numeric identifiers compare as numbers and alphanumeric ones in ASCII order); use a semver library for that and use the regex only to validate or to split.

How to use

  1. Copy the pattern for the variant that fits your rule. Variants differ in strictness, and the rule line says exactly what each accepts.
  2. Check how it is anchored: the patterns use ^ and $ to test a whole string. To find the same thing inside longer text, remove the anchors (and add word-boundary or lookaround checks) and re-run the cases.
  3. If your language is not JavaScript, read Flavor notes and change $ to \z or use a full-match function.
  4. Open it in the tester to see the explanation of each token and try your own inputs.

Worked examples

Split a version into parts

Run the official pattern in the tester with the g flag off and read groups 1 to 5: for 1.0.0-beta+exp.sha.5114f85 they are 1, 0, 0, beta and exp.sha.5114f85.

Reject the tag name

Strip a leading "v" before testing if your input is a Git tag: the semver FAQ explains that the version is "1.2.3" and the tag name is "v1.2.3".

Limits & gotchas

  • Matches the 2.0.0 grammar as read on 2026-10-02. It does not know about any later revision.
  • It does not enforce any maximum length, so very long inputs produce long matches. Check the length first if input is untrusted.

FAQ

Is "v1.2.3" a valid semantic version?

No. The Semantic Versioning FAQ says "v1.2.3" is not a semantic version; prefixing with v is a common way to indicate a version number, as in a Git tag name where the semantic version is "1.2.3".

Why is 1.2.3-0123 invalid but 1.0.0-0a valid?

Numeric pre-release identifiers must not include leading zeroes. "0123" is numeric, so it is rejected. "0a" contains a letter, so it is alphanumeric and the rule does not apply.

Do leading zeros matter in build metadata?

No. Build metadata identifiers may be all digits with leading zeros: 1.0.0-alpha+001 is one of the specification's own examples. Build metadata is also ignored when determining version precedence.

Where do the capture groups point?

Group 1 is major, 2 minor, 3 patch, 4 the pre-release string (or unmatched) and 5 the build metadata (or unmatched).

Sources

  1. semver.org: Semantic Versioning 2.0.0 Used for: Grammar, no leading zeros, pre-release and build rules, the suggested regular expressions, example versions.
  2. MDN: Input boundary assertion: ^, $ Used for: ^ and $ are the start and end of input, or of each line with the m flag.
  3. MDN: Character class escape: \d, \D, \w, \W, \s, \S Used for: \d is [0-9]; \w is letters, digits and underscore; \s is whitespace plus line terminators.
  4. PCRE2: pcre2pattern Used for: Syntax and semantics: groups, named groups, lookbehind rules, atomic groups, possessive quantifiers, \d \s \w with and without UCP, dollar and newline handling, \A \Z \z.
  5. Python docs: re: Regular expression operations (3.13) Used for: Syntax, (?P<name>), atomic groups and possessive quantifiers (3.11+), fixed-length lookbehind, Unicode \d \s \w, $ before trailing newline, \A \Z, re.sub replacement syntax, inline flags at start only (3.11+).
  6. Oracle (Java SE 21): java.util.regex.Pattern Used for: Construct table, \d \s \w without UNICODE_CHARACTER_CLASS, possessive and atomic constructs, named groups, line terminators and $, \A \Z \z.
  7. Go: regexp/syntax Used for: Full syntax table; no lookaround or backreferences; \d \s \w are ASCII-only; $ is \z; (?P<name>) and (?<name>); repetition limit 1000; \A and \z.
  8. Google RE2: RE2 Syntax (wiki) Used for: RE2 syntax with explicit NOT SUPPORTED markers: lookaround, backreferences, possessive quantifiers, \Z, atomic groups.

Every document above was opened and read on 2026-10-02. Documentation changes; if a page here disagrees with the current docs, trust the docs and tell us.