RegExp.escape()
Escapes a string so it can be embedded in a regular expression as a literal pattern.
RegExp.escape(string)- Added in JavaScript Baseline 2025 (newly available May 2025); specified in the ECMAScript 2027 draft
Parameters
| Parameter | Type | Description |
|---|---|---|
$string | string | The string to escape. It is not coerced — a non-string argument throws. |
Return value
stringA new string with regex syntax characters escaped, safe to use as a literal pattern for the RegExp() constructor.
Errors
Throws a TypeError if string is not a string; no type coercion is performed.
Changelog
- PHP Baseline 2025Newly available across major browsers in May 2025; older engines and runtimes need a manual escape helper or a polyfill.
RegExp.escape() takes arbitrary text and returns a pattern that matches exactly that text. Reach for it any time part of a regex comes from somewhere you do not control — a search box, a config file, a filename, a domain name — and you want it treated as literal characters rather than syntax.
Why it matters
Without escaping, a user typing c++ makes new RegExp(input) throw a SyntaxError ("nothing to repeat"), and a user typing .* silently matches everything. Escaping removes both failure modes.
The output looks strange, and that is fine
The method does more than prefix backslashes. The first character, if it is a letter or digit, is emitted as a hex escape such as \x63, spaces become \x20, punctuation like - and = becomes \x2d and \x3d, and lone surrogates become \uXXXX. That first-character rule exists so the result stays safe when you concatenate it directly after an incomplete escape sequence such as \1, \x0 or \u000, where a leading letter or digit would otherwise be swallowed into that escape. The pattern still matches the original text exactly.
Scope
It escapes for regex syntax only. It is not an HTML, SQL, shell or URL escape, and it does not make a pattern safe against catastrophic backtracking in the parts you wrote yourself.
Example
When to use it
- Build a highlight regex from whatever the user typed into a search field.
- Match a literal domain, file path or version string that contains dots and slashes.
- Replace every occurrence of a configurable delimiter without it being read as syntax.
Frequently asked questions
Why does RegExp.escape add \\x codes to ordinary letters and spaces?
The specification escapes the first character when it is a letter or digit, plus spaces and several punctuation marks, so the result stays safe wherever you concatenate it into a larger pattern — for example directly after \\x or inside a character class. The hex escapes match the exact same characters, so behaviour is unchanged; only the printed pattern looks different.
What do I use if RegExp.escape is not available?
Use string.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\\\$&"), which escapes the standard regex metacharacters and covers the common cases. It misses edge cases the built-in handles, such as lone surrogates and concatenation-sensitive leading characters, so feature-detect and prefer RegExp.escape when the engine has it.
Does RegExp.escape make user input safe?
It makes input safe as regex syntax only — the string can no longer inject metacharacters or throw a SyntaxError. It does not sanitize for HTML, SQL, shell commands or URLs, and it does not prevent catastrophic backtracking caused by quantifiers in the surrounding pattern you wrote.
More examples
Fallback for engines without RegExp.escape
The feature test short-circuits, so on any engine that has RegExp.escape the first log prints the built-in's output, \x63\+\+\x20\(3\.0\) — hex escapes and all. Only on an older engine does the manual branch run and print the more readable c\+\+ \(3\.0\), because it escapes just the classic metacharacters. Either way the two tests print true and false: the literal "1+1=2" matches itself, and the escaped dot in "a.c" no longer matches "abc". The fallback misses lone surrogates, the leading-character rule and punctuation such as - and =, so prefer the built-in where available.
function escapeRegExp(string) {
if (typeof RegExp.escape === "function") return RegExp.escape(string);
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
console.log(escapeRegExp("c++ (3.0)"));
console.log(new RegExp(escapeRegExp("1+1=2")).test("1+1=2"));
console.log(new RegExp(escapeRegExp("a.c")).test("abc"));Highlighting a user-supplied search term
The first call finds only the exact price and leaves $500 alone, because the dollar sign and dot were escaped rather than read as an anchor and a wildcard. The second call brackets just the single literal period, which an unescaped "." would have turned into every character in the string.
function highlight(text, query) {
if (!query) return text;
const re = new RegExp(RegExp.escape(query), "gi");
return text.replace(re, (m) => `[${m}]`);
}
console.log(highlight("Prices: $5.00 and $500", "$5.00"));
console.log(highlight("Prices: $5.00 and $500", "."));Splitting on a configurable delimiter
Escaping turns the pipe characters into literals, so the row splits into three fields instead of matching an empty alternation. The second block prints TypeError: the argument is not coerced, so a number must be converted with String() first.
const delimiter = "||";
const row = "alpha||beta||gamma";
const re = new RegExp(RegExp.escape(delimiter), "g");
console.log(row.split(re));
// Non-strings are rejected outright.
try {
RegExp.escape(42);
} catch (err) {
console.log(err.name);
}
Discussion