By default, the quantifiers you already know — *, +, ?, and {n,m} — are all greedy. Greedy means: try to match as much text as possible first, and only give back (“backtrack”) characters if that’s the only way the rest of the pattern can succeed.
This default is convenient most of the time, but it can also produce surprising, over-eager matches if you’re not expecting it.
Greedy quantifiers grab the maximum
Consider \d{2,4} — “between 2 and 4 digits.” Applied repeatedly across a string, greedy behavior means each match takes as many digits as it’s allowed to, not the fewest.
const matches = "123456 78 9".match(/\d{2,4}/g);
console.log(matches); // ["1234", "56", "78"]
Walk through it: starting at the first 1, the engine grabs the maximum of 4 digits (1234). The next attempt starts right after, at 56 — only 2 digits remain before the space, so it takes both. Then 78 matches the same way. The final lone 9 can’t match at all, because {2,4} requires at least 2 digits.
The classic HTML pitfall
The most famous example of greedy matching gone wrong is trying to match an HTML tag with .+:
const html = "<b>bold</b> and <i>italic</i>";
const match = html.match(/<.+>/);
console.log(match[0]);
// "<b>bold</b> and <i>italic</i>" — the WHOLE string, not just "<b>"
Here’s why: .+ is greedy, so it first tries to consume everything to the end of the string. Then the engine needs a literal > to finish the match, so it backtracks one character at a time from the end — but the very last character in the string happens to be a > (from </i>), so backtracking stops immediately, right there. The result is one giant match spanning both tags instead of two small ones.
Watch out
This is the real reason “don’t parse HTML with a naive regex” is such common advice. It’s not that regex can’t match tag-like patterns — it’s that greedy quantifiers combined with a generic . will happily bridge across multiple tags unless you constrain them more carefully (which you’ll see in the next lesson).
Greedy quantifiers at a glance
| Quantifier | Meaning | Greedy behavior |
|---|---|---|
* |
0 or more | Matches as many as possible, backtracks if needed |
+ |
1 or more | Matches as many as possible, backtracks if needed |
{n,} |
n or more | Matches as many as possible, backtracks if needed |
{n,m} |
between n and m | Matches up to m, backtracks toward n if needed |
Predict the greedy match
Without running it yet, predict what /a.*b/ matches against the string "a1b2b3b".
Show solution
It matches "a1b2b3b" — the entire string. .* greedily grabs everything to the end, then backtracks character by character until it finds the last possible b that lets the pattern succeed, which is the final b in the string, not the first one.
What’s next
Now that you’ve seen greedy quantifiers eat more than you’d like, the next lesson introduces their opposite: lazy matching, using *?, +?, and friends, applied to this exact same HTML example so you can see the difference side by side.