JavaScript Regex Flags Explained —g, i, m, s, u, y —When to Use Each

Regex flags are small modifiers that completely change how a pattern behaves.

Forgetting a flag is one of the most common causes of regex bugs in JavaScript. Adding the wrong flag is almost as common.

There are six flags in JavaScript regex:

g  —global
i  —case-insensitive
m  —multiline
s  —dotAll
u  —Unicode
y  —sticky

Each flag exists for a specific reason. Each one changes matching behavior in ways that surprise developers who do not know exactly what they do.

This guide explains every JavaScript regex flag, when to use it, and the bugs that happen when you forget it.

If you want to test flags interactively, the Regex Tester lets you toggle each flag and see the effect immediately.


The g Flag —Global

The global flag makes the regex search for ALL matches, not just the first.

const text = "foo foo foo";
const withoutG = /foo/;
const withG = /foo/g;

console.log(text.match(withoutG)); // ["foo"] —only first match
console.log(text.match(withG));    // ["foo", "foo", "foo"]

Without g, most regex methods stop after finding the first match.


Where g Matters

MethodWithout gWith g
match()Returns first match with groupsReturns array of all full matches
exec()Returns first match repeatedlyAdvances lastIndex, returns each match once
test()Always true/falseAdvances lastIndex
replace()Replaces first occurrenceReplaces all occurrences
matchAll()Throws TypeErrorWorks correctly

The g Flag Trap with test()

const regex = /\d+/g;

console.log(regex.test("abc 123")); // true
console.log(regex.test("abc 123")); // false —lastIndex!
console.log(regex.test("abc 123")); // true

The g flag causes test() and exec() to update regex.lastIndex. This means repeated calls may produce inconsistent results.

Fix: either omit g when using test(), or reset lastIndex.

Related reading: Check Whether a String Matches a Regex in JS —test() vs exec() vs match()


The i Flag —Case-Insensitive

The i flag makes matching ignore case differences.

const regex = /hello/i;

console.log(regex.test("hello")); // true
console.log(regex.test("Hello")); // true
console.log(regex.test("HELLO")); // true
console.log(regex.test("HeLLo")); // true

Without i, "Hello" does NOT match /hello/.


When i Matters

  • user input validation
  • log parsing (where case varies)
  • searching/filtering
  • command parsing
