Skip to content

Regex Tester: Test and Debug JavaScript Regular Expressions Online

Free regex tester for JavaScript patterns — live match highlighting, named groups, replace preview. No sign-up, runs in your browser.

Try it now: Regex Tester Test JavaScript regular expressions against sample text with live match highlighting, named capture groups and a replace preview — all six flags explained.

What Is a Regex Tester, and Why Test Live Instead of Guessing?

A regular expression — regex, for short — is a small pattern language for describing shapes of text: “three digits, a dash, four digits,” or “anything that looks like an email address.” The pattern itself is compact and, to anyone who hasn't stared at one for a while, close to unreadable. /^(?<user>[\w.+-]+)@(?<domain>[\w-]+\.[\w.]+)$/ means something precise, but nobody gets it exactly right by reading it once.

An online regex tool closes that gap: type a pattern, paste sample text, and watch which characters actually get highlighted as a match. It turns a written pattern into something you can verify instead of something you have to trust — the same job whether you're specifically after a regex tester JavaScript can run natively, or using a browser tester as a quick sandbox before porting the logic to a regex tester Python will validate separately. That matters because regex mistakes are almost never syntax errors — the pattern compiles fine and simply matches the wrong thing, or matches too much, or matches nothing at all on the one input that mattered. A live tester surfaces that immediately, against real sample data, instead of three commits later when a bug report shows up.

One thing worth being upfront about: regex isn't a single universal language — JavaScript, Python's re, PCRE, and POSIX grepagree on the core ideas but disagree on specific syntax. GenKitLab's Regex Tester compiles your pattern with the actual JavaScript RegExpengine built into your browser, stated plainly rather than hidden in fine print. If you're testing a pattern destined for Python or PCRE specifically, treat this as validating the logic — the flavor differences that actually bite are covered once, in the gotchas table further down, rather than repeated here.

The Regex Ideas Worth Actually Understanding

Regex tutorials often list every token in one long table and move on. Three ideas cause a disproportionate share of real bugs, so they're worth slowing down for.

Greedy vs. lazy quantifiers

A quantifier like *, +, or {2,5}says “repeat the previous token.” By default, quantifiers are greedy: they match as much text as possible, then backtrack only if the overall pattern would otherwise fail. Add a ? right after the quantifier and it becomes lazy: it matches as little as possible instead.

greedy vs. lazy — same pattern, one character different
Input:    <a><b>

/<.+>/     greedy  → matches <a><b>   (the whole thing — .+ eats past the first >)
/<.+?>/    lazy    → matches <a>     (stops at the first > it can)

This one character accounts for a large share of “why did my regex match too much” bugs, especially around HTML-like tags, quoted strings, and anything delimited by a repeated character. Watching it happen against real sample text — rather than reasoning about it in the abstract — is the fastest way to internalize which one you actually need.

Capturing groups, non-capturing groups, and named groups

Parentheses in a pattern do two jobs at once: they group tokens so a quantifier applies to the whole group, and — by default — they capture whatever matched inside them so you can read it back out afterward. Sometimes you want the grouping without the capture, which is what (?:...) — a non-capturing group — is for: it groups tokens for quantifying or alternation without adding a numbered slot to the result.

Numbered capture groups (match[1], match[2]) work, but they're fragile — insert a new group earlier in the pattern and every number after it shifts. Named groups, (?<name>...), fix that: they're addressed by name in code (match.groups.name) and, in a replacement string, as $<name> instead of $1. This is exactly what the tester's Matches panel lists per match — every named and numbered group, including ones that matched nothing because they sat inside an alternation or an optional section that didn't participate.

capturing vs. non-capturing vs. named
/(\d{4})-(\d{2})-(\d{2})/          numbered:  match[1], match[2], match[3]
/(?:\d{4})-(?:\d{2})-(?:\d{2})/    grouped, nothing captured
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/   named:  match.groups.year, .month, .day

Lookahead vs. lookbehind

Both are “zero-width assertions” — they check that something is present without consuming any characters themselves, so they don't appear in the match text. A lookahead, (?=...), asserts what comes after the current position; a lookbehind, (?<=...), asserts what comes before it. Negated forms exist too: (?!...) and (?<!...) assert the opposite — that something does not follow or precede.

