Positive lookahead
Assert that a pattern is followed by specific content — without including that content in the match — using (?=…).
- Explain what a lookahead assertion is and why it is zero-width
- Write a positive lookahead with (?=…)
- Apply lookaheads to match content only when followed by a specific context
A lookahead is a zero-width assertion: it looks forward from the current position to check whether a pattern matches, but does not consume any characters. The match engine either passes or fails at that position, but the "looked-ahead" text is not included in the match.
The problem without lookaheads
Suppose you want to extract prices from text like "99.99 USD 14.50 EUR". You
want the numbers followed by USD, but not the numbers followed by EUR.
Without lookaheads, the simplest approach includes USD in the match:
"99.99 USD".match(/[\d.]+\s*USD/); // "99.99 USD" — but you wanted just "99.99"You could use a capturing group and read group 1, but then the surrounding code must always know to look at group 1. A lookahead keeps the match clean.
Positive lookahead syntax
(?=pattern) asserts "what comes next must match pattern" without consuming it:
// Match digits only when followed by " USD"
/[\d.]+(?=\s*USD)/.exec("99.99 USD 14.50 EUR")[0]; // "99.99"
/[\d.]+(?=\s*USD)/.exec("14.50 EUR"); // nullThe \s*USD is in the lookahead — it is checked but not included in the result.
The match is just the number.
More examples
Match a word only when followed by a colon:
const text = "name: Alice age: 30 city: Paris";
const keys = text.match(/\w+(?=:)/g);
// ["name", "age", "city"]Match file only when it's followed by .js:
const files = "index.js style.css main.js readme.md";
const jsFiles = files.match(/\b\w+(?=\.js)/g);
// ["index", "main"]Match numbers that appear before a percentage sign:
"Discount: 20%, tax: 8.5%, surcharge: 2%".match(/[\d.]+(?=%)/g);
// ["20", "8.5", "2"]Lookaheads are zero-width
Because the lookahead does not advance the match position, two lookaheads can apply simultaneously at the same position:
// Match a position followed by a digit AND followed by fewer than 5 characters before end
// (this is artificial — the point is that lookaheads stack)
/(?=\d)(?=.{1,4}$)\d+/.exec("abc 42")[0]; // "42"Multiple lookaheads at the same position are the basis of password validation patterns, covered in the combining-lookarounds lesson.
Lookahead with the global flag
With g, the engine matches all non-overlapping positions:
const amounts = "Total: $10, tax: $1.50, shipping: $5".match(/\$[\d.]+(?=\b)/g);
// But simpler: match dollar amounts directly
"Total: $10, tax: $1.50, shipping: $5".match(/\$[\d.]+/g);
// ["$10", "$1.50", "$5"]A lookahead is most valuable when the context you want to exclude from the match is adjacent and complex — like a unit abbreviation — rather than a simple affix.
Lookaheads (and all zero-width assertions) do not affect what text is consumed. They only affect whether the match succeeds at a given position. You can think of them as an invisible gate: the engine checks whether the gate condition is true before proceeding, but passing the gate does not move the cursor.
Where to go next
Next: negative lookahead — (?!…) asserts that what follows does not match
a pattern.