/^/
MediumWeb5 min read

HTML Comment Regex

Detects an HTML comment, <!-- ... -->, using a lazy quantifier so the match stops at the first closing --> instead of swallowing everything up to the last one in the document.

#html#comment#web#markup#lazy-matching

Regex Pattern

<!--[\s\S]*?-->

Pattern Breakdown

Hover over a token to see what it does.

<!--[\s\S]*?-->
TokenMeaning
<!--The literal opening delimiter of an HTML comment
[\s\S]A character class matching absolutely any character, including newlines (since plain . cannot match line breaks)
*?A lazy quantifier: match as few characters as possible while still allowing the rest of the pattern to succeed
-->The literal closing delimiter that ends the comment

Detailed Explanation

What it does

This pattern finds an HTML comment anywhere in a string, from <!-- to the nearest following -->, and correctly handles comments that span multiple lines because [\s\S] matches newlines as well as any other character.

Why it works

[\s\S]*? is the classic 'match anything, lazily' idiom in JavaScript regex, used because the dot metacharacter never matches line-break characters unless the /s (dotAll) flag is set. Making the quantifier lazy (*? instead of *) is essential: with a greedy *, the engine would first try to consume all remaining characters in the string, then backtrack until it finds the LAST --> in the input, potentially swallowing several separate comments (and the real markup between them) into a single incorrect match. The lazy form instead stops at the first --> it encounters, correctly isolating one comment at a time.

Common use cases

  • Stripping HTML comments out of a template or generated markup string before rendering
  • Extracting documentation or metadata embedded in HTML comments (e.g. <!-- TODO: ... -->)
  • Validating that a snippet of markup contains a properly closed comment before saving it
  • Building a simple HTML sanitizer or minifier step that removes comment nodes

Edge cases

  • A comment spanning multiple lines, like <!-- line one\nline two -->, matches correctly because [\s\S] includes \n
  • An empty comment, <!---->, still matches since *? allows zero characters between the delimiters
  • An unterminated comment with no closing -->, like <!-- oops, correctly fails to match anything
  • With the global flag and .match()/.exec(), two separate comments in one string, <!--a--><!--b-->, are correctly returned as two distinct matches only because the quantifier is lazy; a greedy version would incorrectly merge them into a single match spanning both

Limitations

  • Does not validate the HTML spec's restriction that comment content must not start with '>', '->' or contain '--' or end with '-'
  • Does not distinguish a comment that appears inside a <script> or <textarea> block, where '<!--' has different parsing rules
  • Regex-based comment stripping is not a substitute for a real HTML parser when handling untrusted or malformed markup

Interactive Tester

Edit the pattern or text below — matching runs live in your browser.

<!-- comment --> <!-- multi line --> <!---->
3 matches

Test Cases

Editable — add your own inputs to see if they pass.

InputExpectedResult
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass

Language Variants

Production-ready examples in 12 languages.

const htmlCommentRegex = /<!--[\s\S]*?-->/;
console.log(htmlCommentRegex.test('<!-- note -->')); // true

// Strip all comments (lazy quantifier keeps each match isolated)
const stripped = '<!--a--><p>x</p><!--b-->'.replace(/<!--[\s\S]*?-->/g, '');

Common Mistakes

Using a greedy quantifier, <!--[\s\S]*-->, which spans from the first <!-- all the way to the LAST --> in the document, merging multiple separate comments (and the markup between them) into one match

Fix: Use the lazy quantifier *? so the match stops at the nearest closing --> instead of the farthest one

Using . instead of [\s\S] and forgetting that . does not match newline characters by default, causing multi-line comments to fail

Fix: Use [\s\S] (or enable the /s dotAll flag with . in engines that support it) so the match spans line breaks

Relying on this regex to safely sanitize untrusted HTML for security purposes

Fix: Use a proper HTML parser/sanitizer library for untrusted input; regex-based stripping can be bypassed by malformed or adversarial markup

Performance Notes

  • The lazy *? quantifier can require more backtracking steps than a greedy * on pathological inputs with many partial '--' sequences, but stays linear for typical HTML
  • Prefer the global flag with matchAll()/exec() loops rather than manually scanning indices when extracting many comments from a large document
  • For very large documents, a streaming HTML parser will outperform and outscale a single big regex pass

Browser Compatibility

EngineSupportedNotes
ChromeYes
FirefoxYes
SafariYes
EdgeYes
Node.jsYes