lookahead and lookbehind, side by side
/\d+(?= USD)/          matches "100" in "100 USD" — the currency label isn't consumed
/(?<=\$)\d+/           matches "100" in "$100"    — the dollar sign isn't consumed
/\d+(?! USD)/          matches numbers NOT followed by " USD"
/foo(?!bar)/           matches "foo" only when it isn't immediately followed by "bar"

Lookbehind is the one to double-check across flavors: JavaScript has supported it in evergreen browsers for years, but if a pattern is headed somewhere older or more constrained, it's worth confirming lookbehind actually exists there before you rely on it.

Shorthand classes are worth a quick mention alongside these: \d is digits, \wis “word” characters (letters, digits, underscore), and \s is whitespace — each has an uppercase negated counterpart (\D, \W, \S). They're simple on their own; most of the confusion around them comes from forgetting that \wdoesn't include a hyphen or an accented letter unless the pattern is explicitly Unicode-aware.

Real-World Regex Use Cases by Role

The same tool gets reached for very differently depending on the job in front of you.

  • Form validation — checking an email or phone-number field client-side before it ever hits a server. Testing the pattern against real edge cases (a plus-addressed email, an international phone number with spaces) catches the false rejections that annoy real users, which a pattern that only looks right rarely does.
  • Log parsing — pulling a timestamp, status code, or request path out of a line of raw server logs pasted straight from a terminal. This is where greedy quantifiers most often bite: a careless .* swallows past the field boundary you actually wanted.
  • Find-and-replace in an editor— most code editors support regex search-and-replace with capture-group references. Testing the pattern and the replacement together first, on a representative sample, avoids a replace-all that's subtly wrong across a whole file.
  • Data extraction and scraping — pulling a specific value (a price, an ID, a code) out of messy, semi-structured text before running the same pattern across a much larger dataset, where a mistake is expensive to notice and rerun.
  • Input sanitization — checking that a pattern used to strip or reject unwanted input (control characters, disallowed symbols) actually behaves correctly on adversarial input, not just the clean example that inspired it.

Testing a Regex in Code: JavaScript, Python, and the Command Line

A tester is where you work out the pattern. Eventually it needs to run in actual code, and the exact syntax for “does this match” versus “give me the matched groups” differs by language.

JavaScript

RegExp.prototype.test() answers a yes/no question; String.prototype.match() and matchAll() return the matched text and its groups:

javascript
const pattern = /(?<user>[\w.+-]+)@(?<domain>[\w-]+\.[\w.]+)/;

pattern.test("[email protected]"); // true

const match = "[email protected]".match(pattern);
match.groups.user;   // "ada"
match.groups.domain; // "example.com"

// with the g flag, use matchAll to get every match, not just the first
const text = "[email protected], [email protected]";
for (const m of text.matchAll(/(?<user>[\w.+-]+)@(?<domain>[\w-]+\.[\w.]+)/g)) {
  console.log(m.groups.user, m.groups.domain);
}

One gotcha worth knowing before it costs a debugging session: a regex compiled with the g flag carries state — lastIndex — across calls when you reuse the same object with .exec() or .test() in a loop. matchAll() sidesteps this by returning a fresh iterator each time.

Python

Python's remodule separates “does it match anywhere” from “does it match starting at the beginning” more explicitly than JavaScript does:

python
import re

pattern = re.compile(r"(?P<user>[\w.+-]+)@(?P<domain>[\w-]+\.[\w.]+)")

m = pattern.search("contact: [email protected]")  # search anywhere in the string
if m:
    m.group("user")    # "ada"
    m.group("domain")  # "example.com"

pattern.match("[email protected]")   # match only from the very start of the string
pattern.findall("[email protected], [email protected]")  # every match, as tuples of groups

Notice the named-group syntax difference: Python uses (?P<name>...), JavaScript uses (?<name>...) — no P. Copy a Python pattern straight into a JS-only tester and that's usually the first thing that needs adjusting.

Command line: grep -E

