Lookbehind
Assert what precedes a match with positive (?<=…) and negative (?<!…) lookbehind assertions, and understand their engine constraints.
- Write positive and negative lookbehind assertions
- Apply lookbehind to extract values from labelled contexts
- Explain the fixed-width constraint that some engines impose
Lookahead checks what comes after the current position. Lookbehind checks
what comes before — again without consuming those characters. Positive lookbehind
(?<=…) requires the preceding text to match; negative lookbehind (?<!…) requires
it not to match.
Positive lookbehind
(?<=prefix) asserts that the match is immediately preceded by prefix:
// Extract numbers that follow a dollar sign
"Price: $42.50".match(/(?<=\$)[\d.]+/); // ["42.50"]
// Extract version numbers after "v"
"release v2.1.4".match(/(?<=v)\d+\.\d+\.\d+/); // ["2.1.4"]
// Extract the domain after "@" in an email
"user@example.com".match(/(?<=@)[\w.]+/); // ["example.com"]In each case, the lookbehind character (or text) is checked but excluded from the
match. You get the number 42.50, not $42.50.
Negative lookbehind
(?<!prefix) asserts that the match is not preceded by prefix:
// Match a digit not preceded by a dollar sign
"tax: 8%, price: $10".match(/(?<!\$)\d+/g); // ["8"] (not "10")
// Match "able" not preceded by "suit"
"capable notable suitable".match(/(?<!suit)able/g); // ["able", "able"] — in capable, notableLookbehind was added to JavaScript in ES2018 (V8 engine). Before that, it was unavailable in JS. If you are targeting very old environments, lookbehind is not available — but any modern browser or Node.js version supports it.
The fixed-width constraint
Many regex engines (including those in older Python and Java versions, and some non-JS engines) require fixed-width lookbehind — the pattern inside the lookbehind must match a known, constant number of characters:
# Python: variable-width lookbehind is NOT allowed in the default 're' module
re.search(r"(?<=\$\d+\.)\d+", "$42.50") # Error: look-behind requires fixed width
# Fixed-width: works
re.search(r"(?<=\$)\d+", "$42") # OK — \$ is 1 char wideJavaScript's engine (since ES2018) allows variable-width lookbehind, so this is
only an issue when writing patterns for Python's re module or older engines. The
third-party regex module for Python does support variable-width lookbehind.
// JavaScript: variable-width lookbehind is fine
"see $42.50 here".match(/(?<=\$[\d.]+)\s/); // matches the space after the priceExtracting values from labelled text
Lookbehind makes clean extraction from key: value style content:
const config = "host: localhost\nport: 5432\nuser: admin";
// Extract the hostname (value after "host: ")
config.match(/(?<=host:\s)\S+/); // ["localhost"]
config.match(/(?<=port:\s)\d+/); // ["5432"]
config.match(/(?<=user:\s)\w+/); // ["admin"]Compare this to the capturing-group approach:
/host:\s(\S+)/ — you get the same result but must read from group 1. The
lookbehind version keeps the match clean.
Lookbehind combined with other anchors
// Match words at the end of a line that are preceded by a colon
const text = "status: active\nmode: passive\nscore: 99";
const values = text.match(/(?<=:\s)\w+/g);
// ["active", "passive", "99"]Where to go next
Next: combining lookarounds — stacking multiple lookaheads and lookbehinds at the same position for complex conditional matching like password validation.
Negative lookahead
Assert that a pattern is NOT followed by specific content using (?!…) — the complement to positive lookahead.
Combining lookarounds
Stack multiple lookaheads and lookbehinds at the same position to express precise, multi-condition matching — including the classic password validation pattern.