Skip to content

Readable Regular Expressions

When to use

Use this when a regex is too complex to read as a single literal. The regexr package provides a tagged template function that lets you build regular expressions from named, composable pieces — no double-escaping, no builder classes, just template literals and interpolation.

The pattern

Use the re tagged template to write regex fragments as template strings. Interpolate RegExp instances or other re-tagged strings to compose larger patterns. Use the escape helper to safely include user input.

Composing patterns

Name each part of a complex regex, then interpolate them into a final pattern. Each re-tagged template returns a RegExp that can be interpolated into another re template or passed to new RegExp() to add flags:

javascript
import { r as re } from "regexr";

const useDBStatement = re`^USE .*;$`;
const createDBStatement = re`^CREATE DATABASE .*;$`;
const pattern = re`${useDBStatement}|${createDBStatement}`;

const regex = new RegExp(pattern, "gm");

Breaking down a complex pattern

Give each piece a descriptive name. The template literal reads like documentation — each variable explains what that part of the regex matches:

javascript
import { r as re } from "regexr";

const year = re`\d{4}`;
const month = re`\d{2}`;
const day = re`\d{2}`;
const date = re`^${year}-${month}-${day}$`;

date.test("2025-01-15"); // true
date.test("25-1-5");     // false

Mixing regex literals and tagged templates

Interpolate existing RegExp instances directly into re-tagged templates. This lets you reuse patterns defined as literals:

javascript
import { r as re } from "regexr";

const protocol = /https?/;
const domain = /[\w-]+(\.[\w-]+)+/;
const url = re`^${protocol}://${domain}$`;

url.test("https://example.com"); // true

Escaping user input

Use the escape helper to escape strings that may contain regex-special characters so they match literally:

javascript
import { r as re, escape } from "regexr";

const searchTerm = "$10.00 (USD)";
const pattern = re`value: ${escape(searchTerm)}`;

// matches literal "$10.00 (USD)" instead of
// treating $, ., and () as regex operators

Adding flags

The re tag returns a RegExp without flags. Wrap it in new RegExp() to add flags like g, m, or i:

javascript
import { r as re } from "regexr";

const comment = re`^\s*#.*$`;
const allComments = new RegExp(comment, "gm");

const text = `
  # comment one
  code here
  # comment two
`;

text.match(allComments);
// ["  # comment one", "  # comment two"]

Trade-offs

  • Pros: No double-escaping. Named pieces are self-documenting. Composes naturally through template interpolation. The escape() helper safely escapes user input. Tiny dependency (regexr on npm).
  • Cons: Requires an npm dependency. The re tag returns a flagless RegExp — wrap in new RegExp() when you need flags like g or m.
  • Versus a builder class: A fluent builder API (.digit().repeat()) adds methods for every regex feature and requires learning a custom DSL. Tagged templates use standard regex syntax directly, which is more concise and familiar.
  • Versus regex literals: Use a literal (/pattern/) when the pattern is short, static, and easy to read at a glance. Use re when the pattern is long enough to benefit from named parts.