A capture group is anything wrapped in parentheses. The regex still matches the whole string it always would, but parentheses tell the engine “also remember this specific piece separately.”
pattern: (\d{4})-(\d{2})-(\d{2})
input: "shipped on 2026-09-16"
match: 2026-09-16
group 1: 2026
group 2: 09
group 3: 16The whole match is index 0; groups start at 1. If you only care whether something matched, you don't need groups at all. They're for extracting or rearranging the pieces.
Named groups make this readable
(?<year>\d{4}) instead of a bare (\d{4}) lets you reference year by name instead of remembering it's “group 1.” Switch to names once a pattern has more than two groups. Insert a new group early in the pattern and every later group's number shifts by one, which silently breaks any code that referenced them by position.
Non-capturing groups
Sometimes you need parentheses for grouping or alternation but don't want a numbered group out of it. Use (?:...). Example: (?:https?|ftp)://(\w+) groups the protocol alternation without it taking group 1 away from the hostname.
Replace patterns reuse the groups
Reformatting a date from YYYY-MM-DD to DD/MM/YYYY:
pattern: (\d{4})-(\d{2})-(\d{2})
replacement: $3/$2/$1
"2026-09-16" → "16/09/2026"$1, $2, $3 refer to the groups in the order they were captured, and you can reorder or repeat them in the replacement. $1-$1 is valid if you want the first group twice. Named groups use $<year> in the replacement instead of a number.
Forgetting the g flag
Without the g flag, a replace only touches the first match, even if the pattern matches five times in the input. It's the usual answer to “why didn't my regex replace everything,” and the fix is always to add g.