Skip to content

Free Regex Tester — Debug & Match Patterns Online

Test regex patterns instantly against any text. Live match highlighting, capture groups, replace preview, split, and pattern explainer. JavaScript-flavor regex, 100% private, no signup.

No Tracking Runs in Browser Free
All testing runs locally in your browser. Your pattern and text never leave this device.
Flags
Highlighted matches
Matches & capture groups
Reviewed for ECMA-262 fidelity, capture-group correctness (with and without the /d flag), replacement-template parity with String.prototype.replace, ReDoS resistance via wall-clock budgeting, and accessibility (ARIA roles, screen-reader match announcements, RTL/LTR handling). — Go Tools Text Tooling Team · May 21, 2026

What Is a Regex Tester?

A regex tester (regular expression tester) is a tool that lets you write a regex pattern, paste a piece of test text, and see exactly what the pattern matches — with capture groups, replacement preview, and a flag breakdown — without recompiling code or running a script. For developers, it shortens the loop from minutes to milliseconds: tweak the pattern, watch the highlights move, ship the regex with confidence.

A regular expression is a compact language for describing text patterns. `\d+` matches one or more digits. `[A-Za-z_]\w*` matches a typical identifier. `(?\d{4})-(?\d{2})-(?\d{2})` matches an ISO date and names each part. Regular expressions are the backbone of search-and-replace in every code editor, of validation in every form, of log parsing in every observability stack, and of `grep`, `sed`, and `awk` — the Unix tools half the internet runs on. They're also notoriously hard to write correctly: an off-by-one quantifier or a missing escape can match the wrong substring, miss a match entirely, or — worst case — trigger catastrophic backtracking that takes a CPU core hostage. A good regex tester catches each of those failure modes before they reach production.

