Backreferences in pattern: \N and \k<name>

Capturing groups earn their keep in three places. You already know two of them: the results array from match, and the $1, $2 slots in a replacement string. The third is the one people forget — you can point back at a group from inside the same pattern. That reference is called a backreference, and it says “match the exact text this group captured a moment ago.”

That single ability unlocks a class of problems plain patterns can’t touch: anything where two parts of the input must be identical to each other, even though you don’t know ahead of time what that text will be.

([’“])
captures a quote → group 1
(.*?)
the stuff between the quotes
\1
must equal whatever group 1 captured
A backreference reuses captured text later in the same match

Backreference by number: \N

Inside a pattern, pattern:\N means “the same text that group number N matched.” Group numbering is exactly what you already use for the results array: count opening parentheses left to right, starting at 1.

The cleanest way to feel why this matters is a concrete task.

You want to find quoted strings. A quote can be single subject:'...' or double subject:"...", and both should match. The catch: whichever quote character opens the string must also close it.

A first attempt is to allow either quote at both ends with a character class:

let str = `The tag said "It's new".`;

let regexp = /['"](.*?)['"]/g;

// Not what we want
alert( str.match(regexp) ); // "It'

Look at what the engine did. It matched an opening match:", then pattern:(.*?) — lazy, so it grabs as little as possible — and stopped at the very next character that satisfies pattern:['"]. That character is the apostrophe in It's. The class pattern:['"] doesn’t care which quote opened the string; any quote closes it. So the match ends early at "It', mangling the result.

The tag said Its new
opening quote matched apostrophe closes it too early
Why a quote class on both ends breaks: the apostrophe counts as a closing quote

The fix is to remember which quote opened the string and demand the same one at the end. Wrap the opening quote in a capturing group and backreference it: pattern:(['"])(.*?)\1.

let str = `The tag said "It's new".`;

let regexp = /(['"])(.*?)\1/g;

alert( str.match(regexp) ); // "It's new"

Now it works. The engine matches pattern:(['"]) and stores what it captured — a double quote — as group 1. Later, pattern:\1 is no longer “any quote,” it’s “the double quote I just saw.” The apostrophe in It's doesn’t match a double quote, so the engine keeps going and correctly stops at the closing match:".

The same numbering scales: pattern:\2 is the text of the second group, pattern:\3 the third, and so on. What follows a backreference isn’t the group’s pattern re-run, but the literal characters it matched this time around.

Type any text with quoted phrases below and watch both patterns work on it at once — the class-on-both-ends version versus the backreference version. Try a string with an apostrophe inside double quotes to see exactly where the naive one breaks.

interactiveQuote class vs. backreference, live

Backreference by name: \k<name>

Numbers get fragile the moment a pattern grows. Add a group near the front, and every number after it shifts by one — including your backreferences. Named groups sidestep that entirely.

You name a group by writing pattern:(?<name>...), and you reference it later with pattern:\k<name>. Here’s the quote example rewritten so the quote group is called quote:

let str = `The tag said "It's new".`;

let regexp = /(?<quote>['"])(.*?)\k<quote>/g;

alert( str.match(regexp) ); // "It's new"

Identical behavior, more readable pattern. pattern:\k<quote> says “the same text the group named quote captured,” so it locks the closing quote to the opening one just as pattern:\1 did.

by number
([’“])(.*?)\1
shifts if you add a group earlier
by name
(?<quote>[’“])(.*?)\k<quote>
stable no matter where it sits
Two ways to say the same thing

Here’s that typo checker as something you can actually use. It highlights every doubled word it finds with pattern:\b(\w+)\s+\1\b (case-insensitive). Edit the text to add or remove repeats and watch the count update.

interactiveDoubled-word finder

Both forms — pattern:\N and pattern:\k<name> — do the same job. Reach for numbers on tiny throwaway patterns, and names the moment a regexp has enough parentheses that counting them stops being obvious.