The i flag: case-insensitive (straightforward, one gotcha)
/hello/i.test("HELLO") // true
/hello/i.test("Hello") // true
// Gotcha: without 'u', case folding only covers ASCII.
// Turkish dotless 'ı' does not case-fold to 'I' without locale consideration.
// For Unicode-correct case-insensitive matching, combine i + u:
/ı/iu // handles non-ASCII case folding more correctlyThe g flag: global — and stateful. This one bites people.
The g flag makes exec() and test() stateful. The regex object stores its last match position in lastIndex. Each call to exec() or test() advances lastIndex. This is the source of some of the most baffling bugs in JavaScript:
const re = /hello/g;
re.test("hello world"); // true — lastIndex is now 5
re.test("hello world"); // false — starts at position 5, no match
re.test("hello world"); // true — lastIndex reset to 0 after failure
// Classic bug in a conditional:
const re = /d+/g;
if (re.test(input)) {
// re.lastIndex is now non-zero
const match = re.exec(input); // starts mid-string, misses early matches!
}
// Safe pattern: use re.exec() in a loop, or use str.match() / str.matchAll():
for (const match of input.matchAll(/d+/g)) {
console.log(match[0]); // no lastIndex statefulness issue
}The gflag's stateful behavior is a JavaScript design decision from the Netscape era that the language cannot remove without breaking the web. It is widely considered a design mistake. Prefer str.matchAll() (ES2020) over manual re.exec()loops to avoid it entirely.
The m flag: multiline — redefines ^ and $
const text = "line one line two line three"; // Without m: ^ matches start of string, $ matches end of string /^line/g.test(text) // true — matches "line one" at start of string /two$/g.test(text) // false — "two" is not at end of string // With m: ^ matches start of each line, $ matches end of each line /^line/gm // matches "line one", "line two", "line three" /two$/gm // true — "two" is at end of its line
This distinction matters for parsing multi-line text. Without m, ^ and $ are string boundaries. With m, they are line boundaries. If you are parsing log files or structured text and your anchors behave unexpectedly, m is usually the missing flag.
The s flag: dotAll — dot matches newlines
// Without s: . does not match
/a.b/.test("a
b") // false
// With s (ES2018+): . matches any character including
/a.b/s.test("a
b") // true
// Before the s flag existed, the workaround was [\s\S]:
/a[\s\S]b/.test("a
b") // true — matches "any character including newlines"The s flag (dotAll) was added in ES2018. Before it existed, the idiomatic JavaScript solution was the [\s\S] hack. If you see that pattern in legacy code, . with the s flag is the modern equivalent.
The u flag: Unicode mode — you probably need this
The u flag enables full Unicode mode. Without it, JavaScript regex treats strings as UTF-16 code units, not Unicode code points. This breaks for characters outside the Basic Multilingual Plane (emoji, some Asian scripts):
// Without u: emoji is two UTF-16 units, . matches only one
/^.$/.test("😀") // false — emoji is 2 code units, . matches 1
// With u: emoji is one Unicode code point
/^.$/u.test("😀") // true
// Unicode property escapes (u flag required):
/p{Emoji}/u.test("😀") // true
/p{Script=Latin}/u.test("a") // true
/p{Uppercase_Letter}/u.test("A") // trueIf your application handles user input in any language beyond ASCII, the u flag is not optional — it is necessary for correct behavior. The v flag (ES2024) is the successor to u with additional Unicode set operations; prefer it in new code if your environment supports it.
The d flag: indices — match position tracking
The d flag (ES2022) adds a indices property to the match result, containing the start and end positions of each capture group:
const match = "2026-05-17".match(/(?<year>d{4})-(?<month>d{2})-(?<day>d{2})/d);
console.log(match.indices);
// [[0, 10], [0, 4], [5, 7], [8, 10]]
// Overall match: chars 0-10
// year group: chars 0-4
// month group: chars 5-7
// day group: chars 8-10
// Useful for: syntax highlighting, editor extensions, source mapsTest regex flags in your browser
Regex Tester — test patterns with live highlighting →