Look-ahead / behind
Lookahead ((?=...) / (?!...)) and lookbehind ((?<=...) / (?<!...)) are zero-width assertions: they match positions, not characters. Used to add context without consuming.
Practical lookaround recipes
EXAMPLE
// 1) Positive lookahead — match X only if followed by Y
/foo(?=bar)/.test('foobar') // true — matches 'foo'
/foo(?=bar)/.exec('foobar')[0] // 'foo' (the 'bar' is NOT consumed)
// 2) Negative lookahead — match X only if NOT followed by Y
/foo(?!bar)/.test('foobar') // false
/foo(?!bar)/.test('foobaz') // true
// 3) Positive lookbehind — match X only if preceded by Y
/(?<=USD)\d+/.exec('USD42')[0] // '42'
// 4) Negative lookbehind
/(?<!\\)n/.test('foo\\n') // false — the `n` IS preceded by \
/(?<!\\)n/.test('foon') // true
// 5) Real recipes
// 5a) Password strength — has digit + lowercase + uppercase + length >= 8
const strong = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}$/;
strong.test('Hunter2!'); // true
strong.test('hunter'); // false
// 5b) Match a word NOT followed by a comma
/\bfoo\b(?!,)/g
// 5c) Extract numbers that are NOT IDs (i.e. not preceded by 'id=')
const s = 'id=42 amount=99';
[...s.matchAll(/(?<!id=)\b\d+\b/g)].map(m => m[0]); // ['99']
// 5d) Quoted-string contents (greedy avoidance)
const q = `name="Ada", role="admin"`;
[...q.matchAll(/"([^"]*)"/g)].map(m => m[1]); // ['Ada', 'admin']
// 5e) Split on commas NOT inside quotes (CSV-lite)
'a,"b,c",d'.split(/,(?=(?:[^"]*"[^"]*")*[^"]*$)/); // ['a', '"b,c"', 'd']
// 6) Replace using lookaround
// Add a thousands separator (no consume of digits before)
'1234567'.replace(/\B(?=(\d{3})+(?!\d))/g, ','); // '1,234,567'
// 7) Lookbehind support
// JavaScript: lookbehind needs a modern engine (V8 in Node 10+, Safari 16.4+).
// PCRE / Python / Java / .NET: supported, variable-length too.
// POSIX (basic grep / awk): NO lookaround — use ERE / Perl flavour.
// 8) Common pitfalls
// • Lookarounds are zero-width — they don't appear in the match string
// • Variable-length lookbehind support varies; recent JS engines DO allow it
// • Catastrophic backtracking — combine lookahead with greedy quantifiers carefully
//
// Tools:
// • regex101.com — visualise + benchmark + step through
// • regexr.com — quick reference
// • RE2 (Go, ripgrep) — no lookaround; linear-time guarantee
Why it matters
Lookarounds are the regex version of “asserting context.” They turn ambiguous matches into precise ones — but if you can express the same thing with capture groups + post-filter, the regex is usually easier to read.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Lookahead is widely supported; lookbehind is in modern engines (ECMAScript 2018+). foo(?=bar) // foo followed by bar (consume only foo) foo(?!bar) // foo NOT followed by bar (?<=foo)bar // bar preceded by foo (?<!foo)bar // bar NOT preceded by fooTry it Yourself »
Exercise
Negative lookahead syntax.
/foo(?
bar)/
One character.
Discussion
Loading…