How to Use a Variable in a Regular Expression (JavaScript, Python, PHP)

Static regex patterns are easy:

const regex = /\d+/;

But what happens when your pattern depends on user input? When the word to match comes from a configuration file? When the search term is typed by a user?

function search(text, term) {
  // How do I use `term` inside a regex?
}

This is where dynamic regex patterns become necessary.

The problem is that inserting variables into regex is surprisingly error-prone. Special characters in the variable can break the pattern, introduce security risks, or create performance issues.

This guide covers how to safely use variables in regex across JavaScript, Python, and PHP, with real production examples.

If you want to test dynamic patterns interactively, the Regex Tester is useful for experimenting.


The Core Problem: Special Characters in Input

When you insert a variable into a regex, the regex engine interprets special characters in the variable as regex syntax.

const term = "hello.world";
const regex = new RegExp(term);

This creates the pattern hello.world, where . matches ANY character, not a literal dot.

The pattern matches helloXworld, hello-world, hello_world —all of which may be unintended.

This is the most common bug in dynamic regex patterns.


JavaScript: Building a RegExp from a Variable

const searchTerm = "foo";
const regex = new RegExp(searchTerm, "gi");

The RegExp constructor takes a string and compiles it into a regex pattern.

Without escaping:

const searchTerm = "(foo)";
const regex = new RegExp(searchTerm);
// Matches "foo" as a capturing group, not literally "(foo)"

JavaScript: Escaping User Input

The safe approach is to escape special characters before inserting:

