← All Tools

Match Count Shows Up But No Highlight Appears — Why?

Guide · Last verified Aug 26, 2026

Sometimes you drop a pattern into a regex tester, run it, and it reports something like "3 matches" — yet no matter how closely you look at the results panel, not a single character is highlighted. It looks like a bug, but it's actually an inevitable outcome of how regex engines and highlight rendering interact. This guide walks through the cause at the code level.

1. The symptom: the number and the screen disagree

For example, if you drop the pattern a* into the live tester on the Regex Cheatsheet and type just "bcd" as the text, the match count shows up as several matches, yet not a single character of the text gets colored. Users naturally wonder, "if there are no matches, why is there a number?" — but in reality, matches do exist; they just exist in a form that can't be rendered on screen.

2. What is a zero-width match?

A regex match usually "consumes" a portion of the string. If the pattern abc matches the text "abc", it has consumed 3 characters. But quantifiers that allow "zero or more," like a*, or assertions that are designed from the start not to consume any characters — \b (word boundary), ^/$ (anchors), (?=...) (lookahead), (?<=...) (lookbehind) — can succeed while producing a match whose start and end positions are identical, i.e. a match of length zero. From the engine's perspective this is still unambiguously a "successful match," so it gets included in the count — but the core of the problem is that there's not a single character left to wrap in a highlight.

3. Why highlight rendering can't draw this

Most regex testers render match results by wrapping them as <span class="match-hl">matched string</span>. If the matched string is an empty string (""), the result is <span class="match-hl"></span> — an empty tag with no content. An empty span has nothing to paint a background color onto, so nothing changes visually — this isn't a rendering failure, it's that there are simply no pixels to color in the first place. Meanwhile, the logic that counts matches just counts the length of the array returned by matchAll(), so a zero-length match gets counted exactly the same as any other. The counting logic and the visualization logic operate on different criteria (existence vs. pixels), and that's where the gap between them comes from.

Measured example: Apply the pattern a* (with the g flag) to the text "bcd." The regex engine recognizes "zero a's" as a match at each of four positions — every character position (0, 1, 2) plus the end of the string (3) — and returns four empty matches in total. The match count display shows "4 matches," but since all four have length zero, not a single one is highlighted.
PatternTarget textMatch countOn-screen highlight
a*bcd4None (all length 0)
\bcat\bcat category1"cat" (3 chars) shown
(?=cat)cat category2None (lookahead doesn't consume)

4. Why word boundaries and lookahead are especially confusing

Assertions like \b or (?=...) are specified to only check "does the context before/after this position satisfy the condition," and by design they never include any character in the result. So using (?=cat) alone finds "every position right before the letters 'cat'," but the matched string is always an empty string. A common source of confusion for beginners is expecting the lookahead to also highlight the text that follows it — but in reality, the lookahead itself consumes nothing, so unless you chain an actually-consuming pattern like cat after it, nothing appears on screen at all.

5. So how do you check for this?

If you run into "a match count shows up but there's no highlight," the first move isn't to suspect a bug — it's to check whether your pattern contains a zero-width element (a quantifier allowing zero occurrences, an anchor, a lookahead/lookbehind). In the live tester on the Regex Cheatsheet, toggling the g, i, m, s, u, and y flags one at a time while testing your actual pattern lets you see exactly where zero-width matches occur. And if you want to rebuild the pattern into a different form, the Regex Generator is a good place to start over from standard patterns like emails or URLs.

Frequently Asked Questions

Q. Should a zero-width match really count as a real "match"?

A. Yes. Under standard regex engine behavior, a match of length zero is a fully valid match success. JavaScript's String.matchAll() and RegExp.exec() both return it as-is. During replace operations, though, engines apply special handling to force the search position forward by one to avoid infinite loops.

Q. If there's no visible highlight, can I just treat it as no match?

A. It depends on your purpose. If you only need to check whether a given position satisfies a condition (e.g. finding replacement positions), the match is genuinely valid and useful. If instead you want to visually confirm exactly what text was matched, you need to add a part of the pattern that actually consumes characters.

Q. Is there a way to make a lookahead show up as a highlight?

A. Not with the lookahead alone, since it never consumes anything by itself. If you place a consuming portion in front (e.g. cat in cat(?=egory)) and keep the lookahead purely as a trailing condition check, the consumed "cat" part will show up highlighted.

Q. Does this behave the same way in other languages like Python or Java?

A. Yes. The concept of zero-width matches is common across the standard regex family (the PCRE lineage), so Python's re module, Java's Pattern class, and others all correctly return zero-length matches the same way. How each tool's tester UI chooses to visualize them, however, can differ.