JavaScript Regex Flags — g, i, m, s, u, and v Explained
JavaScript regex flags change how patterns match. Learn when to use global /g, case-insensitive /i, multiline /m, dotAll /s, unicode /u and the new /v flag, plus how the...
JavaScript regex flags modify matching behavior. The most common bug: reusing a /g regex between calls without resetting lastIndex. This causes intermittent “no match” failures that are hard to debug.
Test your patterns with the Regex Tester.
All JavaScript regex flags
const re = /pattern/flags; // Literal syntax
const re = new RegExp('pattern', 'flags'); // Constructor
// Available flags:
// g — global: find all matches, not just first
// i — case-insensitive
// m — multiline: ^ and $ match line boundaries, not string boundaries
// s — dotAll: . matches newline (\n)
// u — unicode: enables Unicode escape sequences \u{...}
// v — unicodeSets (ES2024): upgraded unicode with set notation
// d — hasIndices: .exec() returns start/end indices for groups
// y — sticky: match from lastIndex position only
/g — global flag and lastIndex
The global flag makes .exec() stateful — it remembers where it left off using lastIndex:
const re = /\d+/g;
const str = 'abc 123 def 456';
// .exec() with /g advances lastIndex:
re.exec(str); // ['123'], re.lastIndex = 7
re.exec(str); // ['456'], re.lastIndex = 15
re.exec(str); // null, re.lastIndex = 0 (reset)
// Common bug: reusing /g regex across calls
function findFirstNumber(str) {
const re = /\d+/g;
return re.exec(str)?.[0];
}
findFirstNumber('abc 123'); // "123" ✓
findFirstNumber('abc 123'); // "123" ✓ (new regex each call, fine)
// BUGGY version: regex defined outside function
const DIGIT_RE = /\d+/g;
function findFirstNumberBuggy(str) {
return DIGIT_RE.exec(str)?.[0];
}
findFirstNumberBuggy('abc 123'); // "123" ✓ (lastIndex: 0 → 7)
findFirstNumberBuggy('abc 456'); // undefined ✗ (starts at lastIndex=7, "456" is at index 4, miss!)
findFirstNumberBuggy('abc 789'); // "789" ✓ (lastIndex reset after null)
Fix: Use .test() or .exec() without /g when only checking a single match. Or reset lastIndex explicitly:
const DIGIT_RE = /\d+/g;
function findFirstNumber(str) {
DIGIT_RE.lastIndex = 0; // Reset before use
return DIGIT_RE.exec(str)?.[0];
}
/g with String.matchAll()
matchAll() is the safe way to iterate all matches with /g:
const str = 'cat bat sat';
const matches = [...str.matchAll(/[cbsr]at/g)];
// [['cat', index:0], ['bat', index:4], ['sat', index:8]]
// Capture groups:
const dates = 'Born 2001-05-12, died 2090-12-31';
for (const match of dates.matchAll(/(\d{4})-(\d{2})-(\d{2})/g)) {
const [full, year, month, day] = match;
console.log(`${year}/${month}/${day}`);
}
// 2001/05/12
// 2090/12/31
/i — case-insensitive
/hello/i.test('HELLO'); // true
/hello/i.test('Hello'); // true
/^[a-z]+$/i.test('ABCdef'); // true (same as [a-zA-Z])
// Common use: case-insensitive search
'The Quick Brown Fox'.match(/quick/i); // ['Quick']
/m — multiline
Without /m, ^ and $ match start/end of the entire string. With /m, they match start/end of each line:
const text = 'line 1\nline 2\nline 3';
/^line/g.exec(text); // ['line'] at position 0 only
const lines = text.match(/^line.+$/mg);
// ['line 1', 'line 2', 'line 3']
// Replace only at start of lines:
text.replace(/^/mg, '> ');
// "> line 1\n> line 2\n> line 3"
/s — dotAll (single-line mode)
By default, . does NOT match \n. The /s flag makes . match everything:
// Without /s:
/<div>.*<\/div>/.test('<div>hello\nworld</div>'); // false (. stops at \n)
// With /s:
/<div>.*<\/div>/s.test('<div>hello\nworld</div>'); // true
// Practical: match multiline HTML tags:
const html = '<p>\n Hello world\n</p>';
const match = html.match(/<p>(.*?)<\/p>/s);
// match[1] = "\n Hello world\n"
/u — unicode
// Without /u: emoji counted as 2 chars (surrogate pairs)
'😀'.length; // 2
/^.$/.test('😀'); // false (. matches one code unit, not code point)
// With /u: emoji is one character
/^.$/u.test('😀'); // true
// Unicode category escapes (require /u):
/\p{Letter}/u.test('ñ'); // true
/\p{Number}/u.test('②'); // true (Unicode numbers)
/\p{Emoji}/u.test('😀'); // true
// Unicode property escapes:
'abc αβγ'.match(/\p{Script=Greek}+/gu); // ['αβγ']
/v — unicodeSets (ES2024)
The /v flag is an upgraded version of /u with set operations:
// Intersection of unicode properties:
/[\p{Letter}&&\p{ASCII}]/v.test('a'); // true (ASCII letter)
/[\p{Letter}&&\p{ASCII}]/v.test('ñ'); // false (not ASCII)
// Subtraction:
/[\p{Letter}--\p{ASCII}]/v.test('ñ'); // true (non-ASCII letter)
// String literal inside character class:
/[\q{hello|world}]/v.test('hello'); // true
// More accurate multichar sequences:
/[🏴]/v.test('🏴'); // true (Scottish flag)
/d — indices (ES2022)
// Returns start and end index for each group:
const match = 'hello world'.match(/(?<word>\w+)/d);
// match.indices[0] = [0, 5] (full match)
// match.indices.groups.word = [0, 5] (named group)
// Useful for code editors: highlight where each capture is
Related tools
- Regex Tester — test regex flags interactively
- JavaScript Regex Replace — replace with patterns
- Text Diff — compare text strings
Related posts
- The 2026 Regex Cheatsheet (PCRE, JS, Python — Side by Side) — A dense reference for modern regex: anchors, character classes, quantifiers, loo…
- Catastrophic Backtracking: The Regex That Kills Your Server — One regex with nested quantifiers can reduce your server to 100% CPU in millisec…
- Regex Email Validation — Patterns, Edge Cases, and Best Practices — Email validation regex needs to balance strictness with real-world email formats…
- Regex Lookahead and Lookbehind — Zero-Width Assertions Explained — Regex lookaheads and lookbehinds match positions without consuming characters. H…
- Regex Replace in JavaScript — String.replace() and replaceAll() with Patterns — Master JavaScript regex replace: String.replace() with capture groups, replaceAl…
Related tool
Test regular expressions with live match highlighting and explanation.
Written by Mian Ali Khalid. Part of the Dev Productivity pillar.