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

FlagJavaScriptEffect
globalgfind every match; also makes lastIndex stateful
case-insensitiveifold case for the whole pattern
multilinem^ and $ also match at line breaks
dotAlls. also matches a newline
unicodeucode-point semantics and \p{...} escapes
unicode setsvthe successor to u: set operations in classes
stickyymatch only at lastIndex, no scanning forward
indicesdadds 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 ]
  • m redefines what ^ and $ mean. It does not make . cross newlines — that is s, and the two are frequently confused.
  • A regex literal carries its flags with it: /a/g created once and reused keeps state between calls.
  • Python spells the same modes as re.I, re.M, re.S, re.X and re.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 \A or a single-string check is easier to reason about than a multiline pattern.
  • In JavaScript, m also recognises \r, \n, \u2028 and \u2029 as 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.

Regex in JavaScript Unicode and character properties

Last refreshed 2026-09-18.