Code of the Day
IntermediateLookarounds

Lookbehind

Assert what precedes a match with positive (?<=…) and negative (?<!…) lookbehind assertions, and understand their engine constraints.

Regular ExpressionsIntermediate8 min read
Recommended first
By the end of this lesson you will be able to:
  • 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, notable

Lookbehind 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 wide

JavaScript'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 price

Extracting 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"]
JavaScript — editable, runs in your browser

Where to go next

Next: combining lookarounds — stacking multiple lookaheads and lookbehinds at the same position for complex conditional matching like password validation.

Finished reading? Mark it complete to track your progress.

On this page