How to escape a string for a regular expression

By

Escape a string with the JavaScript RegExp.escape rules so a regular expression matches it as plain text, including dots, dashes, and a leading digit.

Free toolThis tutorial uses the RegExp Escape. It runs in your browser, so open it in another tab and follow along.

A regular expression is a small program. Most of its characters mean “match this text”. A handful of them mean something else. . means any character. * means “the thing before me, any number of times”. ( starts a group. If you paste a file name, a URL, or a user string into a pattern without escaping it, those characters stop being text and start being instructions.

Escaping is the step that turns a string back into text. You want the pattern to match those exact characters, and nothing the engine would otherwise invent. The RegExp escape tool does that with the rules of RegExp.escape, the method in the ECMAScript spec. You paste the literal. You copy the pattern. You pass it to new RegExp.

The method has been in the spec since the 2025 edition. Older engines do not have it as a built-in. The algorithm is small, and it is the one this page implements, so the output matches what a current engine prints. A replace that only puts a backslash in front of . and * gets the easy cases wrong. A hyphen written as \- is a syntax error in unicode mode. A leading digit can be swallowed by a \1 that sits in front of the escaped text. Those two cases are why the spec is pickier than the usual one-liner.

Escape a string for a regular expressionA literal string goes through RegExp.escape, becomes a pattern, and then new RegExp matches that text and nothing else.Literal stringRegExp.escapePatternnew RegExp(pattern)Matches the literal

What the algorithm actually does

The spec walks the string one code point at a time. A few characters get a backslash. The rest get a \x or \u escape, or they stay as themselves.

The syntax characters are ^ $ \ . * + ? ( ) [ ] { } |. Each one is written with a backslash in front. / is not a syntax character inside a pattern, but it is escaped too, so the result is safe inside a /pattern/ literal.

Five controls have a short name. Tab is \t. Line feed is \n. Vertical tab is \v. Form feed is \f. Carriage return is \r.

A second set of punctuation is not escaped with a backslash. It is written as \x plus two hex digits. That set is , - = < > # & ! % : ; @ ~ and the two quote marks, plus a backtick. The hyphen is in there on purpose. \- is not a legal escape in unicode mode, and new RegExp uses unicode mode when you pass the u or v flag. Writing the hyphen as \x2d works in every mode.

White space, line endings, and a lone surrogate are written as \xHH when the code unit is 255 or below, and as \uXXXX when it is higher. The hex is lowercase. An ordinary letter, an ordinary digit that is not in the first position, and a character such as é stay as themselves.

The first character is the special case. If it is a digit or an ASCII letter, it is always a \x escape, even though the same character later in the string would be left alone. A digit at the start would otherwise extend a \1 or a \0 written in front of this pattern. A letter at the start would otherwise extend a \c, which is how a pattern writes a control character. The spec closes that hole by making the first character impossible to join onto.

A file name with a dot

Paste foo.bar. The tool prints:

Escaped: \x66oo\.bar
Source: foo.bar
new RegExp("\\x66oo\\.bar")

The first character is f. f is an ASCII letter, so it becomes \x66, which is the hex for that letter. The rest of foo stays. The dot is a syntax character, so it becomes \.. bar stays.

The note under the output says this, word for word:

The first character is an ASCII letter, so it is written as \x. That keeps it from extending a \c in front of it.

The third line is the JavaScript you would paste. The doubled backslashes are the string literal. Once JavaScript reads that string, the pattern is \x66oo\.bar.

new RegExp("\\x66oo\\.bar") matches foo.bar. The same pattern does not match fooXbar. An unescaped foo.bar would match fooXbar, because the dot matches the X. That is the whole point of the escape.

A price, and a leading digit

Paste 10.5.

Escaped: \x310\.5
Source: 10.5
new RegExp("\\x310\\.5")

The first character is a digit, so it is written as \x. That keeps it from extending a \0 or a \1 in front of it.

1 becomes \x31. The 0 is not in the first position, so it stays a 0. The dot becomes \.. The 5 stays. The pattern reads as the characters 1, 0, ., 5, in that order.

If you had written 1 as itself and then concatenated this pattern onto \1, the engine would see \11 or \10 and treat it as a backreference. The \x form cannot be extended that way. The digit is already closed.

A URL

Paste https://flaviocopes.com.

Escaped: \x68ttps\x3a\/\/flaviocopes\.com
Source: https://flaviocopes.com
new RegExp("\\x68ttps\\x3a\\/\\/flaviocopes\\.com")

h is an ASCII letter in the first position, so it is \x68. The colon is in the punctuation set, so it is \x3a, not \:. Each slash is a syntax-adjacent /, so each one is \/. The dot in .com is \.. The letters in the middle stay letters.

A pattern of https://flaviocopes.com with no escapes is a syntax error or a match against far more than that host, depending on the flags. The escaped form matches the host and the scheme, and it matches nothing that merely looks similar.

Parentheses, a hyphen, and a newline

Paste (foo).

Escaped: \(foo\)
Source: (foo)
new RegExp("\\(foo\\)")

There is no note. ( and ) are syntax characters, so each gets a backslash. The first character is (, which is neither a digit nor an ASCII letter, so the leading-character rule does not fire. The pattern matches the four characters (foo) and does not open a group.

Paste foo-bar.

Escaped: \x66oo\x2dbar
Source: foo-bar
new RegExp("\\x66oo\\x2dbar")

The hyphen is \x2d. It is not \-. That is the unicode-mode case. If you later pass the u flag, \- throws. \x2d does not.

Paste a string that is foo, then a real newline, then bar.

Escaped: \x66oo\nbar
Source: foo
bar
new RegExp("\\x66oo\\nbar")

The source line in that summary contains the newline. The escaped line contains the two characters \ and n. A newline is one of the five controls with a short name. The pattern matches a string that has a line break in that spot. It does not match the two characters backslash and n in the source text.

The empty string

Clear the box. The page shows nothing, because there is no pattern to copy. The function still has an answer:

Escaped: 
Source: 
new RegExp("")

The pattern is empty. new RegExp(”) matches the empty string, and test() returns true for every input.

That second sentence is the part worth keeping. An empty pattern is legal. test() on it returns true for every string, because the empty string is a substring of every string. If your code escapes user input and the user sent nothing, you probably wanted to reject the search, not to match everything.

What the page refuses

The page stops at 100,000 characters. Past that it says: The string is over 100,000 characters. This page stops there.

That limit is the page, not the spec. A real RegExp.escape will take a longer string. A pattern that long is rarely what you wanted to compile, and the page would lock the tab while it walked it.

There is no error for a character the algorithm does not understand. Every code point has a case. A character such as é is left as é. A character above U+00FF that is not white space and not a lone surrogate is left as itself too. The escapes are for the characters that would change meaning, plus the first character, plus white space so the pattern stays on one line when you read it.

When this is the wrong tool

The regex tester answers a different question. You already have a pattern, and you want to see what it matches. Escaping is the step before that, when the text must be literal.

The string escape tool is also a different job. It turns a string into a JavaScript or JSON literal, with quotes and with \n for a newline. That output is a string, not a pattern. If you put it inside new RegExp you have escaped the wrong layer, and the engine will look for backslashes in the text.

Building the pattern by hand is fine when you wrote the pattern. It is the wrong move when any part of the pattern comes from outside the file. A search box, a file name, a domain, a slug. Escape that part, then concatenate it with the operators you meant to write. ^ plus the escaped text plus $ is a full-string match. The escaped text alone is a substring match, which is what RegExp.escape returns.

The JavaScript regular expressions post walks the operators themselves. Read that when you want + and groups and flags. Use this page when a piece of text has to survive those operators unchanged.

One limit to keep in mind. The tool shows the pattern. It does not compile it, and it does not run it against a subject. Copy the third line into the tester, or into your own file, when you want to see a match. The string never leaves the browser, and it is not written into the URL.

Try the RegExp Escape

Escape a string with the JavaScript RegExp.escape rules so a regular expression matches it as literal text. The string stays in the browser.

Open the RegExp Escape →

  • Regex Tester & Explainer: Test regular expressions with live match highlighting and a plain-English breakdown of the pattern.
  • String Escape / Unescape: Escape and unescape JavaScript strings, JSON strings, hex bytes, and Unicode sequences — with a swap button.

More tool tutorials →

Want me to talk about your product? You can sponsor this site.