Regex Tester for JavaScript Patterns

Type a pattern, paste the text you want to search, and every match lights up as you type. The panel beside your text lists each match with its groups, previews a replacement, breaks the pattern into plain English, and writes the same pattern for Python and PHP.

Paste a full literal such as /ab+c/gi and the flags split off on their own.

Load sample
0 characters
Matches
0
Groups
0
Time
0 ms

Matches show up here. Click a row to select the match in your text.

sample / access log

Reading the access log sample

The Failed requests in a log sample loads five lines from an Nginx access log and a pattern hunting for errors. Two lines match: a POST to /api/generator/jojo-stand rejected with 429, and a GET for /favicon.ico answered with 404. Here is why each piece is there.

  • "(?<method>GET|POST)
    Starts at the opening quote of the request. The IP address and timestamp come before the quote, so they never get tested against GET or POST.
  • (?<path>\S+)
    Takes characters up to the next space. A request path holds no raw spaces, so \S+ stops in the right spot without a lookahead.
  • [^"]+"
    Skips the protocol version and stops at the closing quote. A negated class finishes in one pass. Writing .+" instead runs to the end of the line first, then backs up one character at a time.
  • (?<status>[45]\d\d)
    Accepts 4xx and 5xx codes only. Change [45] to [2-5] and all five lines match.

The Matches tab labels each group by name, so you read method POST, path /api/generator/jojo-stand and status 429 instead of counting brackets. Click a row and the tester selects the same span in your text.

flags / g i m s u y

What each flag changes

Most "my regex works in one place but not another" reports come down to a flag. The table lists the six toggles above the text box and the moment each one makes a visible difference.

FlagEffectWhere you notice the change
gKeeps searching after the first match.Turn g off and the Matches tab shows one row, and Replace swaps only the first hit.
iUpper and lower case count as equal.error finds ERROR and Error in mixed log output.
m^ and $ match at every line break, not only at the edges of the whole text.^\d+ against a numbered list returns every number instead of the first one.
s. matches line breaks too.<p>.*?</p> picks up paragraphs split over several lines.
uReads the pattern as Unicode code points. Turns on \p{...} and \u{1F600}, and rejects loose escapes such as \- outside a class.^.$ matches a single emoji only with u. Without u, an emoji counts as two units.
yA match has to start exactly where the last one ended. No scanning ahead.With y alone, the match must start at position 0. With g and y, matches have to sit back to back.

The d and v flags are left out on purpose. d only adds index arrays, and the Matches tab already shows each start and end position. v changes character class syntax in ways older browsers reject.

port / python + php

Moving a pattern to Python or PHP

Regex syntax looks universal until you paste a JavaScript pattern into Python and get a re.error. These are the differences we see trip people up most.

FeatureJavaScriptPython rePHP preg_*
Named group(?<id>...)(?P<id>...)Both forms
Named backreference\k<id>(?P=id)\k<id> or (?P=id)
Group in the replacement$1, $<id>\1, \g<id>$1 or ${1}, no names
LookbehindAny lengthFixed width onlyFixed width per branch
$ without mEnd of the text onlyEnd of the text, or right before a final newlineSame as Python unless you add the D modifier
\d0 to 9 onlyAny Unicode digit in str patterns0 to 9, any Unicode digit with the u modifier
Inline flagsNo (?i). Recent browsers accept the scoped form (?i:...)(?i) at the start(?i) anywhere

The Code tab does the mechanical swaps for you. Named groups become (?P<name>...) for Python, \u escapes become \x{...} for PHP, and $1 in the replacement becomes \g<1>. Behavior differences, such as lookbehind width or the meaning of $, show up as notes under the snippet instead of a silent rewrite, since rewriting them would change what the pattern matches.

timeout / 2000 ms

When a pattern never finishes

Some patterns take exponential time on text they fail to match. The usual shape is a quantifier inside a repeated group, such as (\w+\s?)+$ or (a|aa)+. On a 37 character line with a stray ! at the end, the engine tries every way to split the word characters between the inner and outer + before giving up. The number of ways doubles with each extra character.

In a normal page, a pattern like this freezes the tab. This tester runs every pattern in a Web Worker and shuts the worker down after 2 seconds, then reports a timeout. Press Runaway pattern under the pattern box to see the cutoff fire.

Times out^(\w+\s?)+$
Fails in microseconds^\w+(?:\s\w+)*\s?$

Both patterns accept the same lines. The second one works because every repetition has to start with whitespace, so the engine has one way to split the text, not millions. Three habits keep patterns out of this trap:

  • Give each repetition a fixed start. If the inner and outer quantifier both eat the same characters, rewrite the group so they do not overlap.
  • Prefer negated classes over dot. [^,]*, stops at the first comma. .*, runs to the end of the line and walks back.
  • Test the line you expect to reject. Patterns look fast on input they match. Paste a line where the bad character comes last and watch the Time figure.

A timeout here does not prove a pattern runs forever. A slow phone and a large paste both add time. On a short line, treat anything above a few hundred milliseconds as a warning sign for production code.

limits / read before trusting a result

Where this tester stops

  1. JavaScript syntax only. Possessive quantifiers (a++), atomic groups ((?>...)), recursion, \A, \Z and Python's (?P<name>...) throw a syntax error here, even though PCRE accepts all of them. Use the tester to check JavaScript behavior, then read the Code tab notes before shipping to another language.
  2. The first 500 matches get listed. The counter keeps going up to 100,000, and the Replace output always covers the whole text.
  3. Highlighting turns off above 200,000 characters. Matching still runs. For multi-megabyte logs, grep or ripgrep on your machine is the better fit.
  4. Results depend on your browser. Chrome, Firefox and Safari agree on standard syntax. Newer features such as scoped modifiers (?i:...) or duplicate group names throw errors in browsers from before 2025.
  5. Your text stays in this tab. Matching runs in your browser. Copy link stores the pattern, flags and text after the # in the URL. Browsers keep everything after the # away from the server, but anyone holding the link reads your text, so skip it for private data.

Regex tester questions

Short answers to what developers ask us most about testing patterns.

Which regex flavor does this tester use?

The JavaScript (ECMAScript) engine built into your browser, the same syntax Node.js accepts. Most PCRE and Python patterns work unchanged, but possessive quantifiers, atomic groups and the Python style (?P...) named group raise a syntax error.

Why does my pattern match here but fail in Python?

Four usual causes: Python wants (?P before a group name, lookbehind in Python needs a fixed width, the dollar sign in Python also matches before a final newline, and \d in Python matches digits from every script. Open the Code tab and pick Python to get a converted pattern plus notes on behavior that differs.

Why did the tester stop with a timeout?

The pattern ran longer than 2 seconds, which almost always means catastrophic backtracking. Look for a repeated group whose contents also repeat, such as (\w+\s?)+, and rewrite the group so each repetition starts with a character the previous one cannot consume.

How do I reuse a capture group in the replacement?

Write $1 for group 1, $2 for group 2 and so on. $& inserts the whole match and $$ inserts a literal dollar sign. For a named group, write a dollar sign followed by the name in angle brackets. The Reorder ISO dates sample turns 2026-03-14 into 14/03/2026 with $3/$2/$1.

What is the difference between the m and s flags?

They change different characters. The m flag makes the caret and dollar sign match at the start and end of each line. The s flag makes the dot match line breaks. Use m for line-by-line anchors and s for matching a block of text across lines.

Is the text I paste sent to a server?

No. Matching and replacing run in a Web Worker inside your browser. Copy link writes your text into the part of the URL after the # sign, which browsers do not send to servers, though anyone you share the link with sees the text.