function escapeRegex(string) {
  return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

const userInput = "hello.world (test)";
const escaped = escapeRegex(userInput);
const regex = new RegExp(escaped, "gi");

console.log(regex.test("hello.world (test)")); // true
console.log(regex.test("helloXworld (test)")); // false

The escape function prepends a backslash to every regex special character.


JavaScript Example: Case-Insensitive Search

function searchInText(text, query) {
  const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  const regex = new RegExp(escaped, "gi");
  return text.match(regex);
}

const text = "Hello World. hello world. HELLO.";
const results = searchInText(text, "hello");
console.log(results); // ["Hello", "hello", "HELLO"]

This is a realistic search function, safe against user input with special characters.


JavaScript Example: Dynamic Replacement

function highlight(text, term) {
  const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  const regex = new RegExp(`(${escaped})`, "gi");
  return text.replace(regex, "<mark>$1</mark>");
}

console.log(highlight("Find foo and foo.bar", "foo"));
// "<mark>foo</mark> and <mark>foo</mark>.bar"

Notice that foo.bar is NOT highlighted —the escaped foo pattern correctly matches only literal foo.


JavaScript: The new RegExp() Pitfall

A common mistake:

const term = "\\d+";
const regex = new RegExp(term);
// This creates /\d+/, not /\\d+/

Because term contains \\d+, the string literal becomes \d+, which is the digit pattern, not a literal backslash followed by "d+".

This double-escaping confusion causes endless bugs.


Python: Building a Regex from a Variable

import re

search_term = "foo"
pattern = re.compile(search_term, re.IGNORECASE)
matches = pattern.findall("Hello foo Foo foo")
print(matches)  # ['foo', 'Foo', 'foo']

Python: Escaping User Input

import re

def escape_regex(text):
    return re.escape(text)

user_input = "hello.world (test)"
escaped = re.escape(user_input)
pattern = re.compile(escaped, re.IGNORECASE)

print(pattern.findall("hello.world (test) says helloXworld"))
# ['hello.world (test)']

Python's re.escape() handles escaping automatically. It is the standard way to use variables in Python regex.


Python Example: Dynamic Search Function

import re

def search_text(text, query):
    escaped = re.escape(query)
    pattern = re.compile(escaped, re.IGNORECASE)
    return pattern.findall(text)

text = "Error: connection failed. error: timeout."
results = search_text(text, "error")
print(results)  # ['Error', 'error']

PHP: Building a Regex from a Variable

$searchTerm = "foo";
$pattern = "/" . preg_quote($searchTerm, "/") . "/i";
$matches = [];
preg_match_all($pattern, "Hello foo Foo foo", $matches);
print_r($matches[0]);
// ['foo', 'Foo', 'foo']

PHP: The Second Parameter of preg_quote

PHP's preg_quote takes an optional second parameter —the delimiter to escape:

$searchTerm = "foo/bar";
$pattern = "/" . preg_quote($searchTerm, "/") . "/";
// Escapes the / inside $searchTerm too

This is easy to forget and causes subtle bugs when the input contains the delimiter.


Common Mistake #1: Forgetting to Escape

This is the most common mistake.

function findMatches(text, term) {
  const regex = new RegExp(term, "gi");  // Bug: term is not escaped
  return text.match(regex);
}

findMatches("foo.bar", "."); // Returns every character

The . becomes a wildcard instead of a literal dot.


Common Mistake #2: Double Escaping

const term = "\\.";
const regex = new RegExp(term);
// This matches a literal dot, because \\ becomes \

But if you pass \\. (already escaped), the regex engine sees \. which matches a literal dot. If you pass \. (single backslash in string), it becomes . (any character).

This backslash counting is the source of countless bugs.

Related reading: Common Regex Mistakes Developers Keep Making

Related reading: \d Is Less Efficient Than [0-9] —Node.js Regex Performance Deep Dive


Common Mistake #3: Building Regex Inside Loops

const terms = ["foo", "bar", "baz"];
const results = [];

terms.forEach(term => {
  const regex = new RegExp(term, "gi");  // New regex each iteration
  const match = text.match(regex);
  if (match) results.push(...match);
});

This works but is inefficient for large datasets. The regex is recompiled every iteration.

Better: compile once, reuse.

const regexes = terms.map(term => new RegExp(escapeRegex(term), "gi"));

Common Mistake #4: Using Variable in Regex Literal

JavaScript does not support interpolation in regex literals:

const term = "foo";
const regex = /term/gi;  // Matches literal "term", not the value of term

You MUST use new RegExp() for dynamic patterns.


Performance: Compile Once, Match Many

Regex compilation has a cost. For patterns that change frequently, the compilation overhead can be significant.

// Bad: recompiles every call
function searchBad(text, term) {
  return text.match(new RegExp(escapeRegex(term), "gi"));
}

// Better: cache compiled regex
const regexCache = new Map();

function searchBetter(text, term) {
  if (!regexCache.has(term)) {
    regexCache.set(term, new RegExp(escapeRegex(term), "gi"));
  }
  return text.match(regexCache.get(term));
}

Python: Caching Compiled Patterns

import re
from functools import lru_cache

@lru_cache(maxsize=100)
def get_pattern(term):
    return re.compile(re.escape(term), re.IGNORECASE)

def search_text(text, term):
    pattern = get_pattern(term)
    return pattern.findall(text)

Security: ReDoS (Regular Expression Denial of Service)

User-supplied regex patterns can cause catastrophic backtracking.

If you allow users to supply the raw pattern (not escaped), they could craft:

const evilPattern = "(a+)+b";
const regex = new RegExp(evilPattern);
regex.test("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
// CPU spike —possible denial of service

Never pass unsanitized user input directly to new RegExp().

Related reading: Regex Catastrophic Backtracking —How to Fix Regex That Freezes Your App


When to Escape vs When to Not

Use CaseEscape?
User search inputAlways escape
Config file valuesAlways escape
Hardcoded variable with known safe contentNot needed
Input that should be regex syntaxDo not escape

Sometimes you intentionally want the variable to contain regex syntax:

const pattern = "\\d{3,5}";  // Intended as regex
const regex = new RegExp(pattern);

In that case, you do NOT escape.


FAQ

How do I use a variable inside a regex in JavaScript?

Use new RegExp(variable, "flags"). Escape the variable with string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") to avoid special character issues.


How do I use a variable inside a regex in Python?

Use re.compile(re.escape(variable), flags) or re.search(re.escape(variable), text).


How do I use a variable inside a regex in PHP?

Use preg_quote($variable, $delimiter) and embed it in the pattern string: "/" . preg_quote($var, "/") . "/".


What is re.escape() in Python?

re.escape() escapes all special regex characters in a string so it can be used safely in a pattern.


Can I escape regex in JavaScript?

JavaScript does not have a built-in RegExp.escape(), but you can use:

string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")

What happens if I do not escape user input?

Special characters in the input will be interpreted as regex syntax, causing incorrect matches and potential security vulnerabilities.


Can I build a regex from a variable using a regex literal?

No. Regex literals in JavaScript (/pattern/) are static. Use new RegExp() for dynamic patterns.


Final Thoughts

Using variables in regex patterns is something every developer eventually needs to do.

The pattern is always the same:

  1. Escape the input for special regex characters
  2. Build the pattern using new RegExp() (JS), re.compile() (Python), or preg_quote() (PHP)
  3. Test against edge cases that include regex special characters

Most regex-related bugs in production come from forgetting step 1. It is easy to miss because test data rarely includes dots, parentheses, or backslashes. Real-world data always does.

Build the escaping habit early, and dynamic regex patterns will stop surprising you.

If you want to test how dynamic patterns behave with different inputs, the Regex Tester makes it easy to experiment interactively.

You may also find these related developer tools useful: