/^/
MediumWeb6 min read

HTML Tag Regex

Finds opening and closing HTML tags such as <div>, </span>, or <br/> by matching an angle bracket, an optional slash, a tag name, and any attributes up to the closing bracket.

#html#web#parsing#markup#tag#dom

Regex Pattern

<\/?([a-zA-Z][a-zA-Z0-9]*)\b[^>]*>

Pattern Breakdown

Hover over a token to see what it does.

<\/?([a-zA-Z][a-zA-Z0-9]*)\b[^>]*>
TokenMeaning
<Literal opening angle bracket that starts every tag
\/?Optional forward slash, present on closing tags like </div>
([a-zA-Z][a-zA-Z0-9]*)Capturing group for the tag name: a letter followed by letters or digits
\bWord boundary ensuring the tag name is not immediately followed by more identifier characters
[^>]*Any characters except '>' — matches attributes, quoted values, and whitespace
>Literal closing angle bracket that ends the tag

Detailed Explanation

What it does

This pattern locates HTML tag-shaped substrings within text: an opening angle bracket, an optional slash for closing tags, a valid tag name, optional attributes, and a closing angle bracket. It captures the tag name in group 1 so callers can inspect which element was matched.

Why it works

The tag name must start with a letter and continue with letters or digits, matching real HTML element naming rules while excluding things like <1invalid>. The trailing [^>]* greedily consumes any attributes or whitespace without needing to understand their internal structure, simply stopping at the first '>' character, which keeps the pattern simple at the cost of not validating attribute syntax.

Common use cases

  • Quickly stripping or highlighting tags in a text editor or preview pane
  • Lightweight sanitization pre-pass before running a real HTML parser
  • Detecting whether a string of user input contains any HTML markup at all
  • Simple syntax highlighting or tag-counting tools

Edge cases

  • Self-closing tags like <br/> and <input type="text"/> are matched, including the trailing slash inside [^>]*
  • Tags with attribute values containing '>' inside quotes (e.g. <a title="5 > 3">) will incorrectly end the match early
  • HTML comments (<!-- ... -->) are partially matched as malformed tags since '!' isn't a valid tag-name start, so they are correctly skipped
  • Malformed markup like <div<span> can produce unexpected partial matches since there's no real nesting awareness

Limitations

  • This is not a real HTML parser — it cannot handle nested quotes, malformed markup, or CDATA sections correctly
  • Does not validate that opening and closing tags are properly balanced or nested
  • Attribute contents are not parsed or validated, only skipped over as opaque text
  • For any production HTML manipulation, use a real parser (DOMParser, Cheerio, BeautifulSoup) instead of regex

Interactive Tester

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

<div> <div class="container"> </div>
3 matches· capture groups: 1

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 tagRegex = /<\/?([a-zA-Z][a-zA-Z0-9]*)\b[^>]*>/g;
const tags = html.match(tagRegex);

Common Mistakes

Using a regex like this to sanitize or strip user-submitted HTML for security purposes

Fix: Never rely on regex for HTML sanitization — use a dedicated library (DOMPurify, bleach, HtmlSanitizer) that understands the full HTML grammar

Assuming [^>]* correctly handles attribute values that themselves contain '>' inside quotes

Fix: For anything beyond quick text scanning, parse with a real HTML parser instead of extending the regex

Forgetting the global flag when trying to find all tags in a document, so only the first match is returned

Fix: Use the g flag with match()/matchAll() in JS, or the language's equivalent find-all API, to retrieve every tag

Performance Notes

  • [^>]* is a negated character class, which matches efficiently in linear time with no catastrophic backtracking risk
  • The word boundary \b after the tag name is cheap since it's a zero-width assertion, not an additional scan
  • On very large HTML documents, a streaming or DOM-based parser will outperform and out-scale repeated global regex scans

Browser Compatibility

EngineSupportedNotes
ChromeYes
FirefoxYes
SafariYes
EdgeYes
Node.jsYes