jsguides

String.prototype.matchAll()

str.matchAll(regexp)

The matchAll() method returns an iterator of all matches of a string against a regular expression, including capture group details for every match. The regexp must include the global /g flag — passing a regex without /g throws a TypeError.

This was introduced in ES2020 to solve a gap in match(): with the /g flag, match() returns all matched strings but strips capture group information. matchAll() gives you both — all matches and all their capture groups — through a lazy iterator.

Syntax

str.matchAll(regexp)

Parameters

ParameterTypeDescription
regexpRegExpA regular expression with the /g flag. Throws TypeError if /g is absent.

Return value

A RegExp String Iterator that yields match result arrays. Each result has the same shape as a non-global match() result: index 0 is the full match, subsequent indices are capture groups, and groups holds named captures.

Examples

Basic usage

const text = "The quick brown fox jumps over the lazy dog";

const matches = [...text.matchAll(/o/g)];

console.log(matches[0]);
// ['o', index: 12, input: 'The quick brown fox...', groups: undefined]

console.log(matches.length);
// 4

This example spreads the iterator into an array so you can inspect the results. Each element is a match array with the same shape as a non-global match() result — the matched text at index 0, the position in index, and any capture groups at subsequent indices. When your regex includes parenthesized groups, matchAll() preserves them for every match, which is the feature that sets it apart from match().

Working with capture groups

const dates = "2026-03-09, 2025-12-25, 2024-01-01";

// Extract year, month, day using capture groups
const regex = /(\d{4})-(\d{2})-(\d{2})/g;
const matches = [...dates.matchAll(regex)];

console.log(matches[0]);
// ['2026-03-09', '2026', '03', '09', index: 0, input: '2026-03-09...']

// Iterate over all matches
for (const match of dates.matchAll(regex)) {
  console.log(`Year: ${match[1]}, Month: ${match[2]}, Day: ${match[3]}`);
}
// Year: 2026, Month: 03, Day: 09
// Year: 2025, Month: 12, Day: 25
// Year: 2024, Month: 01, Day: 01

With match() and the /g flag you would lose match[1], match[2], match[3] entirely. matchAll() preserves them for every match.

The for...of loop above accesses capture groups by numeric index — match[1] for the year, match[2] for the month. Like any array-indexed scheme, this breaks if you reorder the groups. Named capture groups let you refer to matched content by a descriptive label instead, which stays correct regardless of the group order.

Named capture groups

const log = "2026-03-09 14:30:45 [INFO] Server started";

// Named capture groups (ES2018+)
const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/g;

for (const match of log.matchAll(pattern)) {
  console.log(match.groups);
  // { year: '2026', month: '03', day: '09' }
}

Named groups make the intent clear and let you access captures by descriptive names rather than numeric indices.

The examples so far show how matchAll() preserves detail that match() drops. In practice, you will often combine the iterator with destructuring and map() to transform matched text into structured data — the pattern shown next is common in parsers and data extraction pipelines.

Common patterns

Using matchAll with destructuring

const data = "id:1,name:Alice,id:2,name:Bob";
const pattern = /id:(?<id>\d+),name:(?<name>\w+)/g;

const users = [...data.matchAll(pattern)].map(m => m.groups);
console.log(users);
// [{ id: '1', name: 'Alice' }, { id: '2', name: 'Bob' }]

The combination of matchAll and destructuring from groups is a clean way to parse structured text into typed objects.

Destructuring pulls named groups into a flat object, which is useful when you need key-value pairs. But sometimes you also need the match position — where in the source string a match occurred. The index property on each match result gives you that information, which is another detail that match() with /g discards.

Collecting match positions

const source = "cat bat sat";
const positions = [];

for (const match of source.matchAll(/[a-z]at/g)) {
  positions.push({ word: match[0], index: match.index });
}

console.log(positions);
// [{ word: 'cat', index: 0 }, { word: 'bat', index: 4 }, { word: 'sat', index: 8 }]

Each match result includes match.index — the starting position in the original string — which match() with /g discards.

match() vs matchAll()

Featurematch()matchAll()
ReturnsArray or nullIterator
Capture groups with /gNot accessibleAccessible per match
/g flag requiredNoYes (always)
Match positionsLost with /gAvailable per match
MemoryFull array upfrontLazy iterator

TypeError for missing /g flag

Unlike match(), which silently ignores the absence of the global flag and returns a single match, matchAll() throws a TypeError if the regex does not have /g. This is intentional: without /g, the method would have ambiguous semantics.

const str = "abc abc";
str.matchAll(/abc/);   // TypeError: String.prototype.matchAll called with a non-global RegExp
str.matchAll(/abc/g);  // works

If you have a regex without /g and need to use matchAll, create a new regex with the global flag: new RegExp(regex.source, regex.flags + 'g').

Reusing the regex

The regex’s lastIndex property is reset between calls to matchAll. You can reuse the same regex object for multiple matchAll calls without resetting lastIndex manually — the method handles it internally. This differs from exec() in a loop, where you must manage lastIndex yourself to avoid infinite loops or skipped matches.

const re = /\d+/g;
const a = [...'abc 123 def'.matchAll(re)];
const b = [...'xyz 456 pqr'.matchAll(re)]; // lastIndex reset automatically
console.log(b[0][0]); // '456'

See Also