Regex+ compiles extended regex syntax down to native JavaScript RegExp objects
JS regexes ➕ future. A template tag for readable, high-performance, native JS regexes with extended syntax, context-aware interpolation, and always-on best practices.
At a glance
- What is it?
- The regex template tag from Steven Levithan gives JavaScript regular expressions insignificant whitespace, atomic groups, subroutines and context-aware interpolation, then returns a native RegExp so nothing slows down at runtime. It also turns on the v, x and n flags for you, which is where the real behaviour change lives.
- Who is it for?
- Regex+ fits a JavaScript team whose regular expressions have grown past the point where a single line is reviewable, especially if the patterns include nested quantifiers where ReDoS is a live concern. It does not fit a codebase whose patterns are short and simple, since the extra syntax has to be learned and the returned object is still a RegExp rather than something new.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 164 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A template tag that returns a native RegExp, not a wrapper object
The library publishes a template tag named `regex`, and the tag is the whole public API. The claim in the project description is that it modernizes JavaScript regular expressions with always-on best practices and support for key new features, while returning native `RegExp` instances that run with native performance.
That last clause is the design decision everything else follows from. Many regex helper libraries wrap the native object so they can add methods like `map` or `replaceAll`. This one does not. What you get back is a genuine `RegExp`, so it works with every string and regexp method you already use, and there is no wrapper overhead on a hot path.
The usage is short:
import {regex} from 'regex';
const str = 'abc';
// Returns a native RegExp instance, so it works with all string/regexp methods
regex`\w`.test(str); // → true
str.match(regex('g')`\w`); // → ['a', 'b', 'c']Note the second call. The tag accepts flags as an argument, so `regex('g')` produces a tagged template rather than a plain one. The return value is still a `RegExp`, so `str.match` receives an ordinary object with the global flag set.
The size claim is specific: lightweight, 7 kB minzip. There is also a Babel plugin available, which the description presents as giving no runtime cost and zero runtime dependencies, and a third party VS Code extension for syntax highlighting of the extended syntax.
The author is Steven Levithan, the licence is MIT, and the package is published as `regex` on npm with type definitions included.
The v, x and n flags are on by default, and that changes matching semantics
The most consequential thing about this library is not the new syntax, it is the flags it enables for you.
The always-on flag v gives the best level of Unicode support and strict errors. The always-on flag x allows you to freely add whitespace and comments to your regexes. The always-on flag n, in named capture only mode, improves regex readability and efficiency.
The project frames this as a modern regex baseline, so you do not need to continually opt-in to best practices. That framing is worth pushing back on slightly. The v flag is not only a Unicode upgrade, it is a strictness change: patterns that were valid under the older u flag can throw under v, and set operations and string properties behave differently. Enabling it silently means existing patterns ported into the tag can fail at construction time rather than at match time.
The x flag is the one that changes how you write. With insignificant whitespace enabled, spaces inside a pattern are ignored and comments are allowed, which is what makes long patterns readable. But it also means a literal space in a pattern has to be written explicitly, so anyone migrating an existing pattern has to think about every space they typed.
The n flag is the quiet one. Named capture only mode means you cannot use numbered capture groups, which removes an entire class of ambiguity about what backreference 2 refers to. The library has to renumber things accordingly when it composes patterns, which is exactly the point.
So the trade is real: you get readability and safety rules that most regex code lacks, in exchange for a stricter dialect that rejects some patterns you may have been using happily for years.
Atomic groups and possessive quantifiers as a ReDoS defence
Atomic groups and possessive quantifiers can dramatically improve performance and prevent ReDoS, which is the feature with the clearest operational payoff.
The example in the project description is deliberately small:
// Atomic group: Avoids ReDoS from the nested, overlapping quantifier
const words = regex`^(?>\w+\s?)+$`;The pattern is a nested, overlapping quantifier: a group containing a word followed by optional whitespace, repeated. Without atomicity, the engine has multiple ways to partition a run of characters, and on a long non-matching input it can backtrack through all of them. The atomic group `(?>...)` forbids backtracking into what it has already matched, so the number of ways to try collapses to one.
This is the classic ReDoS shape. The name comes from regular expression denial of service, and the mitigation is well established in the PCRE and Perl worlds. What is new here is that JavaScript, which historically lacked atomic groups, now has the feature, and this library gives you access to it through a syntax that is otherwise unchanged.
The subtlety worth knowing is that atomicity is not free correctness. An atomic group will not give back characters it consumed, so if you write the group too greedily the overall pattern can fail to match where a backtracking engine would have succeeded. For a pattern like the one above, where the inner group is deterministic, that is not a problem. In a pattern where the group is followed by something that could match the same characters, the atomic group can turn a match into a non-match.
So the practical advice is narrow and concrete: use atomic groups where you have identified a backtracking blowup, and do not sprinkle them through every pattern.
Subroutines and definition groups let you name a subpattern once
Subroutines and definition groups enable composition, and they are the feature that changes how a large pattern is maintained.
The idea is that you write a named group once inside a `(?(DEFINE)...)` block and then call it wherever you need it, using a backreference-like call. The example in the project description defines a date subpattern from year, month and day components and then calls it twice:
const record = regex`
^ Admitted: \g<date> \n
Released: \g<date> $
(?(DEFINE)
(?<date> \g<year>-\g<month>-\g<day>)
(?<year> \d{4})
(?<month> \d{2})
(?<day> \d{2})
)
`;The DEFINE block defines patterns without consuming input, so it can be placed at the end of the expression where it does not interfere with the matching structure.
The value shows up in the refactoring example the project publishes, which takes an unmaintainable date and time validation regex from real world code and rewrites it with the same matches using definition groups for year, month, day, hour, minute, second and time zone. The original is a single line of nested groups that nobody can safely edit. The rewritten version reads as a table of components.
The project also mentions recursive matching via an official plugin, and custom syntax plugins supported, which means the syntax is extensible rather than fixed.
One caveat about the whole grammar: the library publishes a lexicon of supported symbols and a separate list of features that are not planned for implementation, so the supported surface is explicit rather than implied by whatever the underlying engine happens to accept.
Interpolation that knows whether it received a string, a RegExp or a fragment
Context-aware interpolation is the feature with the most subtle rules, and it is where a migration will surprise you.
Interpolated strings have their special characters escaped. Interpolated regexes locally preserve the meaning of their own flags, or their absence, and their numbered backreferences are adjusted to work within the overall pattern. Partial patterns, produced by the `pattern` helper, are contextually sandboxed but not escaped.
The example shows all three in one expression:
const re = regex('m')`
# RegExp: Only the inner regex is case insensitive (flag i), and the outer
# regex's flags (including implicit flag x) don't apply to it
${/^a.$/i}
|
# String: Regex special chars are escaped
# Note that quantifying an interpolated value repeats it as a complete unit
${'a|b'}+
|
# Pattern: This string is contextually sandboxed but not escaped
[${pattern('a-z')}]
`;Three different behaviours in three lines. The interpolated RegExp keeps its own case insensitivity and does not inherit the outer flags. The interpolated string is escaped, so 'a|b' becomes a literal rather than an alternation, and quantifying it repeats the whole unit. The partial pattern is neither escaped nor treated as a complete pattern, it is inserted as a fragment inside the surrounding character class.
The backreference adjustment is the other half. The project documents it with a two line example: a double regex of /(.)\1/ interpolated twice, with the resulting pattern renumbered so the second and third occurrences point at the right groups. Without that adjustment, composing patterns would silently produce a regex that matches the wrong thing.
The library also documents interpolation principles and interpolation contexts, which suggests the rules are more systematic than the examples imply. That is worth reading before you start interpolating.
A 7 kB package with a Babel plugin for zero runtime cost
The distribution options are worth understanding because they determine how the library enters your build.
The package manifest shows the shape of it. The package is named `regex`, version 6.1.0, type module, with a single runtime dependency on regex-utilities at ^2.3.0. The exports map offers an ESM build, a CommonJS build, and an internals entry point that points at the source file rather than a bundled one, which is presumably there for tooling that needs the internals.
There are three bundles. A global one built with esbuild that exposes the library under the name Regex and minifies to dist/regex.min.js, an ESM bundle, and a CommonJS bundle. Type declarations are generated by tsc from the JavaScript sources with allowJs and emitDeclarationOnly, and there is a postbuild step that runs scripts/postbuild.js.
The Babel plugin is the interesting option. Because the tag is a compile time template, a Babel transform can evaluate the template at build time and emit a plain regex literal, which is what the project means by no runtime cost and zero runtime dependencies. The refactoring example in the project description even notes that a refactored pattern can be minified back into a regex literal during a build step using the Babel plugin.
For browsers there are two documented paths. An ESM script tag importing from https://esm.run/regex, or a classic script tag loading https://cdn.jsdelivr.net/npm/regex/dist/regex.min.js and destructuring the `regex` binding from a global `Regex` object.
The package is small enough that the runtime version is perfectly reasonable on its own. The Babel plugin matters when you want the extended syntax to disappear entirely from the shipped artefact.
What the library does not solve, and where a plain RegExp is the better tool
Two boundaries are clear from what the project describes.
The first is that this does not make regular expressions faster in general. It returns native RegExp instances that run with native performance, which is the same performance as the built in engine. What it changes is the shape of the pattern you write, and the shape of the pattern is where the ReDoS risk and the maintenance cost live. If your problem is that a single regex is slow to execute across millions of inputs, this library is not the answer. If your problem is that a single regex is unreadable and has three nested quantifiers in it, it is.
The second is that the always-on flags are a hard semantic change. A pattern that worked under the older u flag can throw under v, because v is stricter. A pattern with literal spaces stops matching under x unless the spaces are escaped. A pattern with numbered capture groups stops working under n. For a greenfield module this is an advantage, since nobody has to remember to opt in. For a large existing corpus of patterns it is a migration project, and the project does not document a compatibility or linting path for checking an existing codebase before switching.
The comparison with the alternative approach is straightforward. A plain RegExp literal has zero dependencies and no learning curve. Regex+ costs 7 kB, or nothing if you use the Babel plugin, and costs you a new syntax to learn. The case for it is the one the author makes in the project description, that JavaScript steps up as one of the best regex flavors alongside PCRE and Perl, and the evidence for that claim is the syntax itself: atomic groups, subroutines, definition groups and recursion are all things you would otherwise have to work around.
For short patterns, the plain literal is still the right answer. The value of the library starts where the pattern stops fitting on one line.
Maintenance, releases, and the test setup behind the claims
The project is a single maintainer project with a deliberate release discipline.
The last push was on 2026-05-12, and the tagged releases are v6.1.0 on 2025-12-09, v6.0.1 on 2025-01-31, and v6.0.0 on 2025-01-29. The two 6.0 releases a day apart show a major version being cut and then a fix, and roughly eleven months to 6.1.0. So the project moves at a rate of a few releases a year with a major version when the syntax changes.
That is a healthy pattern for a library that compiles source code, because a major version is exactly when you want existing users to notice.
The test setup is visible in the scripts and shows what the maintainer considers worth protecting. The test script runs jasmine, then type checks the spec directory with tsc against a dedicated tsconfig, then runs attw, which is the tool that checks that a package's type definitions and exports work across module resolution modes. That last check matters for a library publishing both ESM and CommonJS with separate type entry points, because the failure mode of that setup is a consumer whose bundler resolves the types incorrectly.
There is also a pretest step that runs the build first, so the tests always run against fresh bundles rather than a stale dist directory.
The repository has a spec directory for the tests, a scripts directory for the build scripts, and a pnpm workspace file with a lock file. The licence is MIT, which for a developer tool that sits in everyone's build is about as permissive as it gets.
One thing the project description does not state is a browser support matrix or a list of which ECMAScript regex features are assumed to exist natively. The description claims support for all ES2026 regex features, which is a forward looking statement about the baseline rather than a compatibility list.
Editorial conclusion
Regex+ fits a JavaScript team whose regular expressions have grown past the point where a single line is reviewable, especially if the patterns include nested quantifiers where ReDoS is a live concern. It does not fit a codebase whose patterns are short and simple, since the extra syntax has to be learned and the returned object is still a RegExp rather than something new. Before adopting it, read the interpolation rules, because the difference between interpolating a string, a RegExp and a partial pattern is the part most likely to surprise you, and check that your build can run the Babel plugin if you would rather compile at build time than at runtime.
Frequently asked questions
What does the regex template tag return?
It returns a native RegExp instance, so it works with all string and regexp methods, with no wrapper object and no runtime overhead over the built in engine.
How do I install the Regex+ library?
Run pnpm install regex, or the equivalent with your favourite package manager. In a browser you can also import from https://esm.run/regex as an ES module, or load the minified bundle from the jsdelivr CDN and destructure the regex binding from the global Regex object.
Which flags does Regex+ enable automatically?
The always-on v flag for the best level of Unicode support and strict errors, the always-on x flag which allows whitespace and comments in patterns, and the always-on n flag for named capture only mode.
How does Regex+ help with ReDoS?
It supports atomic groups and possessive quantifiers, which can dramatically improve performance and prevent ReDoS by preventing the engine from backtracking into an already matched group.
What is the difference between interpolating a string, a RegExp and a partial pattern?
Interpolated strings have their special characters escaped, interpolated regexes locally preserve their own flags and have their numbered backreferences adjusted, and partial patterns from the pattern helper are contextually sandboxed but not escaped.
Can Regex+ be compiled away at build time?
Yes. A Babel plugin is available that gives no runtime cost and zero runtime dependencies, and the project notes that a refactored pattern can be minified back into a regex literal during a build step.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/slevithan-regex)Was this useful?