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.
Regex Pattern
<!--[\s\S]*?-->Pattern Breakdown
Hover over a token to see what it does.
| Token | Meaning |
|---|---|
| <!-- | 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.
Test Cases
Editable — add your own inputs to see if they pass.
| Input | Expected | Result | |
|---|---|---|---|
| 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
| Engine | Supported | Notes |
|---|---|---|
| Chrome | Yes | — |
| Firefox | Yes | — |
| Safari | Yes | — |
| Edge | Yes | — |
| Node.js | Yes | — |
Work with this pattern
Take this expression further — test it against your own data, adapt it in the builder, or read up on the syntax behind it.
Regex Tester
Test a regular expression against sample text and see every match highlighted live.
Regex Generator
Build a pattern by clicking blocks — no syntax memorisation required.
Regular Expressions Guide
What regex is, how the syntax works, and the mistakes that trip people up.
Regex Cheat Sheet
Every token, quantifier, and assertion on one printable page.
Python Regex Guide
The re module end to end — search, match, findall, groups, and substitution.
Regex Pattern Library
100+ ready-to-use patterns for email, URLs, dates, IPs, and more.