JavaScript String.prototype.replace() and replaceAll() are two of the most frequently used methods for text manipulation. Combined with regular expressions, they unlock powerful pattern-based transformations: capture groups, named groups, callback functions, and more. This guide covers everything from basics to 15 production-ready examples you can copy-paste today.
Test your regex patterns live in our Regex Tester →
1. replace() vs replaceAll() — Key Difference
The fundamental difference: replace() only replaces the first match when given a string argument, while replaceAll() replaces every occurrence. When using a regex with the /g flag, both behave identically.
// replace() — only replaces the FIRST match with a string argument
const str = 'foo bar foo baz foo';
console.log(str.replace('foo', 'qux'));
// Output: "qux bar foo baz foo"
// replaceAll() — replaces EVERY match (ES2021)
console.log(str.replaceAll('foo', 'qux'));
// Output: "qux bar qux baz qux"
// replace() with regex /g — also replaces every match
console.log(str.replace(/foo/g, 'qux'));
// Output: "qux bar qux baz qux"// Quick comparison table:
// ┌──────────────────────────────┬────────────────────┬──────────────────┐
// │ Method │ String arg │ Regex /g arg │
// ├──────────────────────────────┼────────────────────┼──────────────────┤
// │ str.replace(search, rep) │ First match only │ All matches │
// │ str.replaceAll(search, rep) │ All matches │ All matches │
// └──────────────────────────────┴────────────────────┴──────────────────┘2. Basic Regex Replace: The /g Flag
To replace all occurrences with replace(), pass a regex with the global (g) flag. This has been the standard approach since ES3 and works in every JavaScript environment.
// Replace all digits with '#'
const masked = 'Order 12345, ID 67890'.replace(/\d/g, '#');
console.log(masked);
// Output: "Order #####, ID #####"
// Replace all vowels (case-insensitive with 'i' flag)
const noVowels = 'Hello World'.replace(/[aeiou]/gi, '_');
console.log(noVowels);
// Output: "H_ll_ W_rld"
// Replace all whitespace sequences with a single space
const cleaned = 'too many spaces here'.replace(/\s+/g, ' ');
console.log(cleaned);
// Output: "too many spaces here"
// Remove all non-alphanumeric characters
const alphaOnly = 'Hello, World! @2024'.replace(/[^a-zA-Z0-9]/g, '');
console.log(alphaOnly);
// Output: "HelloWorld2024"
// Case-insensitive replacement
const result = 'Hello HELLO hello'.replace(/hello/gi, 'hi');
console.log(result);
// Output: "hi hi hi"3. Capture Groups: $1, $2 in Replacement String
Capture groups (...) let you reference matched sub-expressions in the replacement string using $1, $2, etc. The entire match is available as $&.
// Swap first and last name using $1 and $2
const name = 'John Smith';
const swapped = name.replace(/(\w+) (\w+)/, '$2, $1');
console.log(swapped);
// Output: "Smith, John"
// Reformat a date: MM/DD/YYYY → YYYY-MM-DD
const date = '12/25/2024';
const isoDate = date.replace(/(\d{2})\/(\d{2})\/(\d{4})/, '$3-$1-$2');
console.log(isoDate);
// Output: "2024-12-25"
// Wrap each word in <strong> tags using $&
const bold = 'hello world'.replace(/\w+/g, '<strong>$&</strong>');
console.log(bold);
// Output: "<strong>hello</strong> <strong>world</strong>"
// Use $` (before match) and $' (after match)
const demo = 'abc-def-ghi'.replace(/-def-/, '[$`|$&|$\']');
console.log(demo);
// Output: "abc[abc|-def-|ghi]ghi"
// Multiple capture groups: restructure a log entry
const log = '[ERROR] 2024-01-15 Connection timeout';
const parsed = log.replace(
/\[(\w+)\] (\d{4}-\d{2}-\d{2}) (.+)/,
'Date: $2 | Level: $1 | Message: $3'
);
console.log(parsed);
// Output: "Date: 2024-01-15 | Level: ERROR | Message: Connection timeout"4. Named Capture Groups: (?<name>...)
Named capture groups (ES2018) improve readability by assigning a name to each group. Reference them in the replacement string with $<name>.
// Named capture groups with $<name> syntax (ES2018+)
const dateStr = '2024-12-25';
const formatted = dateStr.replace(
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
'$<month>/$<day>/$<year>'
);
console.log(formatted);
// Output: "12/25/2024"
// Named groups in a callback function
const url = 'https://example.com:8080/path';
const parts = url.replace(
/(?<protocol>https?):\/\/(?<host>[^:]+):(?<port>\d+)/,
(match, _p1, _p2, _p3, _offset, _str, groups) => {
return `Protocol=${groups.protocol} Host=${groups.host} Port=${groups.port}`;
}
);
console.log(parts);
// Output: "Protocol=https Host=example.com Port=8080/path"
// Combining named and numbered groups
const input = 'John Doe, age 30';
const output = input.replace(
/(?<first>\w+) (?<last>\w+), age (?<age>\d+)/,
'$<last>, $<first> (age: $<age>)'
);
console.log(output);
// Output: "Doe, John (age: 30)"5. Callback Function: replace(regex, fn)
For dynamic replacements, pass a function as the second argument. The callback receives the full match, each capture group, the offset, and the original string.
// Callback signature:
// replace(regex, (match, p1, p2, ..., offset, string, groups) => newString)
// Uppercase every word
const title = 'the quick brown fox'.replace(
/\b\w/g,
(char) => char.toUpperCase()
);
console.log(title);
// Output: "The Quick Brown Fox"
// Double every number found in a string
const doubled = 'I have 3 cats and 12 fish'.replace(
/\d+/g,
(match) => String(Number(match) * 2)
);
console.log(doubled);
// Output: "I have 6 cats and 24 fish"
// Convert hex color to rgb
const hexToRgb = '#ff8800'.replace(
/#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})/i,
(_match, r, g, b) => {
return `rgb(${parseInt(r, 16)}, ${parseInt(g, 16)}, ${parseInt(b, 16)})`;
}
);
console.log(hexToRgb);
// Output: "rgb(255, 136, 0)"
// Use offset parameter: show match positions
const positions: string[] = [];
'abcabc'.replace(/b/g, (match, offset) => {
positions.push(`"${match}" at index ${offset}`);
return match;
});
console.log(positions);
// Output: ['"b" at index 1', '"b" at index 4']
// Conditional replacement based on capture groups
const env = 'DB_HOST=localhost DB_PORT=5432 DB_PASS=secret123';
const redacted = env.replace(
/(\w+)=(\S+)/g,
(_match, key, value) => {
return key.includes('PASS') ? `${key}=****` : `${key}=${value}`;
}
);
console.log(redacted);
// Output: "DB_HOST=localhost DB_PORT=5432 DB_PASS=****"6. 15 Practical Examples
Each example below is a self-contained, runnable snippet. Copy-paste into your browser console or Node.js REPL to test immediately.
Example 1: camelCase to kebab-case
function camelToKebab(str) {
return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
}
console.log(camelToKebab('backgroundColor')); // "background-color"
console.log(camelToKebab('fontSize')); // "font-size"
console.log(camelToKebab('borderTopWidth')); // "border-top-width"
console.log(camelToKebab('XMLHttpRequest')); // "xmlhttp-request"Example 2: Remove HTML Tags
function stripHtml(html) {
return html.replace(/<[^>]*>/g, '');
}
console.log(stripHtml('<p>Hello <b>world</b></p>'));
// Output: "Hello world"
console.log(stripHtml('<div class="test">Content <br/> here</div>'));
// Output: "Content here"
// Also remove &entities;
function stripHtmlFull(html) {
return html
.replace(/<[^>]*>/g, '')
.replace(/&[a-zA-Z]+;/g, ' ')
.replace(/\s+/g, ' ')
.trim();
}
console.log(stripHtmlFull('<p>Price: $100&up</p>'));
// Output: "Price: $100 up"Example 3: Mask Email Address
function maskEmail(email) {
return email.replace(
/^(.)(.*)(@.+)$/,
(_match, first, middle, domain) => {
return first + '*'.repeat(middle.length) + domain;
}
);
}
console.log(maskEmail('alice@example.com')); // "a****@example.com"
console.log(maskEmail('bob@gmail.com')); // "b**@gmail.com"
console.log(maskEmail('charlie@company.io')); // "c******@company.io"Example 4: Format Phone Number
function formatPhone(phone) {
// Strip all non-digits first
const digits = phone.replace(/\D/g, '');
// Format as (xxx) xxx-xxxx
return digits.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
}
console.log(formatPhone('1234567890')); // "(123) 456-7890"
console.log(formatPhone('123-456-7890')); // "(123) 456-7890"
console.log(formatPhone('(123) 456 7890')); // "(123) 456-7890"
console.log(formatPhone('+1-234-567-8901')); // "(123) 456-7890" (first digit stripped)
// International format variant
function formatPhoneIntl(phone) {
const digits = phone.replace(/\D/g, '');
return digits.replace(/(\d{1})(\d{3})(\d{3})(\d{4})/, '+$1 ($2) $3-$4');
}
console.log(formatPhoneIntl('12345678901')); // "+1 (234) 567-8901"Example 5: Trim Extra Whitespace
function trimWhitespace(str) {
return str
.replace(/^\s+|\s+$/g, '') // trim leading & trailing
.replace(/\s+/g, ' '); // collapse internal spaces
}
console.log(trimWhitespace(' hello world '));
// Output: "hello world"
console.log(trimWhitespace('\t line1 \n line2 \t'));
// Output: "line1 line2"
// Remove blank lines from multiline text
function removeBlankLines(text) {
return text.replace(/^\s*\n/gm, '');
}
console.log(removeBlankLines('line1\n\n\nline2\n\nline3'));
// Output: "line1\nline2\nline3"Example 6: Slugify a Title
function slugify(title) {
return title
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '') // remove special chars
.replace(/\s+/g, '-') // spaces to hyphens
.replace(/-+/g, '-') // collapse multiple hyphens
.replace(/^-|-$/g, ''); // trim leading/trailing hyphens
}
console.log(slugify('Hello World!'));
// Output: "hello-world"
console.log(slugify(' JavaScript String Replace --- with Regex! '));
// Output: "javascript-string-replace-with-regex"
console.log(slugify('10 Tips & Tricks for React.js'));
// Output: "10-tips--tricks-for-reactjs"Example 7: Capitalize Each Word (Title Case)
function titleCase(str) {
return str
.toLowerCase()
.replace(/\b\w/g, (char) => char.toUpperCase());
}
console.log(titleCase('hello world')); // "Hello World"
console.log(titleCase('JAVASCRIPT IS FUN')); // "Javascript Is Fun"
console.log(titleCase('the quick brown fox')); // "The Quick Brown Fox"
// Smarter version: skip small words (a, an, the, in, on, etc.)
function smartTitleCase(str) {
const small = new Set(['a', 'an', 'the', 'in', 'on', 'at', 'to', 'for', 'of', 'and', 'but', 'or']);
return str
.toLowerCase()
.replace(/\b\w+/g, (word, index) => {
if (index > 0 && small.has(word)) return word;
return word.charAt(0).toUpperCase() + word.slice(1);
});
}
console.log(smartTitleCase('the lord of the rings'));
// Output: "The Lord of the Rings"Example 8: Swap Two Words
function swapWords(str, word1, word2) {
const regex = new RegExp(`\\b(${word1}|${word2})\\b`, 'gi');
return str.replace(regex, (match) => {
return match.toLowerCase() === word1.toLowerCase() ? word2 : word1;
});
}
console.log(swapWords('Hello World', 'Hello', 'World'));
// Output: "World Hello"
console.log(swapWords('left is right and right is left', 'left', 'right'));
// Output: "right is left and left is right"
// Swap using capture groups only (no function needed)
const swapped = 'foo bar'.replace(/(\w+) (\w+)/, '$2 $1');
console.log(swapped);
// Output: "bar foo"Example 9: Remove Consecutive Duplicate Words
function removeDuplicateWords(str) {
return str.replace(/\b(\w+)\s+\1\b/gi, '$1');
}
console.log(removeDuplicateWords('the the quick brown fox'));
// Output: "the quick brown fox"
console.log(removeDuplicateWords('hello hello world world'));
// Output: "hello world"
console.log(removeDuplicateWords('I I am am very very happy'));
// Output: "I am very happy"
// Remove all consecutive duplicates (even more than 2)
function removeAllDuplicateWords(str) {
return str.replace(/\b(\w+)(?:\s+\1)+\b/gi, '$1');
}
console.log(removeAllDuplicateWords('no no no no duplicates'));
// Output: "no duplicates"Example 10: Add Commas to Numbers
function addCommas(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
console.log(addCommas(1234567)); // "1,234,567"
console.log(addCommas(1000)); // "1,000"
console.log(addCommas(100)); // "100"
console.log(addCommas(1234567890)); // "1,234,567,890"
// Handle decimals too
function addCommasDecimal(numStr) {
const [integer, decimal] = numStr.split('.');
const formatted = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return decimal ? `${formatted}.${decimal}` : formatted;
}
console.log(addCommasDecimal('1234567.89')); // "1,234,567.89"
console.log(addCommasDecimal('1000000.5')); // "1,000,000.5"Example 11: Extract Domain from URL
function extractDomain(url) {
return url.replace(/^(?:https?:\/\/)?(?:www\.)?([^\/:]+).*$/, '$1');
}
console.log(extractDomain('https://www.example.com/path'));
// Output: "example.com"
console.log(extractDomain('http://blog.example.com:8080/post'));
// Output: "blog.example.com"
console.log(extractDomain('https://sub.domain.co.uk/page?q=1'));
// Output: "sub.domain.co.uk"
// Extract just the root domain (without subdomains)
function extractRootDomain(url) {
const domain = url.replace(/^(?:https?:\/\/)?(?:www\.)?([^\/:]+).*$/, '$1');
const parts = domain.split('.');
return parts.slice(-2).join('.');
}
console.log(extractRootDomain('https://blog.shop.example.com'));
// Output: "example.com"Example 12: Replace Only the Nth Occurrence
function replaceNth(str, pattern, replacement, n) {
let count = 0;
return str.replace(new RegExp(pattern, 'g'), (match) => {
count++;
return count === n ? replacement : match;
});
}
console.log(replaceNth('a-b-c-d-e', '-', ' | ', 3));
// Output: "a-b-c | d-e"
console.log(replaceNth('foo bar foo baz foo', 'foo', 'QUX', 2));
// Output: "foo bar QUX baz foo"
// Replace every Nth occurrence
function replaceEveryNth(str, pattern, replacement, n) {
let count = 0;
return str.replace(new RegExp(pattern, 'g'), (match) => {
count++;
return count % n === 0 ? replacement : match;
});
}
console.log(replaceEveryNth('a,b,c,d,e,f', ',', ' | ', 2));
// Output: "a,b | c,d | e,f"Example 13: Convert Template Variables
function interpolate(template, data) {
return template.replace(
/\{\{\s*(\w+)\s*\}\}/g,
(_match, key) => data[key] ?? `{{${key}}}`
);
}
const tpl = 'Hello {{ name }}, welcome to {{ city }}!';
const data = { name: 'Alice', city: 'Tokyo' };
console.log(interpolate(tpl, data));
// Output: "Hello Alice, welcome to Tokyo!"
// With missing key fallback
console.log(interpolate('Hi {{name}}, your role is {{role}}', { name: 'Bob' }));
// Output: "Hi Bob, your role is {{role}}"
// Dollar-sign template syntax
function interpolateDollar(template, data) {
return template.replace(
/\$\{(\w+)\}/g,
(_match, key) => data[key] ?? ''
);
}
console.log(interpolateDollar('User: ${user}, ID: ${id}', { user: 'admin', id: '42' }));
// Output: "User: admin, ID: 42"Example 14: Escape HTML Entities
function escapeHtml(str) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
};
return str.replace(/[&<>"']/g, (char) => map[char]);
}
console.log(escapeHtml('<script>alert("XSS")</script>'));
// Output: "<script>alert("XSS")</script>"
console.log(escapeHtml('Price: $5 < $10 & "free" > 'nothing''));
// Output: "Price: $5 < $10 & "free" > 'nothing'"
// Reverse: unescape HTML entities
function unescapeHtml(str) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
''': "'",
};
return str.replace(/&(?:amp|lt|gt|quot|#039);/g, (entity) => map[entity]);
}
console.log(unescapeHtml('<p>Hello</p>'));
// Output: "<p>Hello</p>"Example 15: Censor Profanity with Asterisks
function censorWords(text, wordList) {
const pattern = new RegExp(
'\\b(' + wordList.join('|') + ')\\b',
'gi'
);
return text.replace(pattern, (match) => '*'.repeat(match.length));
}
console.log(censorWords('This is a damn bad example', ['damn', 'bad']));
// Output: "This is a **** *** example"
console.log(censorWords('Foo and Bar walked into a baz', ['foo', 'bar', 'baz']));
// Output: "*** and *** walked into a ***"
// Partial censoring: keep first and last letter
function partialCensor(text, wordList) {
const pattern = new RegExp('\\b(' + wordList.join('|') + ')\\b', 'gi');
return text.replace(pattern, (match) => {
if (match.length <= 2) return '*'.repeat(match.length);
return match[0] + '*'.repeat(match.length - 2) + match[match.length - 1];
});
}
console.log(partialCensor('That was a damn bad move', ['damn', 'bad']));
// Output: "That was a d**n b*d move"7. replaceAll with String vs Regex
replaceAll() accepts both a string and a regex. When using a regex, the g flag is required, otherwise a TypeError is thrown. With a plain string, no special syntax is needed.
// ✅ replaceAll with a string — simple and effective
const str1 = 'foo.bar.baz';
console.log(str1.replaceAll('.', '/'));
// Output: "foo/bar/baz"
// ✅ replaceAll with regex + /g flag — works fine
const str2 = 'foo123bar456baz';
console.log(str2.replaceAll(/\d+/g, '#'));
// Output: "foo#bar#baz"
// ❌ replaceAll with regex WITHOUT /g — throws TypeError!
try {
'hello'.replaceAll(/hello/, 'world');
} catch (e) {
console.log(e.message);
// "String.prototype.replaceAll called with a non-global RegExp argument"
}
// String replaceAll does NOT interpret regex special chars
const str3 = 'price is $100.00 (USD)';
console.log(str3.replaceAll('$100.00', '€85.50'));
// Output: "price is €85.50 (USD)"
// No need to escape $ or . — they are treated as literal characters
// But replace() with string also only matches literally
const str4 = 'a.b.c';
console.log(str4.replace('.', '-')); // "a-b.c" (first only)
console.log(str4.replaceAll('.', '-')); // "a-b-c" (all)8. Performance Tips
String replacement with regex is fast for most use cases, but there are pitfalls to avoid in performance-critical code.
- Pre-compile regex outside loops: Creating a
new RegExp()inside a loop is wasteful. Declare it once and reuse it. - Avoid catastrophic backtracking: Patterns like
(a+)+bcan cause exponential execution time on non-matching input. Use atomic-style patterns or be specific with character classes. - Use string methods when possible: If you are replacing a literal string (no pattern),
replaceAll(string, string)is faster than a regex. - Prefer non-capturing groups: Use
(?:...)instead of(...)when you do not need backreferences. This reduces memory overhead. - Benchmark with realistic data: Use
performance.now()to measure replacement time on actual input sizes.
// ❌ Bad: regex created inside loop
function processItemsBad(items) {
return items.map(item => item.replace(new RegExp('\\s+', 'g'), '-'));
}
// ✅ Good: regex pre-compiled outside loop
const WHITESPACE_RE = /\s+/g;
function processItemsGood(items) {
return items.map(item => item.replace(WHITESPACE_RE, '-'));
}
// ⚠️ Catastrophic backtracking example
// This pattern can hang the browser on non-matching input:
// const badRegex = /^(a+)+b$/;
// badRegex.test('aaaaaaaaaaaaaaaaaaaaaaaaaac'); // extremely slow!
// ✅ Safe equivalent:
const goodRegex = /^a+b$/;
// Benchmark template
function benchmark(fn, iterations = 100000) {
const start = performance.now();
for (let i = 0; i < iterations; i++) fn();
const end = performance.now();
console.log(`${iterations} iterations: ${(end - start).toFixed(2)}ms`);
}
// Compare string vs regex performance
const testStr = 'hello world hello world hello world';
benchmark(() => testStr.replaceAll('hello', 'hi')); // string method
benchmark(() => testStr.replace(/hello/g, 'hi')); // regex method9. TypeScript Types for replace
TypeScript fully supports replace() and replaceAll() with proper type inference. Here are the key type signatures and patterns for type-safe replacements.
// TypeScript signatures for replace and replaceAll:
//
// interface String {
// replace(searchValue: string | RegExp, replaceValue: string): string;
// replace(searchValue: string | RegExp,
// replacer: (substring: string, ...args: any[]) => string): string;
// replaceAll(searchValue: string | RegExp, replaceValue: string): string;
// replaceAll(searchValue: string | RegExp,
// replacer: (substring: string, ...args: any[]) => string): string;
// }
// Type-safe replacement function
function safeReplace(
input: string,
pattern: string | RegExp,
replacement: string
): string {
return input.replace(pattern, replacement);
}
// Type-safe callback replacement
function mapReplace(
input: string,
pattern: RegExp,
fn: (match: string, ...groups: string[]) => string
): string {
return input.replace(pattern, fn);
}
// Usage examples
const result1: string = safeReplace('hello world', /world/g, 'TypeScript');
console.log(result1); // "hello TypeScript"
const result2: string = mapReplace('3 + 5 = 8', /\d+/g, (match) => {
return String(Number(match) * 10);
});
console.log(result2); // "30 + 50 = 80"
// Generic template literal type (TypeScript 4.1+)
type Replace<
S extends string,
From extends string,
To extends string
> = S extends `${infer Before}${From}${infer After}`
? `${Before}${To}${After}`
: S;
// Compile-time string replacement:
type Result = Replace<'Hello World', 'World', 'TS'>;
// type Result = "Hello TS"10. Frequently Asked Questions
What is the difference between replace() and replaceAll() in JavaScript?
replace() with a string argument replaces only the first occurrence. replaceAll() replaces every occurrence. When using a regex with the /g flag, both methods produce the same result. replaceAll() was introduced in ES2021 and requires the /g flag when given a regex argument.
How do I use capture groups in JavaScript replace?
Wrap part of your regex in parentheses to create a capture group: /(\w+)@(\w+)/. In the replacement string, use $1 for the first group, $2 for the second, and so on. The full match is available as $&. In a callback function, capture groups are passed as the second, third, etc. parameters.
Can I use a function as the replacement in replace()?
Yes. Pass a function as the second argument: str.replace(/pattern/g, (match, p1, p2, offset, string) => { ... }). The function is called for every match and its return value is used as the replacement. This is the most powerful form of replace() and is essential for dynamic transformations.
Why does replaceAll() throw a TypeError with regex?
replaceAll() requires the /g (global) flag when given a regex argument. If you pass a regex without /g, JavaScript throws a TypeError: "String.prototype.replaceAll called with a non-global RegExp argument". This is by design to prevent accidental single-replacement behavior.
How do I replace a string in JavaScript without regex?
Use replaceAll(searchString, newString) for replacing all occurrences of a literal string, or split(search).join(replacement) as a fallback for older environments. For a single replacement, use replace(searchString, newString). These methods do not interpret special regex characters, so they are safer for user-supplied input.
Bookmark this guide for your next string manipulation task. For interactive regex testing, try our tool below.