Regex lookahead and lookbehind check context without consuming it. That makes them useful for extraction and constraints, but also easy to overuse. This guide explains the four common lookarounds, shows alternatives, and highlights portability concerns.
What Is a Zero-Width Assertion?
A consuming token such as [0-9] advances through a digit. A lookaround checks whether another pattern succeeds at the current position, then leaves the current position unchanged.
The four common forms are:
| Assertion | Meaning |
|---|---|
(?=x) |
x must follow |
(?!x) |
x must not follow |
(?<=x) |
x must precede |
(?<!x) |
x must not precede |
Positive Lookahead
To match an integer only when followed by px:
[0-9]+(?=px)
Against width: 320px, the full match is 320; px is checked but excluded.
Lookahead can express multiple whole-value requirements. This JavaScript pattern requires 8–64 ASCII characters, at least one lowercase letter, one uppercase letter, and one digit:
^(?=.{8,64}$)(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).+$
For real passwords, avoid silently excluding Unicode and spaces unless policy requires it. Length should ideally be measured using the same units as the authentication system, and password strength should not rely on regex complexity rules alone.
Negative Lookahead
To match foo only when it is not followed by bar:
foo(?!bar)
To reject reserved usernames while accepting a simple ASCII format:
^(?!(?:admin|root|support)$)[a-z][a-z0-9_]{2,19}$
Enable case-insensitive matching if reserved names are case-insensitive. Put the assertion after ^ so it checks the entire candidate from a known location.
Positive Lookbehind
To extract a number preceded by a literal dollar sign:
(?<=$)[0-9]+(?:.[0-9]{2})?
Against Total: $49.95, the match is 49.95. The dollar sign is context, not output.
A portable alternative is to capture the value:
$([0-9]+(?:.[0-9]{2})?)
Code then reads group 1. Capturing is often clearer and works in engines without lookbehind.
Negative Lookbehind
To match cat when it is not immediately preceded by copy:
(?<!copy)cat
This checks only the exact characters immediately behind cat; it does not express every semantic meaning of “not part of copycat.” Boundaries or a different parser may be needed.
Engine Differences Matter
Modern JavaScript defines lookbehind, but deployment targets still matter. Python's standard re engine requires lookbehind alternatives to have a fixed length in many cases. Other engines permit different forms, and RE2-family engines intentionally omit lookaround and backreferences.
Before using lookbehind:
- identify the engine and minimum runtime version;
- test variable-length alternatives if the pattern needs them;
- consider a capture-based rewrite;
- run compatibility tests in production's actual runtime.
Common Mistakes
Expecting asserted text in the match
Lookarounds do not consume their context. Capture or include the text outside the assertion when it is needed.
Placing an assertion at the wrong position
An assertion examines context at its exact location. Add anchors or consuming tokens until that position is unambiguous.
Stacking many lookaheads for validation
Several scans over the same long input can hurt readability and performance. Sometimes ordinary application checks communicate the policy better.
Assuming lookbehind is simply reversed lookahead
Lookbehind evaluates preceding context, and capture behavior can be unintuitive. Inspect captures rather than relying on intuition.
Testing Lookarounds
Use near-miss cases that change only the context:
const price = /(?<=$)[0-9]+(?:.[0-9]{2})?/u;
console.assert(price.exec("$12.50")?.[0] === "12.50");
console.assert(price.exec("USD 12.50") === null);
console.assert(price.exec("$12.5")?.[0] === "12");
The final assertion exposes a business-rule gap. If cents must be absent or exactly two digits, add a boundary or validate the complete price token.
Best Practices
- Use lookarounds when excluded context materially simplifies extraction.
- Prefer captures for broader compatibility.
- Anchor global constraints deliberately.
- Limit ambiguous wildcards inside assertions.
- Test captures, boundaries, long failures, Unicode, and multiline input.
- Document the required engine and version.
Conclusion
Use regex lookahead and lookbehind as precise context checks, not decorations. Lookahead is broadly useful for constraints; lookbehind is convenient for clean extraction but has more compatibility traps. A capture-based alternative is often the safest portable design.
FAQ
Do lookarounds consume text?
No. They test context at a position and normally leave the match cursor unchanged.
Can a lookahead contain a capture?
Many engines allow it, but capture behavior and later references should be tested carefully.
Why is my lookbehind invalid?
Your engine may not support lookbehind or may require fixed-length alternatives.
Can I replace lookbehind with a capture?
Often yes: include the prefix in the match, capture the desired part, and use that capture in code or replacement.
Are lookarounds bad for performance?
Not inherently, but broad or repeated assertions can multiply work. Benchmark realistic and adversarial failures.