SKU // 91

JavaScript RegExp Quantifiers

Quantifiers say how many times the thing before them may repeat. They turn "one digit" into "five digits," "one or more letters," or "an optional s." Every quantifier applies only to the single item right before it: one character, one class, or one group.

// The Quantifiers

Quantifier Means Example
*0 or more/go*gle/ : ggle, gogle, google
+1 or more/\d+/ : 7, 42, 2026
?0 or 1 (optional)/colou?r/ : color, colour
{n}Exactly n/\d{5}/ : 21201
{n,}n or more/.{8,}/ : 8+ characters
{n,m}Between n and m/\d{2,4}/ : 2 to 4 digits
"a aa aaa aaaa".match(/\ba{2,3}\b/g)   // ["aa", "aaa"]
"file1 file22 file333".match(/file\d+/g)  // ["file1", "file22", "file333"]
/^https?:\/\//.test("http://x.org")       // true  (the s is optional)

// Quantifiers on Real Text

With the g flag you can see exactly what each quantifier grabs:

const text = "Hellooo World! Hello W3Schools!";
console.log(text.match(/o+/g));     // one or more o's
console.log(text.match(/lo*/g));    // an l, then zero or more o's
console.log("1, 100 or 1000?".match(/10?/g));   // a 1, then an optional 0

const nums = "100, 1000 or 10000?";
console.log(nums.match(/\d{4}/g));     // exactly 4 digits
console.log(nums.match(/\d{3,4}/g));   // 3 to 4 digits
console.log(nums.match(/\d{3,}/g));    // 3 or more digits
["ooo", "o", "o", "oo"] ["l", "looo", "l", "l", "lo", "l"] ["1", "10", "10"] ["1000", "1000"] ["100", "1000", "1000"] ["100", "1000", "10000"]
Trap: {n} does not mean "a number with n digits"
Look at the fourth line: \d{4} found "1000" twice. The second one is the first four digits of "10000". A quantifier counts characters; it does not know where a number ends. Add word boundaries to match whole numbers only: "100, 1000 or 10000?".match(/\b\d{4}\b/g) is ["1000"].

// What the Quantifier Applies To

/ha+/       // "h" then one or more "a": ha, haa, haaa
/(ha)+/     // "ha" repeated: ha, haha, hahaha
/[ha]+/     // any mix of h and a: h, aah, hahh
Watch out
/ha+/ does not repeat "ha". The + only sees the a. To repeat more than one character, wrap them in a group first.

// Greedy by Default

Quantifiers grab as much as possible while still letting the overall pattern match. This is called being greedy, and it causes a classic surprise:

const html = "<b>bold</b> and <i>italic</i>";

html.match(/<.+>/)[0];
<b>bold</b> and <i>italic</i>

You wanted just <b>, but .+ ran all the way to the last > in the text.

// Lazy: Add a ?

Put ? after any quantifier to make it lazy: it takes as little as possible.

html.match(/<.+?>/g);
["<b>", "</b>", "<i>", "</i>"]
*? +? ??Lazy versions of * + ?
{n,m}?Lazy range: as few as n
Often Better: a Negated Class
/<[^>]+>/g says "a <, then any characters that are not >, then >." It cannot overshoot, and it is easier to read than a lazy quantifier.

// Two More Traps

* can match nothing at all

"Zero or more" includes zero. A pattern made only of x* succeeds everywhere, by matching empty text:

console.log("abc".match(/x*/g));
["", "", "", ""]

Four empty matches: one before each letter and one at the end. When you need at least one, use +.

There is no {,n}

You might guess /a{,3}/ means "up to 3 a's." It does not. Without the u flag, a { that does not form a valid quantifier is treated as plain text:

console.log(/a{,3}/.test("aaa"));     // not a quantifier...
console.log(/a{,3}/.test("a{,3}"));   // ...it matches this exact text
false true

Write {0,3} instead. With the u flag, the mistake becomes a clear error: new RegExp("a{,3}", "u") is SyntaxError: Invalid regular expression: /a{,3}/u: Incomplete quantifier.

// Rewriting Earlier Patterns

Quantifiers shorten patterns you have already seen:

/^[A-Z][A-Z][A-Z]-\d\d\d\d$/     // before
/^[A-Z]{3}-\d{4}$/               // after: same meaning

/^\d{3}-\d{3}-\d{4}$/            // US phone: 410-555-0199
/^\s+|\s+$/g                     // whitespace at either end (what trim() removes)

// Quick Check

What does this log?

console.log("zzz zz z".match(/z{2}/g));
Show answer
["zz", "zz"]. The first match is the first two letters of "zzz". The leftover "z" is only one letter, so it does not match. Then "zz" matches, and the last "z" is too short.

Which matches "color" and "colour" but not "colouur"?

  1. /colou*r/
  2. /colou+r/
  3. /colou?r/
  4. /colo.*r/
Show answer
C. ? allows zero or one u. A and D also accept "colouur", and B rejects "color".

Rewrite /^\d\d\d\d\d$/ using a quantifier.

Show answer
/^\d{5}$/. {5} means exactly five of the item before it.

// Try It

  1. Click Open Playground. Match "gray", "grey", and "graaay" with one pattern using a class and a quantifier
  2. Write a pattern for a username of 3 to 16 letters, digits, or underscores
  3. Open the greedy HTML example with its Try it Yourself button, then fix it two ways: lazy and negated class
  4. Collapse every run of 2 or more spaces in a sentence to a single space
// Video Walkthroughs
Last updated: Oct 10, 2026, 1:47 AM ET