String.prototype.replace()

With a string pattern it replaces one occurrence and stops. Replacing every occurrence needs either a /g regex or replaceAll — and the $ characters in your replacement are not literal.

String methodES3 (1999)Live demo
Common call
s.replace('a', 'b')
Returns
a new string — assign it, the original never changes
Replaces
nothing; it is the base substitution method
Watch out
a string pattern hits only the FIRST match
string.replace(patternpattern — A string matches literally and ONCE. A RegExp matches by pattern, and replaces every occurrence only if it carries the g flag.type: string | RegExp · required, replacementreplacement — A string, in which $& $1 $` $' and $$ have special meanings. Or a function called with (match, ...groups, offset, string) whose return value is inserted literally.type: string | Function · required)
→ string

Demo

Live evaluation
Try:
Inputs
sstringthe source string
searchstringtext to find
replstringreplacement text
Output
'a-b-c'.replace('-', '+')
'a+b-c'

The first case is the one to internalise: 'a-b-c' with a string pattern becomes 'a+b-c'. One hyphen changed, the second untouched, and nothing warns you. The second case shows the same thing with a word — only the leading 'cat' becomes 'fox'. The fourth case demonstrates that the replacement string is not literal text: $& expands to whatever was matched, so '[$&]' wraps the match in brackets rather than inserting a literal dollar sign and ampersand.

Parameters

NameTypeRequiredDescription
patternstring | RegExpyesA string matches literally and ONCE. A RegExp matches by pattern, and replaces every occurrence only if it carries the g flag.
replacementstring | FunctionyesA string, in which $& $1 $` $' and $$ have special meanings. Or a function called with (match, ...groups, offset, string) whose return value is inserted literally.

Return value

string — A NEW string. Strings are immutable, so the original is never modified — an unassigned replace call does nothing at all.

Common patterns

Replace every occurrence
Either form works; replaceAll reads better for plain text.
s.replaceAll('-', '+');
s.replace(/-/g, '+');
Reorder with capture groups
$1 and $2 refer to the captured parts.
'2026-09-17'.replace(/(\d+)-(\d+)-(\d+)/, '$3/$2/$1');
Compute the replacement
A function replacer sidesteps all $ escaping.
text.replace(/\d+/g, m => Number(m) * 2);

Examples

1. First match only
'a-b-c'.replace('-', '+')
Returns
'a+b-c'
2. Global regex
'a-b-c'.replace(/-/g, '+')
Returns
'a+b+c'
3. No match, no change
'abc'.replace('x', 'y')
Returns
'abc'
4. $& is the match
'x'.replace('x', '$&$&')
Returns
'xx'
5. $$ is a literal $
'price: 5'.replace('5', '$$5')
Returns
'price: $5'
6. Swap two groups
'ab'.replace(/(a)(b)/, '$2$1')
Returns
'ba'

Pitfalls

1. A string pattern replaces only the first match
By far the most common mistake with this method. There is no error, no warning, and the result looks right on any input that happens to contain a single occurrence — so it reaches production and fails on the first two-occurrence string.
Second one survives
'a-b-c'.replace('-', '+')
'a+b-c'
replaceAll
'a-b-c'.replaceAll('-', '+')
'a+b+c'
2. It returns a new string and changes nothing
Strings are immutable. Calling replace as a statement and expecting the variable to update is silent no-op — the result must be assigned or used.
Discarded
let s = 'a-b';
s.replace('-', '+');
s
'a-b'
Assign it
let s = 'a-b';
s = s.replace('-', '+');
s
'a+b'
3. $ in the replacement is not literal
A replacement string built from user input or a regex source can contain $&, $1 or $` and will expand unexpectedly. Double the dollar to get a literal one, or use a function replacer, whose return value is always inserted verbatim.
Expands the match
'x'.replace('x', '$&!')
'x!' // not '$&!'
Function is literal
'x'.replace('x', () => '$&!')
'$&!'
4. A regex built from user input needs escaping
Interpolating arbitrary text into new RegExp turns characters like . * ( into operators, which at best matches the wrong thing and at worst throws a SyntaxError. Use replaceAll with a plain string, or escape the input first.
Dot matches anything
'a.b axb'.replace(new RegExp('a.b', 'g'), '-')
'- -'
Literal string
'a.b axb'.replaceAll('a.b', '-')
'- axb'
5. A non-global regex with replaceAll throws
The reverse trap. replace accepts either, but replaceAll insists the regex carry the g flag so its name cannot lie about what it does.
Throws
'aa'.replaceAll(/a/, 'b')
TypeError: String.prototype.replaceAll called with a non-global RegExp argument
Add the flag
'aa'.replaceAll(/a/g, 'b')
'bb'

When to use

Use it
  • Substituting the first occurrence deliberately
  • Pattern-based substitution with a RegExp
  • Reordering or reformatting with capture groups
  • Computing each replacement with a function
Reach for something else
  • Replacing every occurrence of plain text → replaceAll, which is clearer
  • You only want to test for presence → includes
  • You want the pieces, not a new string → split
  • Building the pattern from user input → escape it, or use a string pattern

Notes

Complexity
O(n) for a string pattern; regex cost depends entirely on the pattern
Return
A new string; the original is untouched
CPython impl
V8: Builtins-string-replace / regexp.cc
Memory
Allocates the result string
Thread-safe
Single-threaded, but a /g regex object carries lastIndex state — do not share one across calls

FAQ

Because a string pattern means exactly one replacement — that is the specified behaviour, not a bug. Use replaceAll for plain text, or a regex with the g flag. This has caught nearly everyone at least once.

'a-b-c'.replace('-', '+');      // 'a+b-c'
'a-b-c'.replaceAll('-', '+');   // 'a+b+c'

History

ES3
replace added with string and RegExp patterns and $-substitution.
ES2015
Symbol.replace made the behaviour customisable by pattern objects.
ES2018
Named capture groups added, usable as $<name> in the replacement.
ES2021
replaceAll added, removing the need for a /g regex on literal text.