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.
/[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)
Anchors
Anchors match a position, not a character.
^— start of the input (or start of a line with themflag).$— end of the input (or end of a line withm).\b— word boundary: the spot between\wand\W.\B— not a word boundary.
/^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).
/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 digitsconst 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.
/(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 captureNamed groups ((?<name>...)) make captures self-documenting:
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.
// 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"]
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.
/3\.14/.test("π ≈ 3.14"); // true — the dot is literal
/[.]/.test("a.b"); // true — inside [] no escape neededFlags
Flags change how the whole pattern behaves. They are written after the closing slash, or as the second argument to new RegExp.
g— global. Without it, methods likeString.matchAllandreplaceAllthrow.i— case-insensitive.m— multi-line.^and$match at every\n, not only the very ends.s— dotAll. The.now also matches newlines.u— Unicode. Enables\p{...}, treats surrogate pairs as one character.y— sticky. Match only atregex.lastIndex, never scan forward.d— hasIndices. The match result gets anindicesarray with start/end of every group.
Multi-line and dotAll in action
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"]