JavaScript Regex Sticky Flag Explained

This article explores the sticky flag (y) in JavaScript regular expressions, detailing how it functions, how it differs from the global flag (g), and when to use it. You will learn how the sticky flag forces a pattern to match strictly at the regex’s lastIndex position without searching forward, making it an essential tool for parsing text and building tokenizers efficiently.

What is the Sticky Flag?

The sticky flag, denoted by the character y, is a modifier applied to a regular expression (for example, /pattern/y or new RegExp('pattern', 'y')).

When the sticky flag is active, a regular expression will attempt to match a target string only at the index specified by the lastIndex property of the RegExp instance. Unlike standard regular expression searches that scan forward through the string until they find a matching sequence, a sticky regex must match immediately at lastIndex. If no match exists at that exact position, the match fails and lastIndex resets to 0.

How the Sticky Flag Works

By default, the lastIndex property of a regular expression starts at 0. When using methods like RegExp.prototype.exec() or RegExp.prototype.test(), the sticky flag alters search behavior in the following ways:

  1. Exact Position Matching: The regex engine checks if the pattern matches the substring starting precisely at str[regex.lastIndex].
  2. Advancing lastIndex: If a match is successful, regex.lastIndex is automatically updated to the index immediately following the matched text.
  3. Immediate Failure: If the character at regex.lastIndex does not begin the match, the method immediately returns null (or false for .test()), and regex.lastIndex is reset to 0.

Code Example

const text = "123 abc 456";
const stickyRegex = /\d+/y;

// First match: checks text starting at index 0 (match: "123")
console.log(stickyRegex.exec(text)[0]); // Output: "123"
console.log(stickyRegex.lastIndex);      // Output: 3

// Second match: checks text starting at index 3 (which is a space " ")
// Since " " does not match \d+, the match fails immediately.
console.log(stickyRegex.exec(text));    // Output: null
console.log(stickyRegex.lastIndex);      // Output: 0

If you manually set lastIndex to a position where a match exists, the sticky regex succeeds:

const text = "123 abc 456";
const stickyRegex = /\d+/y;

stickyRegex.lastIndex = 8; // Index where "456" begins
console.log(stickyRegex.exec(text)[0]); // Output: "456"
console.log(stickyRegex.lastIndex);      // Output: 11

Sticky Flag (y) vs. Global Flag (g)

While both the g and y flags retain state across multiple .exec() or .test() calls by using the lastIndex property, their matching strategies differ fundamentally:

const text = "apple 123";

const globalRegex = /\d+/g;
globalRegex.lastIndex = 0;
console.log(globalRegex.exec(text)[0]); // Output: "123" (scanned forward past "apple ")

const stickyRegex = /\d+/y;
stickyRegex.lastIndex = 0;
console.log(stickyRegex.exec(text));    // Output: null (stopped because index 0 is "a")

Performance and Use Cases

The primary use case for the sticky flag is lexical analysis (lexing/tokenizing) in compilers, interpreters, and template engines.

When tokenizing code or structured text: * Characters must be parsed in a strict sequential order without skipping unrecognized tokens. * Using standard regexes often requires extracting substrings (str.slice(i)) or prefixing patterns with the start-of-input anchor ^, both of which can introduce memory overhead and performance bottlenecks. * The sticky flag avoids substring allocation and ensures linear-time matching directly against the target string at the current parser offset.