For a quick check against a file or piped output without writing any code, extended grep (POSIX ERE) is usually already on the machine:

shell
grep -E '[0-9]{3}-[0-9]{4}' contacts.txt        # lines containing a phone-number-shaped string
grep -Eo '[\w.+-]+@[\w-]+\.[\w.]+' server.log     # -o prints only the matched text, not the whole line
grep -E -v '^#' config.env                       # -v inverts: lines that do NOT match

POSIX ERE is a noticeably smaller flavor than JavaScript or PCRE — no lookahead, no lookbehind, no non-greedy quantifiers, no named groups — so a pattern built for one of those needs simplifying before it works with grep -E, not just re-punctuating.

Regex Gotchas: Backtracking, Escaping, and Flavor Differences

Most regex bugs fall into a handful of repeat offenders. Knowing the shape of each one in advance saves the debugging session.

GotchaWhat's happeningWhat to do about it
Catastrophic backtrackingNested or overlapping quantifiers, like (a+)+b, force the engine to try an exploding number of ways to split the input between the inner and outer repetition before it can conclude there's no match. On a long non-matching string, this can take seconds, minutes, or effectively forever.Avoid nesting a quantifier inside a group that's itself quantified over an overlapping character set. Prefer a specific, non-overlapping class over a broad .* wherever possible, and anchor the pattern so the engine can fail fast.
Common escaping mistakesCharacters like . ( ) [ ] { } + * ? ^ $ | \ are metacharacters with special meaning. Forgetting to escape a literal one (a literal dot in a domain, a literal parenthesis) makes the pattern match more than intended, or fail to compile at all.Escape a literal metacharacter with a backslash (\., \(, \)). Inside a character class, most metacharacters lose their special meaning automatically, except ] , \, ^ (at the start), and -.
Flavor differences that biteJS uses (?<name>...) for named groups; Python and PCRE use (?P<name>...). PCRE and Python support possessive quantifiers and atomic groups; JavaScript doesn't. POSIX ERE (grep -E) has no lookaround or lazy quantifiers at all.Test the logic against the engine you'll actually ship with. Porting between flavors is usually a small syntax change — but confirm it in that flavor's own tester or REPL rather than assuming it transfers.
catastrophic backtracking, demonstrated
/^(a+)+$/.test("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!");
// the trailing "!" means there's no match — but proving that takes the engine
// an exponential number of attempts to split the "a"s between the two + quantifiers.
// On some engines and input lengths, this locks up the thread entirely.

This is exactly the class of bug GenKitLab's Regex Tester caps the visible match list at 2,000 to stay responsive against — but a genuinely pathological pattern can still hang the tab, the same way it would hang a server processing untrusted input. If a pattern locks up against adversarial-looking test text, that's the actual finding: the fix belongs in the pattern (restructure the quantifiers), not in whatever tool exposed it.

GenKitLab vs. regex101 vs. RegExr

regex101 and RegExr are both established, capable tools with large existing audiences, and neither is being oversold here as inferior on the bare comparison — they're good at what they do. Where a simpler, client-side tool actually wins is a narrower claim: less upfront complexity for a first-time or occasional user, and no account required to get a straight answer.

GenKitLabregex101RegExr
Beginner-friendlinessPreloaded example, single view, plain-English flag explanationsPowerful but dense — multiple tabs, blank editor by default, a learning curve before first useFriendlier than regex101, but still oriented around a persistent workspace
Flavor supportJavaScript (ECMAScript) only — stated plainly, not oversoldMultiple flavors: PCRE, JS, Python, Go, and morePHP/PCRE and JavaScript — no Python
Sign-up requiredNoNo for basic use; account needed to save patterns long-termAccount required to persist saved favorites
Client-side processingYes — matching runs locally in your browser, nothing transmittedMatching runs client-side; account features are server-backedMatching runs client-side; account features are server-backed
Replace previewBuilt in, with $1, $<name>, and $& substitutionBuilt in, full-featuredBuilt in
Best fitA quick pattern check or a beginner learning what a flag doesDeep, multi-flavor work and detailed pattern debuggingQuick JS/PCRE checks with a saved-favorites habit

