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 →