← Back to JavaScript Course | Chapter 9: Async & Web APIs | Lesson 23 of 26

JS RegExp Groups

Parentheses in a regex do more than just group parts of a pattern together -- they create capturing groups, letting you extract specific pieces of a match (like the area code from a phone number) separately from the full match, or reference an earlier part of the same pattern later using a backreference.

Basic Capturing Groups

Wrapping part of a pattern in parentheses, like (\d{3})-(\d{4}), captures each parenthesized section separately -- calling .match() on a matching string returns an array where index 0 is the full match, and each following index corresponds to one captured group, in order.

Note: Number capturing groups mentally in the order their opening parenthesis appears, left to right, to correctly index into the match result array.

Warning: Forgetting that match()[0] is the full match (not the first captured group) is a very common off-by-one mistake when reading results.

Example: Basic Capturing Groups

javascript
const match = "555-1234".match(/(\d{3})-(\d{4})/);
console.log(match[0], match[1], match[2]);

Non-Capturing Groups: (?:...)

A non-capturing group, written (?:pattern), groups part of a pattern (for applying a quantifier, or organizing alternation) without adding an entry to the array of captured results -- useful when you need grouping purely for structure, not for extracting that piece separately.

Note: Use (?:...) whenever a group exists purely for structural grouping (like applying a quantifier to an alternation) and its content does not need to be individually extracted.

Warning: Using a regular capturing group everywhere, even when the captured value is never actually used, needlessly clutters the match result array with unused entries.

Example: Non-Capturing Groups: (?:...)

javascript
const match = "abcabc".match(/(?:abc)+/);
console.log(match[0]); // grouped for the +, but not captured separately

Named Capturing Groups: (?<name>...)

Instead of referencing a captured group by its numeric position, (?<name>pattern) assigns it a readable name, accessible afterward via match.groups.name -- making code that works with several captured pieces significantly clearer than relying on numbered positions.

Note: Prefer named groups over numeric indices whenever a pattern captures several pieces, since match.groups.year reads far more clearly than match[1].

Warning: Renumbering or reordering groups in a pattern that relies on numeric indices requires updating every reference to those indices -- named groups avoid this fragility entirely.

Example: Named Capturing Groups: (?<name>...)

javascript
const match = "2024-06-15".match(/(?<year>\d{4})-(?<month>\d{2})/);
console.log(match.groups.year, match.groups.month);

Backreferences: Reusing a Captured Group

A backreference, written \1 (or \k<name> for a named group), refers back to whatever a previous group in the same pattern actually matched -- useful for finding repeated content, like a word that appears twice in a row, without hardcoding what that repeated content actually is.

Note: Use a backreference specifically when you need to match "the same thing again" without knowing in advance what that specific text will be.

Warning: A backreference matches the exact text the group captured during THIS specific match attempt -- it is not a fixed pattern, and different matches in the same string can have different backreferenced content.

Example: Backreferences: Reusing a Captured Group

javascript
console.log(/(\w+) \1/.test("hello hello")); // true, same word repeated

A Practical Example: Parsing a Full Name

Combining named capturing groups with a realistic pattern -- extracting a first and last name from a full name string, or breaking a URL into its component parts -- illustrates how groups turn a single matched pattern into several individually usable pieces of extracted data.

Note: Design capturing groups around the actual pieces of data you need to use separately afterward, not around how the pattern happens to naturally break into pieces visually.

Warning: A parsing pattern assuming a rigid, fixed structure (like always exactly "First Last") will fail on real-world input that does not follow that exact assumption, like a middle name or a single-word name.

Example: A Practical Example: Parsing a Full Name

javascript
const match = "Sam Sharma".match(/(?<first>\w+) (?<last>\w+)/);
console.log(match.groups.first, match.groups.last);
Common Mistakes
  1. Forgetting that match()'s result array places the full match at index 0, with captured groups starting at index 1, not 0.
  2. Using a capturing group when a non-capturing group (?:...) would be more appropriate, cluttering the match result with unwanted extra captured pieces.
  3. Mixing up backreference numbering when a pattern has several groups, referencing the wrong group number.
Chapter Summary
  • (pattern) creates a capturing group, extracting that portion of the match separately.
  • (?:pattern) creates a non-capturing group, useful for grouping without adding an entry to the captured results.
  • (?<name>pattern) creates a named capturing group, accessible by name instead of numeric position for clearer code.
Browser Support

Capturing groups have been supported since JavaScript regex's earliest implementation; named capturing groups are supported in all current major browsers.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.