Regex Greedy vs Lazy Quantifiers: What's the Difference?
If a regex is matching way more than you expected, a greedy quantifier is usually the culprit. Here's the difference and the one-character fix.
Greedy is the default
Quantifiers like *, +, and {n,} are greedy: they match as much as possible, then backtrack only if the rest of the pattern fails.
Say you want the first HTML tag in <b>hi</b>:
"<b>hi</b>".match(/<.*>/)[0]
// "<b>hi</b>" ← matched the WHOLE string
.* greedily swallowed everything up to the last >. Not what you wanted.
Lazy: add a ?
Put ? after the quantifier to make it lazy (also called non-greedy). It matches as little as possible, expanding only when forced:
"<b>hi</b>".match(/<.*?>/)[0]
// "<b>" ← stops at the first >
The lazy versions are *?, +?, ??, and {n,}?.
A second example
Pull the first quoted string out of a line:
const s = 'name="app" port="8080"';
s.match(/".*"/)[0]; // 'app" port="8080' (greedy, too much)
s.match(/".*?"/)[0]; // 'app' (lazy, just right)
When to use which
- Greedy when you genuinely want the longest match, or when there's only one possible match anyway.
- Lazy when you want the shortest match between two delimiters — tags, quotes, brackets.
Often a negated class is even better
Lazy matching still backtracks, which can be slow on long inputs. A negated character class is frequently faster and clearer because it can't overshoot in the first place:
"<b>hi</b>".match(/<[^>]*>/)[0] // "<b>"
[^>]* means "any run of characters that aren't >", so it naturally stops at the first >.
Test it live
Experiment with greedy, lazy, and negated patterns in the regex tester, and brush up with the regex cheat sheet or common regex patterns.
Got a config file to check?
Open the config toolkit →