function searchCaseInsensitive(text, term) {
  const regex = new RegExp(term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi");
  return text.match(regex);
}

The gi combination (global + case-insensitive) is the most common flag pair.


The m Flag —Multiline

The m flag changes how ^ and $ anchors behave.

Without m:

  • ^ matches start of string
  • $ matches end of string

With m:

  • ^ matches start of each line
  • $ matches end of each line
const text = `Line 1
Line 2
Line 3`;

const regex = /^\w+ \d+$/;
console.log(regex.test(text)); // false —^ and $ match entire string

const regexM = /^\w+ \d+$/m;
console.log(regexM.test(text)); // true —matches "Line 1" at line start

Matching Multiple Lines

const text = `ERROR: timeout
INFO: started
ERROR: connection`;

// Without m
const regex = /^ERROR.*/;
console.log(text.match(regex));
// null —^ matches start of entire string, which is "ERROR: timeout" (ok)
// but $ needs multiline for line-by-line

// With m + g
const regexGM = /^ERROR.*/gm;
console.log(text.match(regexGM));
// ["ERROR: timeout", "ERROR: connection"]

The gm combination is essential for line-by-line log parsing.


m Does NOT Affect Dot

A common misconception: m does NOT make . match newlines.

const text = "hello\nworld";
const regex = /hello.world/;
console.log(regex.test(text)); // false —. does not match \n

const regexM = /hello.world/m;
console.log(regexM.test(text)); // still false

For dot to match newlines, you need the s flag.


The s Flag —DotAll

The s flag makes . match newline characters.

const text = "hello\nworld";
const withoutS = /hello.world/;
const withS = /hello.world/s;

console.log(withoutS.test(text)); // false
console.log(withS.test(text));    // true

Without s, . matches everything EXCEPT line terminators (\n, \r, etc.).


When s Matters

  • parsing multiline log entries
  • extracting content across line breaks
  • processing AI-generated responses
  • markdown parsing
  • HTML extraction
const text = `Error: database
connection failed
at line 42`;

const regex = /Error:.*line \d+/;
console.log(regex.test(text)); // false —.* stops at newline

const regexS = /Error:.*line \d+/s;
console.log(regexS.test(text)); // true

The u Flag —Unicode

The u flag enables proper Unicode handling and Unicode property escapes.

// Without u —\w only matches ASCII
console.log(/^\w+$/.test("こんにち�?));   // false

// With u —still false because \w with u still matches ASCII only
console.log(/^\w+$/u.test("こんにち�?)); // false

// But u enables \p{} property escapes
console.log(/^\p{L}+$/u.test("こんにち�?)); // true

Unicode Property Escapes (Require u)

// Match letters from any script
/\p{L}+/u

// Match emojis
/\p{Emoji}/u

// Match currency symbols
/\p{Sc}/u

// Match punctuation
/\p{P}/u

u Changes How Character Classes Work

With the u flag, character classes handle Unicode code points correctly:

// Without u —matches individual surrogate halves
console.log(/[😀-😎]/.test("\uD83D")); // true (matches surrogate half)

// With u —correctly handles full code points
console.log(/[😀-😎]/u.test("\uD83D")); // false

u Affects \d and \s

With the u flag:

// \d still matches Unicode digits
console.log(/^\d+$/u.test("۱۲۳")); // true

// \s still matches Unicode whitespace
console.log(/^\s+$/u.test("\u00A0")); // true (non-breaking space)

Related reading: \d Is Less Efficient Than [0-9] —Node.js Regex Performance Deep Dive


The y Flag —Sticky

The y flag makes the regex match ONLY at the current lastIndex position.

const regex = /\d+/y;

regex.lastIndex = 4;
const text = "abc 123 def 456";

const match1 = regex.exec(text);
console.log(match1[0]); // "123" —matched at position 4
console.log(regex.lastIndex); // 7

// The next exec starts at position 7
const match2 = regex.exec(text);
console.log(match2); // null —"def" is not digits at position 7

g vs y

The g flag skips non-matching characters to find the next match.

The y flag only matches at the exact current position.

const text = "abc 123 def 456";

// Global —skips "abc " to find "123"
const globalRegex = /\d+/g;
console.log(globalRegex.exec(text)[0]); // "123"

// Sticky —fails because position 0 has "abc", not digits
const stickyRegex = /\d+/y;
console.log(stickyRegex.exec(text)); // null

When y Matters

The sticky flag is useful for tokenizing and lexing:

function* tokenize(text) {
  const tokenPatterns = [
    /\d+/y,
    /[a-z]+/y,
    /\s+/y,
    /[+\-*/]/y,
  ];

  let pos = 0;
  while (pos < text.length) {
    for (const pattern of tokenPatterns) {
      pattern.lastIndex = pos;
      const match = pattern.exec(text);
      if (match) {
        pos = pattern.lastIndex;
        yield match[0];
        break;
      }
    }
  }
}

const tokens = [...tokenize("123 + abc - 456")];
console.log(tokens);
// ["123", " ", "+", " ", "abc", " ", "-", " ", "456"]

Flag Order and Syntax

Flags are appended after the closing delimiter:

/pattern/gi  // Order does not matter

All these are equivalent:

/pattern/gi
/pattern/ig
pattern.match(/pattern/gi)
new RegExp("pattern", "gi")

Flags Reference Table

FlagNameEffect
gGlobalFind all matches, not just first
iCase-InsensitiveIgnore case in matching
mMultiline^ and $ match line boundaries
sDotAll. matches newline characters
uUnicodeEnable Unicode property escapes and strict Unicode handling
yStickyMatch only at lastIndex position

Common Flag Combinations

// Most common: global + case-insensitive
/pattern/gi

// Log parsing: global + multiline
/^ERROR.*/gm

// Multiline content: global + dotAll
/<div>.*?<\/div>/gs

// Unicode text: global + unicode
/\p{L}+/gu

// Replace all: global + case-insensitive
str.replace(/foo/gi, "bar")

Flag Mistakes That Cause Bugs

Forgetting g with replace()

"foo foo foo".replace(/foo/, "bar");
// "bar foo foo" —only first replaced

Fix: add g.

"foo foo foo".replace(/foo/g, "bar");
// "bar bar bar"

Forgetting m with ^ and $

const lines = "Line 1\nLine 2\nLine 3";
const regex = /^Line \d$/;
console.log(regex.test(lines)); // false

Fix: add m.


Forgetting s for Multiline Content

const text = "<div>\nHello\n</div>";
const regex = /<div>.*<\/div>/;
console.log(regex.test(text)); // false

Fix: add s.


Forgetting u with Emojis

const emoji = "😀";
console.log(/^.$/.test(emoji)); // false —emoji is 2 code units
console.log(/^.$/u.test(emoji)); // true —u treats as single character

FAQ

What are JavaScript regex flags?

Flags are modifiers that change regex matching behavior. The six flags are g, i, m, s, u, and y.

What does the g flag do?

The global flag makes the regex find all matches instead of stopping after the first one.

What does the i flag do?

The case-insensitive flag makes matching ignore letter case.

What does the m flag do?

The multiline flag makes ^ and $ match the start and end of each line, not just the string.

What does the s flag do?

The dotAll flag makes . match newline characters.

What does the u flag do?

The Unicode flag enables Unicode property escapes and correct handling of code points above U+FFFF.

What does the y flag do?

The sticky flag makes the regex match only at the current lastIndex position.

Can I combine regex flags?

Yes. Combine flags like /pattern/gi, /pattern/gms, or any combination.


Final Thoughts

Regex flags are small characters that have enormous impact on matching behavior.

Most regex bugs caused by flags come down to one thing: assuming the default behavior is what you need.

  • Default matching stops at the first match —but you usually want g.
  • Default matching is case-sensitive —but you often want i.
  • Default anchors match string boundaries —but for lines, you need m.
  • Default dot excludes newlines —but for multiline content, you need s.
  • Default character handling breaks Unicode —for emojis and international text, you need u.
  • Default matching skips ahead —for precise positioning, you need y.

Once you internalize what each flag does, debugging "regex works in Regex101 but not in my code" becomes much faster. Most of the time, the answer is a missing flag.

Related reading: Regex Works in Regex101 but Not in JavaScript —Why

If you want to experiment with flags interactively, the Regex Tester has toggle buttons for every flag, so you can see the effect immediately.

You may also find these related developer tools useful: