Most writing about regular expressions covers the pattern and stops there. The replacement gets a sentence, usually mentioning that $1 exists. That is where the majority of real mistakes live: a pattern that matches perfectly, paired with a replacement that inserts a literal backslash, swallows a dollar sign, or silently drops half the text it was supposed to keep.
Everything JavaScript Understands
The complete list. Anything else after a dollar sign is left as written, which is why a stray $ in your replacement usually survives, right up until it does not.
| Token | Inserts | Example |
|---|---|---|
| $1 to $9 | The text captured by that numbered group | (\w+) → $1 |
| $& | The whole match, groups included | cat → [$&] gives [cat] |
| $<name> | A named group, from (?<name>…) | (?<y>\d{4}) → $<y> |
| $` | Everything before the match | Rarely useful, occasionally perfect |
| $' | Everything after the match | The mirror of the above |
| $$ | One literal dollar sign | $$9.99 gives $9.99 |
The Mistake Everyone Makes Once
A replacement copied from an answer online often uses \1 instead of $1, and in JavaScript that produces a literal backslash followed by a one.
The split is historical. sed, Perl and Python's re.sub take backreferences with a backslash, because that is how the original Unix tools wrote them. JavaScript, .NET, PHP's preg_replace and most editors use the dollar form. Both conventions are older than anyone still arguing about them, and neither is going to change.
The practical consequence is that a replacement string is not portable even when the pattern is. A pattern moves between sed and JavaScript unchanged surprisingly often; its replacement almost never does. When something comes out full of backslashes and ones, this is why.
Numbered Groups Move When You Edit the Pattern
Groups are numbered by the position of their opening bracket, counting from the left. That includes nested ones, so ((a)(b)) makes three groups, not one.
The fragility is in editing. Add a group anywhere before the ones you already reference and every number after it shifts, while the replacement keeps pointing at the old positions. Nothing errors. The output is simply wrong, in a way that looks plausible enough to survive a quick glance.
Two habits avoid it. Use a non-capturing group, (?:…), whenever you need brackets only for grouping or alternation, so it never takes a number. And use named groups, (?<year>\d{4}) with $<year>, whenever the pattern is going to be edited more than once. Names survive renumbering, and a replacement that reads $<year>/$<month> tells the next person what it does, which $2/$1 never will.
Four Substitutions Worth Knowing
These cover most of what people actually reach for a regex replace to do.
Reorder captured parts
Match (\d{4})-(\d{2})-(\d{2}) and replace with $3/$2/$1 to turn an ISO date into a European one. The same shape reorders names, swaps a delimiter, or rebuilds a CSV column in a different order.
Delete by matching
Leave the replacement empty and every match disappears. Matching <[^>]+> strips HTML tags, [ \t]+$ trims trailing whitespace, and ^\s*\n removes blank lines. Deleting is the one case where getting the pattern slightly wrong is very visible.
Wrap without retyping
$& gives you the whole match, so replacing with **$&** bolds every match in Markdown and <a href="$&">$&</a> turns bare URLs into links. No capture groups needed at all.
Collapse repetition
Replace \s+ with a single space to normalise whitespace, or (.)\1+ with $1 to squeeze runs of a repeated character down to one. The second uses a backreference in the pattern, \1, which does use a backslash: the dollar form is for the replacement only.
Backreference in the pattern, dollar in the replacement
The two halves of a substitution use different syntax for the same idea, and this trips people up more than the language differences do. Inside the pattern, \1 means "the same text that group 1 already matched", which is how you find doubled words with \b(\w+)\s+\1\b. Inside the replacement, $1 means "insert what group 1 captured". Same group, two notations, because they are read by two different parsers.
Try a replacement before you run it
Live preview, capture groups, and a stop on patterns that run away.
Where a Replacement String Runs Out
A replacement string can reorder, duplicate and surround captured text. It cannot compute anything, and this is the ceiling people hit.
It cannot change case. sed has \U and \L, and JavaScript has no equivalent, so turning a captured word into uppercase is not expressible as a replacement string. It cannot do arithmetic, so incrementing a number in a matched line is out. It cannot look anything up, so mapping matched keys to values is out.
All three are ordinary work for the function form, where the second argument to replace is a function receiving the match and its groups and returning whatever you like. That is real code rather than a string, so it belongs in your editor rather than in a tool like this one.
The useful signal: if you find yourself wanting the replacement to make a decision, the replacement string is the wrong tool and no amount of cleverness in the pattern will fix it.
Check the Matches Before You Trust the Result
A substitution shows you the finished text, which is exactly the wrong thing to check first. A replacement that looks reasonable can be built from matches that are subtly wrong, and by the time you are reading the output the evidence is gone.
The order that catches mistakes is to get the matches right first, with the pattern alone, and only then write the replacement. The matches tell you what the regex actually selected, including the empty ones and the greedy quantifier that swallowed more than you intended.
Greediness is the usual culprit. Replacing <.+> intending to strip one tag will match from the first < to the last > on the line, taking everything in between. The lazy form, <.+?>, stops at the first >. In the output both look like something happened; only the match list shows which one happened.
Frequently Asked Questions
Related Tools
Keep Reading
Why a Regex Can Hang: Backtracking and the Patterns That Cause It
One regular expression took Cloudflare's network down for 27 minutes. The shape that causes it is recognisable in seconds once you know what to look for.
camelCase, snake_case, kebab-case: Which Case Goes Where
The convention each language expects, the one language where capitalisation changes behaviour, and why acronyms break every rule.
How Diff Works: The Algorithm That Decides What Changed
Myers walks a grid one edit at a time, and Git has run it since 1986. Why the shortest diff is not always the clearest, and what makes one look wrong.
JSON Errors Explained: Trailing Commas, NaN and Other Rejections
Why valid-looking JSON fails to parse, how to read a parser's error position, and the number precision bug that silently corrupts large IDs.