JavaScriptRegex Patterns & Flags

Regex Patterns & Flags

A pattern is the heart of a regex. Once you know the building blocks — character classes, anchors, quantifiers, groups, alternation, lookarounds and flags — almost every pattern you read in the wild becomes legible. This page walks through each piece with small, focused examples.

Character classes

A character class matches one character from a set. You can use a literal class with brackets, a range with a hyphen, or one of the built-in shorthand classes.

JS
/[aeiou]/      // any one vowel
/[a-z]/        // any lowercase letter
/[A-Za-z0-9_]/ // letter, digit, or underscore — same as \w
/[^0-9]/       // ^ inside [] means "not". Any non-digit.

/\d/   // digit          (same as [0-9])
/\D/   // non-digit
/\w/   // word char       ([A-Za-z0-9_])
/\W/   // non-word
/\s/   // whitespace
/\S/   // non-whitespace
/./    // any character except newline (unless the 's' flag is on)
Unicode property escapes
With the `u` flag you can use `\p{...}` and `\P{...}` to match by Unicode property — e.g. `/\p{Letter}/u` matches any letter in any script, including é, ñ, 漢, etc.
Anchors

Anchors match a position, not a character.

  • ^ — start of the input (or start of a line with the m flag).

  • $ — end of the input (or end of a line with m).

  • \b — word boundary: the spot between \w and \W.

  • \Bnot a word boundary.

JS
/^hello/.test("hello world");   // true
/world$/.test("hello world");   // true
/^hello$/.test("hello world");  // false (anchored both ends)

/\bcat\b/.test("a cat sat");    // true  — whole word
/\bcat\b/.test("category");     // false — "cat" is inside "category"
Quantifiers

Quantifiers say how many times the preceding atom may appear.

  • * — zero or more.

  • + — one or more.

  • ? — zero or one (optional).

  • {n} — exactly n times.

  • {n,} — at least n times.

  • {n,m} — between n and m times (inclusive).

JS
/a*/.test("");          // true — zero occurrences is fine
/a+/.test("");          // false — need at least one
/colou?r/.test("color");  // true  (the "u" is optional)
/colou?r/.test("colour"); // true
/\d{4}/.test("2026");   // true  — exactly four digits
Greedy vs lazy
By default quantifiers are **greedy** — they grab as much as possible. Add `?` to make them **lazy** (the smallest match):

JS
const html = "<b>one</b><b>two</b>";
html.match(/<.*>/);     // ["<b>one</b><b>two</b>"]  — greedy
html.match(/<.*?>/);    // ["<b>"]                   — lazy
Groups and alternation

Parentheses do two jobs: they group atoms (so a quantifier applies to several characters) and they capture the matched text for later use.

JS
/(ab)+/.test("ababab");          // true — repeats the pair
"2026-05-13".match(/(\d{4})-(\d{2})-(\d{2})/);
// ["2026-05-13", "2026", "05", "13"]

// Alternation matches one of several patterns
/cat|dog|fish/.test("I have a dog");  // true

// Non-capturing group: parentheses with (?: ... )
"foo bar".match(/(?:foo|bar)/);  // matches but does not capture

Named groups ((?<name>...)) make captures self-documenting:

JS
const m = "2026-05-13".match(
  /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
);
console.log(m.groups);  // { year: "2026", month: "05", day: "13" }
Lookahead and lookbehind

Lookarounds let you assert that something comes next (or before) without consuming it.

  • x(?=y) — positive lookahead: x followed by y.

  • x(?!y) — negative lookahead: x not followed by y.

  • (?<=y)x — positive lookbehind: x preceded by y.

  • (?<!y)x — negative lookbehind: x not preceded by y.

JS
// Match a number, but only when it is followed by "px"
"width: 16px, height: 32em".match(/\d+(?=px)/g);  // ["16"]

// Match a word only when NOT preceded by "no"
"yes-go, no-go, go".match(/(?<!no-)go/g);          // ["go", "go"]
Browser support
Lookbehind landed in V8 in 2018 and is supported in every evergreen browser and Node 10+. Be careful in tools that target old engines.
Escaping

To match a character that has a special meaning, prefix it with a backslash: \., \(, \?, \\. Inside a character class, the rules are looser — most metacharacters are literal there.

JS
/3\.14/.test("π ≈ 3.14");    // true — the dot is literal
/[.]/.test("a.b");           // true — inside [] no escape needed
Flags

Flags change how the whole pattern behaves. They are written after the closing slash, or as the second argument to new RegExp.

  • gglobal. Without it, methods like String.matchAll and replaceAll throw.

  • icase-insensitive.

  • mmulti-line. ^ and $ match at every \n, not only the very ends.

  • sdotAll. The . now also matches newlines.

  • uUnicode. Enables \p{...}, treats surrogate pairs as one character.

  • ysticky. Match only at regex.lastIndex, never scan forward.

  • dhasIndices. The match result gets an indices array with start/end of every group.

Multi-line and dotAll in action

JS
const text = "line one\nline two";

text.match(/^line/g);    // ["line"]                  — no m flag
text.match(/^line/gm);   // ["line", "line"]          — with m

"a\nb".match(/a.b/);     // null  — . does not cross newline
"a\nb".match(/a.b/s);    // ["a\nb"]
Read patterns left-to-right
When a regex looks dense, slow down and parse it like words: anchor → group → quantifier → next piece. Whitespace inside a JS regex is **significant**, so you cannot break a pattern across lines unless you use the `new RegExp` form and concatenate strings — or use a third-party "verbose" helper.