Flags and matching modes
How global, case-insensitive, multiline, dotAll, Unicode and sticky modes change a match, and where to put them.
The flags that matter
| Flag | JavaScript | Effect |
|---|---|---|
| global | g | find every match; also makes lastIndex stateful |
| case-insensitive | i | fold case for the whole pattern |
| multiline | m | ^ and $ also match at line breaks |
| dotAll | s | . also matches a newline |
| unicode | u | code-point semantics and \p{...} escapes |
| unicode sets | v | the successor to u: set operations in classes |
| sticky | y | match only at lastIndex, no scanning forward |
| indices | d | adds match and group start/end offsets |
const re = /^ab.$/;
re.test('ab\nc'); // false, . does not cross the newline
/^ab.$/s.test('ab\nc'); // true with dotAll
/^ab$/m.test('ab\nzz'); // true: with m, $ matches before the newline
/^zz$/m.test('ab\nzz'); // true: with m, ^ matches after the newline
/HELLO/i.test('hello'); // true
'aaa'.match(/a/g).length; // 3, without g you would get one
const m = /(\d)(?<u>\d)/d.exec('42');
m.indices[0]; // [ 0, 2 ]
m.indices.groups.u; // [ 1, 2 ]mredefines what^and$mean. It does not make.cross newlines — that iss, and the two are frequently confused.- A regex literal carries its flags with it:
/a/gcreated once and reused keeps state between calls. - Python spells the same modes as
re.I,re.M,re.S,re.Xandre.A; the letters are equivalent, the API is not.
How modes move the anchors
import re
text = 'first\nsecond\nthird'
re.findall(r'^\w+', text) # ['first'] ^ is string start only
re.findall(r'^\w+', text, re.M) # ['first', 'second', 'third']
re.findall(r'\w+$', text) # ['third']
re.findall(r'\w+$', text, re.M) # ['first', 'second', 'third']
# \A and \z ignore the multiline mode entirely
re.findall(r'\A\w+', text, re.M) # ['first']- Multiline anchors match before and after a newline, not around it, so a trailing carriage return from a Windows file can sit between the text and the
$. - When both modes are possible, an absolute anchor such as
\Aor a single-string check is easier to reason about than a multiline pattern. - In JavaScript,
malso recognises\r,\n,\u2028and\u2029as line terminators.
Inline and scoped modifiers
import re
# turn a mode on for the rest of the pattern
re.search(r'(?i)hello', 'HELLO')
# turn it on for one group only
re.search(r'(?i:hello) world', 'HELLO world')
# turn a mode off inside an otherwise insensitive pattern
re.search(r'(?i)hello (?-i:world)', 'HELLO WORLD') # no match
# verbose mode: whitespace and comments in the pattern itself
re.compile(r'''
^ (?P<area>\d{3}) # area code
- (?P<num>\d{4}) # local number
$
''', re.X)// JavaScript has no inline flags; the flag belongs to the whole pattern
const ci = /hello/i;
// build the pattern dynamically when the mode has to vary at runtime
function literal(text, flags) {
return new RegExp(text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), flags);
}
literal('a.b', 'i').test('A.B'); // true: the dot is escaped, so only a real dot matches
literal('a.b', 'i').test('AxB'); // false, the pattern no longer behaves as a wildcard💡
Flags belong to the pattern, not the call site, except when you use the constructor form. That is exactly why a shared global regex is dangerous: the
g flag and its lastIndex travel with the object into every function that receives it.FAQ
What is the difference between the m and s flags?
m changes the anchors: ^ and $ start matching at line breaks as well. s changes the dot: . starts matching newlines too. They are independent, and both are needed together when you parse a multi-line block with a dot-star.Do I need the u flag?
Yes for any pattern that touches emoji, CJK text or
\p{...} properties, and for correct handling of surrogate pairs. Without it, a class such as [^x] can match half of a two-unit character.Related
Regex in JavaScript Unicode and character properties
Last refreshed 2026-09-18.