JavaScript Regex Named Groups and Lookbehinds

JavaScript regular expressions provide advanced features to make pattern matching more readable and precise. This article explains how to implement named capture groups to extract data using custom labels instead of numerical indices, and how to use positive and negative lookbehind assertions to match text based on what precedes it without including those preceding characters in the result.

Named Capture Groups

Standard capture groups rely on numerical indices (e.g., result[1], result[2]), which can make code fragile if the regular expression structure changes. Named capture groups solve this by assigning explicit identifiers to matched sub-patterns using the (?<name>pattern) syntax.

When a match is found, the captured values are accessible via the groups property on the returned match object.

const datePattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = datePattern.exec('2026-03-30');

console.log(match.groups.year);  // "2026"
console.log(match.groups.month); // "03"
console.log(match.groups.day);   // "30"

Using Named Groups in Replacement

Named groups can also be referenced directly in String.prototype.replace() using the $<name> syntax:

const dateString = '2026-03-30';
const formattedDate = dateString.replace(datePattern, '$<month>/$<day>/$<year>');

console.log(formattedDate); // "03/30/2026"

Lookbehind Assertions

Lookbehind assertions determine whether a match is preceded by a specific pattern without consuming those characters as part of the match. There are two types: positive lookbehind and negative lookbehind.

Positive Lookbehind: (?<=...)

A positive lookbehind ensures that the target pattern is immediately preceded by the sub-pattern defined inside (?<=...).

// Match a price amount only if it is preceded by a dollar sign
const pricePattern = /(?<=\$)\d+(\.\d{2})?/;

const text = 'The total cost is $49.99 for the item.';
const result = text.match(pricePattern);

console.log(result[0]); // "49.99" (The '$' is not part of the match)

Negative Lookbehind: (?<!...)

A negative lookbehind ensures that the target pattern is not immediately preceded by the sub-pattern defined inside (?<!...).

// Match numbers that are NOT preceded by a dollar sign
const quantityPattern = /(?<!\$)\b\d+\b/g;

const inventoryText = 'Order 5 items for $50 each';
const numbers = inventoryText.match(quantityPattern);

console.log(numbers); // ["5"] (Ignores "50" because it follows "$")

Combining Both Features

Named capture groups and lookbehind assertions can be combined to build clean, self-documenting parsing logic:

const logPattern = /(?<=ID:\s*)(?<userId>[A-Z0-9]+)/;
const logEntry = 'User logged in with ID: USR8821 at 10:00 AM';

const userMatch = logPattern.exec(logEntry);

if (userMatch) {
  console.log(userMatch.groups.userId); // "USR8821"
}

By leveraging named capture groups and lookbehind assertions, JavaScript regular expressions become more resilient to pattern modifications and easier to maintain across large codebases.