Lookahead & Lookbehind
Sometimes what a piece of text needs to match depends on what's next to it, but you don't actually want that neighboring text included in your match. You want a number only if it's followed by "px," or a price only if it's preceded by "$" — but you want just the number, not the unit or the currency symbol along with it. That's exactly what lookahead and lookbehind assertions are for: checking context without consuming it.
💡 The Bottom Line
Lookahead (?=...) and lookbehind (?<=...) check that something
does (or with a !, does not) appear right after or right before the
current position — but neither one becomes part of the actual match. They're a way to require
context without capturing it.
Positive lookahead: (?=...)
Matches a number, but only if it's immediately followed by "px" — without including "px" in the match itself:
const re = /\d+(?=px)/;
"width: 240px".match(re)[0] // "240" — matched, and "px" isn't part of it
"width: 240em".match(re) // null — no "px" right after the digits, so no match
The (?=px) part is a condition that must be true right after the digits — it
never consumes or captures "px" itself, it just peeks ahead to check.
Negative lookahead: (?!...)
Matches a number, but only if it's not followed by "px":
const re = /\d+(?!px)/;
"240em".match(re)[0] // "24" — careful: this stops as soon as the rest doesn't look like "px",
// which can be surprising with partial matches; test edge cases directly
Negative lookahead is especially useful for exclusion rules — like "match a word that is not immediately followed by a specific suffix."
Positive lookbehind: (?<=...)
Matches a number, but only if it's immediately preceded by "$" — again, without including the "$" itself in the match:
const re = /(?<=\$)\d+/;
"Total: $42".match(re)[0] // "42" — matched, and "$" isn't part of the match
"Total: 42".match(re) // null — no "$" right before the digits
This is the mirror image of lookahead: instead of checking what comes after the position, it checks what comes before it.
Negative lookbehind: (?<!...)
Matches a number, but only if it's not immediately preceded by "$":
const re = /(?<!\$)\d+/;
"42 items".match(re)[0] // "42" — no "$" before it, so it matches
"$42".match(re) // null — "$" precedes it, so negative lookbehind rejects it
📋 The four assertions at a glance
(?=...)— positive lookahead: must be followed by this(?!...)— negative lookahead: must NOT be followed by this(?<=...)— positive lookbehind: must be preceded by this(?<!...)— negative lookbehind: must NOT be preceded by this
⚠️ Watch Out
None of these four assertions ever appear in the actual matched text — they're checks on the surrounding context only, sometimes called "zero-width assertions" because they don't consume any characters. If you need the surrounding text included in your result too, use a regular (capturing) group instead, not a lookaround.
🙋 There Are No Dumb Questions
Q: Is lookbehind supported everywhere, like lookahead is?
A: Lookahead has been widely supported for a long time. Lookbehind is newer in some engines —
JavaScript only added lookbehind support in relatively recent versions (2018-era engines
onward). If you need to support very old browsers or environments, double-check lookbehind
support before relying on it; lookahead has essentially universal support.
🔧 Try It Yourself
Write a pattern that matches a word only when it is not immediately followed by an
exclamation mark — so it matches "hello" in "hello world" but not the "hello" in
"hello!". Hint: you'll want \w+ combined with a negative lookahead for
!.
✅ Quick Check
What does (?<=\$)\d+ match in the string "Total: $42"?
Next up: the common gotchas that trip up almost everyone learning regex — greedy vs. lazy matching, catastrophic backtracking, and forgetting to escape special characters.