The honest read: if you need to verify a pattern against PHP's PCRE engine, or Python's remodule specifically, regex101's multi-flavor selector is the right tool for that job — GenKitLab's tester doesn't claim to cover that ground, because it doesn't. Where it fits is the much more common case: you're writing or debugging a pattern for JavaScript or TypeScript code, you want an answer in the next few seconds, and you'd rather not create an account to get it.

Explore More Regex Tools

Testing a pattern against sample text is one part of working with regex. GenKitLab runs a small cluster of tools around the rest of that workflow, all client-side, all free:

  • Regex Cheat Sheet — every token, quantifier, character class, group, and lookaround on one searchable page, each with a live example you can run and the flavor differences called out directly, rather than assumed.
  • Regex Generator — build a pattern from labelled blocks, or infer one from a list of strings that should and shouldn't match, verified against both lists before you're asked to trust the result. (This is a deliberately rule-based generator, not a “describe it in English” model call — see the FAQ below for why.)
  • Glob Pattern Tester — for the adjacent but different problem of matching file paths and .gitignore rules, which uses glob syntax, not regex, even though the two get confused constantly.

Once a pattern needs to extract or clean data that's already inside a larger document, a few more tools pick up from there: JSON Formatter & Validator for the structured data a regex often feeds into or extracts from, URL Encoder & Decoder for the percent-encoding that trips up a naive pattern matched against a raw URL, and Base64 Encoder & Decoder for unwrapping an encoded value before a pattern ever needs to see it.

Frequently asked questions

What is regex used for?

Regular expressions describe patterns in text — validating that an email or phone number is shaped correctly, extracting a value like a timestamp or price out of a larger string, finding and replacing text in an editor, parsing log lines, and sanitizing input before it's used elsewhere.

How do I test a regex pattern online for free?

Paste your pattern and some sample text into a browser-based tester and read the highlighted matches. GenKitLab's Regex Tester does this entirely client-side against the JavaScript RegExp engine, with no sign-up and no data sent anywhere — useful the moment you want a fast, no-friction check of a JS or TypeScript pattern.

What's the difference between greedy and lazy matching?

A greedy quantifier (*, +, {2,5}) matches as much text as possible before backtracking only if needed. Adding a ? right after it (*?, +?) makes it lazy — it matches as little as possible instead. On a pattern like /<.+>/ against <a><b>, greedy matches the whole string; /<.+?>/ stops at the first closing bracket.

What do \d, \w, and \s mean?

\d matches a digit (0-9), \w matches a 'word' character (letters, digits, and underscore), and \s matches whitespace (spaces, tabs, newlines). Each has an uppercase negated counterpart — \D, \W, \S — matching everything the lowercase version doesn't.

How do capture groups work?

Parentheses in a pattern both group tokens and, by default, capture whatever matched inside them so you can read it back afterward — as match[1], match[2], and so on. (?:...) groups without capturing, which keeps the numbering stable when you don't need the captured text. (?<name>...) captures with a name instead of a number, which is more maintainable and is what a replacement string references as $<name>.

What's a good regex101 alternative?

regex101 is free to use for testing patterns; an account is only needed to save patterns long-term. It's a strong, multi-flavor tool and not being dethroned here — GenKitLab's Regex Tester is a solid regex101 alternative specifically when you want a simpler first screen, a preloaded example, and no account at all, for a JavaScript-specific pattern.

Can AI write a regex for me from plain English?

A model can produce something that looks plausible, but a regex that looks right and is subtly wrong is often worse than having none — it gets pasted somewhere and trusted. GenKitLab's Regex Generator deliberately takes a different, verifiable approach instead: build a pattern from labelled blocks, or infer one from example strings that should and shouldn't match, checked against both lists before you're asked to rely on it.

What regex flavor should I use?

Whatever flavor the code that will actually run the pattern uses — JavaScript's built-in RegExp for JS/TS, Python's re module for Python, PCRE for PHP and many CLI tools, or POSIX ERE for grep -E. The core ideas transfer across all of them, but named-group syntax, lookbehind support, and possessive quantifiers differ enough that a pattern should be confirmed in its actual target flavor before shipping.

Last updated