/^/

Lesson 9 of 28

Non-Capturing Groups

Use (?:...) to group parts of a pattern for structure or quantifying without adding a numbered capture group you'll never use.

5 min read

Not every pair of parentheses needs to capture. Sometimes you just want to group part of a pattern — to apply a quantifier to a sequence, or to scope an alternation — without adding another numbered group to keep track of. That’s what (?:...) is for: a non-capturing group.

The syntax

A non-capturing group looks almost identical to a normal group, with ?: right after the opening parenthesis:

/(?:ab)+/       // groups "ab" for the + quantifier, captures nothing
/(?:https?|ftp)/ // groups an alternation, captures nothing

Everything else about how it matches is identical to a capturing group — it just doesn’t show up in match[1], match[2], etc., and it doesn’t shift the numbering of any capturing groups that come after it.

A common real-world case is matching one of several URL schemes, where you don’t care about which scheme matched, only about the URL as a whole:

const text = "Visit https://example.com or ftp://files.example.com today";
const urls = text.match(/(?:https?|ftp):\/\/\S+/g);
console.log(urls); // ["https://example.com", "ftp://files.example.com"]
Visit https://example.com or ftp://files.example.com today not a url
2 matches

Keeping numbering clean

The most practical reason to reach for (?:...) is to keep your capturing groups meaningful when a pattern also needs grouping for structure. Consider matching a title followed by a name, where you only want the name:

const str = "Mrs. Smith";
const withCapture = str.match(/(Mr|Mrs|Ms)\. (\w+)/);
console.log(withCapture[1]); // "Mrs"  — the title, group 1
console.log(withCapture[2]); // "Smith" — the name is now group 2

const withoutCapture = str.match(/(?:Mr|Mrs|Ms)\. (\w+)/);
console.log(withoutCapture[1]); // "Smith" — the name is group 1

Making the title non-capturing means the name — the only thing you actually want — lands at match[1] instead of match[2]. It also avoids returning a title string you were never going to use.

Mrs. Smith Mr. Jones Dr. Who
2 matches· capture groups: 1

Notice “Dr. Who” doesn’t match at all — the alternation only lists Mr, Mrs, and Ms, so a title outside that list is correctly rejected.

Capturing vs. non-capturing, at a glance

Capturing (...) Non-capturing (?:...)
Groups for quantifiers/alternation Yes Yes
Appears in match[n] Yes No
Shifts numbering of later groups Yes No
Supports backreferences (\1) Yes No — nothing to reference
Use when… You need the matched text afterward You only need the grouping, not the text

Tip

As a rule of thumb: if you never write match[n] or \n for a particular group, make it non-capturing. It signals intent to anyone reading the pattern later — including future you.

Group without capturing

You want to match a price that’s either in dollars or euros, like $19.99 or €19.99, but you only care about capturing the numeric amount — not which currency symbol was used.

Show solution
const pattern = /(?:\$|€)(\d+\.\d{2})/;
console.log("$19.99".match(pattern)[1]); // "19.99"
console.log("€19.99".match(pattern)[1]); // "19.99"

(?:\$|€) groups the two currency symbols as alternatives without capturing either one, so match[1] is always the amount, regardless of which currency was used.

What’s next

Non-capturing groups drop the capture entirely — but sometimes you do want to keep the captured text, just with a more descriptive label than a number. Next: named groups.

Quick check

What is the main difference between (abc)+ and (?:abc)+?

Why might you prefer (?:Mr|Mrs|Ms)\. (\w+) over (Mr|Mrs|Ms)\. (\w+) when you only care about the name?