This tester runs the native ECMA-262 RegExp engine that ships in every modern browser — the same engine you call from JavaScript, TypeScript, Node.js, Deno, or Bun. That means: capture groups (numbered and named with `(?...)`), lookahead and lookbehind assertions (`(?=...)`, `(?!...)`, `(?<=...)`, `(?

What the tester surfaces beyond raw matches: the Matches & capture groups panel lists every match with its [start, end) offsets and each capture group's value — the same information you'd get from `String.prototype.matchAll` with the /d flag, but laid out for visual scanning. The Replace tab shows a live substitution preview supporting the full $1 / $& / $` / $' / $$ / $ template alphabet — exactly what JavaScript's String.replace accepts. The Split tab applies String.split with the regex and shows each part. The Explain tab tokenises the pattern and annotates each piece in plain English, useful for code review, teaching, and porting between dialects.

For privacy: every operation is local. Your pattern and your test text never leave the page — they aren't logged, aren't sent to an analytics service, aren't stored on disk. Only your UI preferences (active tab + which flags you usually have on) persist to localStorage. That makes this tool safe for redacted log samples, proprietary patterns, internal config, and patterns that include hints about your data schema. Compared to server-backed testers like regex101, the privacy and latency story is strictly better; the trade-off is single-flavor support (JavaScript only).

If you're new to regex, the Common patterns dropdown ships with battle-tested starters: email address, URL, IPv4, UUID, hex color, ISO date, US phone number, and a trim-trailing-whitespace pattern. Load one, observe the matches against the supplied sample text, then mutate the pattern one character at a time to feel how the engine responds. Pair this with the Text Diff tool when you want to compare before/after of a regex-driven cleanup, with JSON Formatter when your input or expected output is JSON, or with URL encoder when the strings you match are URL-encoded.

// The pattern you build in this tester drops straight into JavaScript.
// Example: extract every ISO date from a string with named groups.

const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/g;
const text = 'shipped 2026-05-21, scheduled 2026-06-30';

for (const m of text.matchAll(pattern)) {
  console.log(m.groups.year, m.groups.month, m.groups.day);
  // → 2026 05 21
  // → 2026 06 30
}

// Same regex, used in a replace with $<name> templates:
text.replace(pattern, '$<day>/$<month>/$<year>');
// → 'shipped 21/05/2026, scheduled 30/06/2026'

// With the /d flag, every match carries [start, end] indices
// per capture group — the Matches panel uses this to paint offsets.
const p2 = /(?<year>\d{4})-(?<month>\d{2})/gd;
const m = [...text.matchAll(p2)][0];
m.indices.groups.year; // [8, 12]

Key Features

Live Match Highlighting

Every match in your test text lights up the instant your pattern parses. Alternating colours make adjacent matches easy to count visually, and the count badge above the result tells you the exact total — no Run button, no debounce delay over 200ms.

Capture Groups Side Panel

The Matches & capture groups panel on the right lists each match as a card with its [start, end) offsets, the full match text, and every positional + named capture group inside it. Named groups label themselves as $ so you can read the data the same way you'll consume it in code.

Live Replace Preview with $1 / $& / $

Switch to the Replace tab to see your substitution applied in real time. The full ECMAScript replacement alphabet works: $1..$N for positional, $ for named, $& for the whole match, $` and $' for prefix/suffix, $$ for a literal dollar. Side-by-side input and output panels with one-click copy.

Split with Regex Boundaries

The Split tab calls String.prototype.split with your regex and shows every part as a numbered list. Empty parts are rendered with a ⏎ glyph so you can see how the engine handled adjacent delimiters — useful for debugging CSV-like input cleanup.

Pattern Explainer (Token-by-Token)

The Explain tab tokenises your pattern into chips coloured by class (escape / quantifier / character class / group / anchor / alternation) and annotates each chip with a one-line description. Read your own regex back to yourself before shipping; use it for code review or teaching.

ReDoS-Safe (Wall-Clock Timeout)

Every match call is wrapped in a 250-millisecond budget. Classic catastrophic-backtracking shapes like `(a+)+`, `(a|aa)+b`, and deeply nested quantifiers abort cleanly with a Pattern timed out warning — the page stays responsive instead of locking the tab. Detection without a server-side sandbox.

Common Patterns Library

Battle-tested starters for the eight patterns developers reach for most: email, URL, IPv4, UUID v4, hex color, ISO date, US phone number, and trim-trailing-whitespace. Each loads with a matching sample so you see the regex working before you adapt it.

Permalink Sharing (No Upload)

Copy link encodes pattern + flags + sample text into the URL hash (#p=…&f=gim&t=…). Browsers never transmit URL fragments in requests, so a shared link reproduces your state on the recipient's machine without touching go-tools.org servers. Self-contained, audit-friendly.

100% Private, Browser-Only

Your regex and test text never leave your device. No network requests, no logging, no analytics on what you type. Verify in DevTools → Network: zero requests when you type. Safe for proprietary patterns, redacted logs, and any text you wouldn't paste into regex101.

Worked Examples

Extract every email address from a paragraph

[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}
/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g

Paste the pattern with the /g flag on, drop a paragraph into the test text, and every email lights up in the highlighted view. The Matches & capture groups panel on the right lists each address with its [start, end) offsets — useful when piping the same regex into grep, sed, or a code editor.

Capture date parts with named groups

(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/g

ECMA-262 named groups appear in the right panel as $, $, $. Switch to the Replace tab and try $/$/$ to reformat ISO dates to DMY in a single pass — the same trick works in any modern JS replace call.

Find/replace with $1 back-references

(\w+) (\w+)
Replacement: $2, $1   →   Jack Doe → Doe, Jack

Two unnamed capture groups, replacement template $2, $1, and the Replace tab gives you a live preview. $&, $`, $', $$, and $ are all supported — exactly the substitution alphabet from the ECMAScript spec, so what you copy out runs unchanged in any JS engine.

Strip trailing whitespace line by line

[ \t]+$
/[ \t]+$/gm

Combine the /g (global) and /m (multiline) flags so $ anchors at every line end, not just the end of input. The Replace tab with an empty replacement preview shows a clean diff: trailing tabs and spaces vanish, the prose stays put. The same regex with the /s (dotAll) flag off keeps . from crossing newlines.

Detect catastrophic backtracking and survive it

(a+)+b
Test text: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac

The nested + quantifiers form a classic ReDoS pattern. In a naive tester this hangs the tab. Here the wall-clock guard fires after 250ms, the Pattern timed out banner appears, and the page stays responsive. Anchor the pattern, switch to a non-overlapping alternation, or use atomic-like idioms — and re-test.

Split a CSV-like line by mixed delimiters

[,;|]\s*
alpha, beta; gamma | delta → ["alpha", "beta", "gamma", "delta"]

Switch to the Split tab. Any comma, semicolon, or pipe (followed by optional whitespace) becomes a part boundary. Useful for cleaning copy-pasted tag lists, normalising user input, or pre-processing log fields before a real CSV parser — see CSV to JSON when the data really is RFC 4180.

How to Use the Regex Tester

  1. 1

    Type your pattern between the slashes

    Drop any ECMA-262 regular expression into the /…/ field. Bad patterns highlight in red with a parser message; valid patterns proceed to live matching.

  2. 2

    Toggle the flags you need

    g (global), i (case-insensitive), m (multiline), s (dotAll), u (unicode), y (sticky), d (indices). Each chip lights up when on; the readout right of the pattern shows the canonical literal.

  3. 3

    Paste your test text

    Matches highlight in alternating colours as you type. The Matches & capture groups panel on the right lists every match with [start, end) offsets and each capture group's value (named groups labeled $).

  4. 4

    Switch tabs for Replace, Split, or Explain

    Replace previews a substitution template alongside the input. Split slices on every match boundary. Explain breaks the pattern token-by-token with a plain-English description per element.

  5. 5

    Copy the literal or share a permalink

    Copy /pattern/flags drops the canonical regex literal on your clipboard for direct use in JavaScript / TypeScript / Node. Copy link encodes the full state into a URL hash (no upload) so a colleague can reproduce it locally.

Common Regex Mistakes

Forgot the /g flag and only got one match

Without /g (or /y), the engine stops after the first match. The match, matchAll, replace, and split methods all behave differently around the global flag. Toggle /g and re-run; the count above the results jumps from 1 to N.

✗ Wrong
Pattern: /\d+/  →  '1 22 333' yields only ['1']
✓ Correct
Pattern: /\d+/g  →  '1 22 333' yields ['1', '22', '333']

Who Uses This Tool

Validate Form Inputs Before Shipping
Confirm your email / phone / postcode / username regex matches what you expect — and rejects what you don't — across edge cases (unicode names, plus-aliasing, international formats) before the validation reaches production and bounces real users.
Extract Data from Logs and Configs
Build a pattern that pulls request IDs, status codes, latencies, or stack-trace lines out of an arbitrary log slice. Named groups make the data self-documenting; the Matches panel shows offsets so you can pipe the same regex into `rg --replace` or `grep -oE` afterwards.
Find/Replace Across a Codebase
Draft a refactor pattern (e.g. `(\w+)\.apply\(null,\s*\[(.*?)\]\)` → `$1($2)`) here, preview the substitution against representative snippets, then paste the validated regex into your editor's project-wide find/replace with confidence.
Sanity-Check a Pattern Found Online
Pasted a regex from StackOverflow or a blog? Drop it into the Explain tab — every token gets annotated in plain English. Catches subtle issues (`.+?` where you wanted `.+`, missing `^`/`$` anchors, accidentally greedy quantifiers) before the regex lands in your code.
Teach Regex to a Teammate
Open the Explain tab on a working pattern and walk through it token-by-token. The colour coding (escape / quantifier / character class / group / anchor / alternation) lets the learner see the structural shape of the regex, not just the characters.
Port a Pattern Between Languages
Have a Python or PCRE regex you need to use in JavaScript? Paste it here. If it parses, the explainer shows you the JS-equivalent semantics; if it doesn't, the parser error names the offending construct (atomic groups, possessive quantifiers, inline `(?i)`) so you know exactly what to rewrite.
Debug a Slow Production Regex
If a server regex is suspected of catastrophic backtracking, paste it into this tester with a sample of the input. The 250ms wall-clock guard fires on pathological cases, giving you an immediate diagnosis before you reach for profiler tooling — and the explainer points at the nested-quantifier root cause.

Engine & Algorithm Notes

ECMA-262 RegExp Engine (Native Browser)
Uses `new RegExp(pattern, flags)` and the engine that ships with V8 / JavaScriptCore / SpiderMonkey — the same regex semantics you get in JavaScript anywhere. Patterns that validate here run unchanged in Node.js, Deno, Bun, and every modern browser.
Match Iteration via String.matchAll
Global iteration uses `text.matchAll(regex)` rather than a manual lastIndex loop, so every match carries its capture groups, named groups, and (with /d) [start, end] indices. Zero-width matches are handled with the standard +1 lastIndex advance to avoid infinite loops.
Wall-Clock Timeout for ReDoS Protection
A 250-millisecond budget wraps every match, replace, and split call. The engine can still backtrack internally on a single match attempt, but the outer iteration cooperates with the budget — pathological patterns abort with `timedOut: true` and the UI surfaces a warning instead of locking the tab.
Replacement-Template Reimplementation
The Replace tab parses $1..$N, $&, $`, $', $$, and $ manually instead of delegating to `String.replace`, so the preview behaves identically across engines (older Safari, older Node) where named-group templates have edge cases. The output is exactly what current JavaScript engines produce.
Pattern Tokenizer for the Explainer
The Explain tab runs a hand-written tokenizer that classifies each pattern fragment (escape / metachar / quantifier / character class / group-open / group-close / anchor / alternation). Unfamiliar constructs fall through to `literal` with a generic note so the explainer never silently drops content.
Permalinks via URL Hash (Never Transmitted)
Share state is encoded in the location.hash fragment (`#p=…&f=…&t=…&tab=…`). Browsers never transmit the fragment in HTTP requests, so go-tools.org servers receive zero data when a permalink is opened. The hydration happens entirely on the recipient's device.

Regex Best Practices

Anchor Patterns When You Mean To
`^pattern$` matches an exact string; `pattern` matches anywhere. The wrong choice is the most common bug in form validation: missing `^` lets a leading `attacker.com/` slip past a domain check; missing `$` lets trailing garbage through. Use the Match tab against deliberately broken samples to confirm what's rejected.
Prefer Non-Capturing Groups for Pure Structure
`(?:foo|bar)+` and `(foo|bar)+` are functionally identical, but the first one doesn't allocate a capture group. Reach for `(?:…)` whenever the group exists only for a quantifier or alternation — keeps your numbered $1..$N stable and saves a tiny amount of engine work.
Use the /u Flag for Anything Beyond ASCII
Without /u, the dot and `\w` treat surrogate-pair characters (emoji, non-BMP code points) as two UTF-16 units. With /u, they're one code point each — what your users will perceive. /u also enables `\p{Letter}` and other property escapes. Default to /u for new patterns unless you have a specific reason not to.
Name Your Capture Groups
`(?\d{4})-(?\d{2})` is self-documenting. Six months later when you read the regex back, `m.groups.year` is obviously the year — `m[1]` is not. Replacement templates with $ survive group reordering, too: positional templates break the moment someone adds another group.
Test Failure Cases, Not Just Success
A regex tester is for the failures. Confirm what your pattern matches, then deliberately mutate the test text to see what it doesn't — leading whitespace, trailing whitespace, missing parts, extra parts, wrong case, mixed scripts. Patterns that pass valid input but accept garbage are the bugs production exposes first.

Frequently Asked Questions

Is my regex or test text sent to your server?
No. Every match, replace, split, and explain operation runs in JavaScript inside your browser using the native RegExp engine. Your pattern and text are not uploaded, not logged, not stored on disk, not sent to any third party. Only your UI preferences (active tab + which flags you usually have on) are saved to localStorage so the page remembers them next visit — never the pattern or the test text. You can verify by opening DevTools → Network: typing in either box fires zero requests. This makes the tool safe for proprietary patterns, redacted log samples, internal config, and anything else you would not paste into regex101.
What regex flavor does this tester use — PCRE, Python, Java, JavaScript?
ECMA-262 (JavaScript), the dialect implemented by V8, JavaScriptCore, and SpiderMonkey — the same engine you get from `new RegExp(pattern, flags)` in any browser, Node.js, Deno, or Bun. That means the supported features are: capture groups (numbered + named with `(?...)`), lookaheads `(?=...)` and `(?!...)`, lookbehinds `(?<=...)` and `(?...)`, possessive quantifiers `a++`, conditionals `(?(1)yes|no)`, and inline modifiers `(?i)` will throw a syntax error. Python's `re.VERBOSE` is not supported here. For Python/Java/Go regex, port the pattern back to its native engine — most simple patterns transfer unchanged, and the explainer here is flavor-neutral.
What do each of the flags g, i, m, s, u, y, d do?
g (global) — find every match, not just the first; required for iterating with .matchAll and for global replace. i (case-insensitive) — A and a match the same character. m (multiline) — ^ and $ anchor at every line break, not just the start/end of the whole input. s (dotAll) — . matches newlines too; without /s a dot stops at \n. u (unicode) — enables \u{HHHH} escapes, Unicode property escapes (\p{Letter}), and treats the pattern as a sequence of Unicode code points instead of UTF-16 code units. y (sticky) — anchors each match at lastIndex, useful for tokenisers. d (hasIndices, ES2022) — populates `.indices` and `.indices.groups` with [start, end] pairs for every capture; this tester uses /d under the hood to draw the group boundaries. Toggle them as chips above the test text; the canonical /pattern/flags literal is shown in the readout.
How do I write capture groups and how do I refer back to them?
Wrap a sub-pattern in parentheses: `(\d{4})-(\d{2})-(\d{2})` gives you three positional groups, accessible as $1, $2, $3 in replacements or as m.groups[0..2] in the Matches panel. Use `(?...)` for named groups: `(?\d{4})-(?\d{2})` lets you write $/$ in the replacement template. Use `(?:...)` for a non-capturing group when you only need grouping for a quantifier (`(?:foo|bar)+`) — it does not create a back-reference, which keeps your numbered $1..$N stable. Inside the same pattern, refer to an earlier capture with `\1`, `\2`, etc. — handy for finding doubled words like `\b(\w+)\s+\1\b`.
How do lookahead and lookbehind work, and what are they good for?
Lookarounds are zero-width assertions — they check that something matches (or doesn't) without consuming characters. `(?=foo)` (positive lookahead) succeeds if `foo` follows the current position; `(?!foo)` (negative lookahead) succeeds if `foo` does NOT follow. `(?<=foo)` and `(?
Why does my regex hang the browser, and what is catastrophic backtracking?
Catastrophic backtracking happens when a pattern has nested quantifiers over overlapping alternatives — the classic shape is `(a+)+`, `(a|aa)+`, `(\w*)*`, or `(?:a|a?)+`. On an input where the match can fail at the last character, the engine tries an exponential number of group splits before giving up. On `aaaaaaaaaaaaaaaaaaaaab` with the pattern `(a+)+b`, that is 2^21 ≈ 2 million backtracks before the answer. This tester wraps every match call in a 250ms wall-clock budget — if the budget is exceeded, iteration stops and you see a Pattern timed out warning. Fixes: anchor the pattern to its boundaries (`^...$`), prefer non-overlapping alternatives (`(a|b)` instead of `(a|aa)`), unroll nested quantifiers (`a+` instead of `(a+)+`), or rewrite to use possessive-like idioms (`(?=(a+))\1` simulates `a++` in JS).
How is this regex tester different from regex101.com?
Three differences. (1) Privacy: regex101 sends every keystroke to its servers for backend evaluation and stores patterns in shared community links; this tool runs entirely in your browser, no network calls. (2) Speed: a server roundtrip on every change adds 80-300ms; here matches update in under 10ms even on long text. (3) Flavor focus: regex101 supports PCRE, Python, Java, .NET, JavaScript, Rust, and Go with feature-flag UI; this tool focuses on ECMA-262 (JavaScript) — the flavor every browser, Node, Deno, and Bun ship — and the explainer plus permalinks are tuned to that single flavor. If you need PCRE-only features, regex101 is still the right tool; for JavaScript / TypeScript work, this is faster and more private. Permalinks here are URL hashes (no upload), so a shared link reproduces your pattern locally on the recipient's machine.
How do I escape special characters like . | ( ) [ ] { } * + ? ^ $ \?
Put a backslash in front: `\.`, `\|`, `\(`, `\)`, `\[`, `\]`, `\{`, `\}`, `\*`, `\+`, `\?`, `\^`, `\$`, `\\`. Forward slash `/` does not need escaping in a string-passed pattern (only in JS regex literals between the slashes). Inside a character class `[...]` most metacharacters lose their special meaning, so `[.]`, `[*]`, `[+]` all match a literal character — the only metacharacters that stay special are `]`, `\`, `^` (only at start to negate), and `-` (range when between two characters). When in doubt, paste the literal text into the pattern and run the explainer: every escape sequence gets a one-line description in the breakdown panel.
Can I share a regex with a colleague via a link?
Yes — and the link contains no server roundtrip. Click Copy link in the action bar: the tester encodes your pattern, flags, test text, and active tab into the URL hash (`#p=...&f=gim&t=...&tab=match`). Anyone who opens the link hydrates the page with the same state — locally on their machine. Because the data lives in the hash fragment, it is never sent to the go-tools.org server (browsers do not transmit fragments in requests) and is not logged in our access logs. The link length grows with text size, so for >2KB samples copy the regex via Copy /pattern/flags and paste the text separately. For collaborative regex review without sharing the actual text, share just the pattern and flags — the recipient pastes their own corpus and gets the same matches.
Does the tester support Unicode, emoji, and non-Latin scripts?
Yes. Enable the /u flag to opt into full Unicode handling: \w matches Latin word characters (the default semantic), but with /u you can match wider categories via Unicode property escapes — `\p{Letter}` matches every letter in every script, `\p{Script=Han}` matches Chinese ideographs, `\p{Emoji}` matches emoji, `\p{Number}` matches every digit/numeral. Without /u, surrogate-pair emoji like 👨‍💻 are seen as two UTF-16 code units and patterns like `^.$` will fail to match them; with /u the dot treats each grapheme code point as one character. For RTL scripts (Arabic, Hebrew), patterns work without special handling — direction is a render-time concern, not a regex-engine concern. CJK content matches the same way as Latin.
What's the difference between .match, .matchAll, .replace, and .split with a regex?
String.prototype.match returns the first match (or an array of all matches when /g is set) but loses capture groups when /g is on. String.prototype.matchAll requires /g and returns an iterator of match arrays WITH capture groups and indices — what this tester uses internally. String.prototype.replace accepts either a string template ($1, $&, etc) or a callback called per match with (match, ...groups, offset, string, namedGroups). String.prototype.split splits on every match — useful with /g but the global flag is ignored for split semantics. This tool exposes match via the Match tab, replace via Replace, and split via Split, so you can preview every flavour without leaving the page; the literal /pattern/flags is one click away when you're ready to paste into code.
Why doesn't my Python or Java regex work here?
Because this tester runs ECMA-262 (JavaScript) — most patterns port cleanly, a handful don't. Common porting gotchas: (1) Python's inline flags `(?i)` and `(?x)` are not valid in JS — use the flag chips above instead. (2) Python's `\A` and `\Z` are `^` and `$` in JS (with /m for line anchors). (3) Java/Python conditionals `(?(name)yes|no)` are not supported in JS — rewrite with an alternation. (4) Possessive quantifiers `a++` and atomic groups `(?>...)` are JS-unavailable — simulate with `(?=(a+))\1`. (5) Python `(?P...)` is `(?...)` in JS. (6) `\h` for horizontal whitespace and `\v` for vertical are not in JS — use `[ \t]` and `[\n\r]`. For a portable port, the explainer breaks down what each token does so you can swap the unsupported syntax for an equivalent.
Is there a maximum text size or match count?
Practical limit: about 200,000 characters of test text and 500 highlighted matches displayed at once. Beyond 500 matches the Matches panel shows a Showing first 500 banner; the count badge still reports the true total. The 250ms wall-clock budget bounds runaway patterns regardless of size. For multi-megabyte log files, run the regex with command-line `grep -oE` or `rg` (ripgrep) — they stream and won't hit a UI rendering cap. For one-off scans of huge text, paste a representative slice into this tester to validate the pattern, then run the validated pattern against the full file in your shell.

Related Tools

View all tools →