Skip to content

MD091 - Markdown inside an HTML block renders as literal text

Aliases: no-markdown-in-html

This rule is opt-in. Enable it with extend-enable = ["MD091"] in the [global] section of your config.

What this rule does

Reports a link or an image written inside an HTML block, where CommonMark does not parse it. The line renders as the characters you typed, brackets and all.

Why this matters

A centered banner is the usual way to meet this. It looks like it should work, and every part of it is valid on its own:

<div align="center">
  <img src="../logo.png" width="200">
  <br>
  [Documentation](https://example.com/docs) | [Changelog](CHANGELOG.md)
</div>

The two links there are published as [Documentation](https://example.com/docs) | [Changelog](CHANGELOG.md). Nothing warns about it: the markdown is well formed, the HTML is well formed, and the failure is that the two were combined.

The reason is CommonMark's HTML block. A line starting with <div> (or another block-level tag) opens a block that runs until a blank line, and inside it every line is passed through to the output untouched. So the block above extends from <div align="center"> through </div>, and the links sit inside it.

This is easy to get wrong in the other direction too. Inline HTML does not do this, so the same links after a <span> render normally, and an author who has seen one case work is likely to assume the other does as well.

The remedy is a blank line, which ends the block and returns the rest to markdown:

<div align="center">
  <img src="../logo.png" width="200">

[Documentation](https://example.com/docs) | [Changelog](CHANGELOG.md)

</div>

Note how little separates the two. The blank line is the whole difference between a working banner and a broken one, and neither version looks wrong on its own.

Examples

Incorrect

<div align="center">
[Documentation](https://example.com/docs)
</div>
<table>
<tr><td>![Build status](badge.svg)</td></tr>
</table>

Correct

A blank line after the opening tag ends the HTML block, so the markdown below it is markdown again:

<div align="center">

[Documentation](https://example.com/docs)

</div>

Inline HTML opens no block at all, so nothing in this line is affected:

<span class="badge">[Documentation](https://example.com/docs)</span>

Writing the link as HTML is equally correct, and is what the surrounding block already is:

<div align="center">
<a href="https://example.com/docs">Documentation</a>
</div>

Configuration

This rule has no options.

What this rule leaves alone

The test for a finding is whether the construct would have rendered differently outside the block. Several shapes fail that test and are silent.

Reference links whose label is not defined. [text][ref] is a link only when ref has a definition somewhere in the document. Without one it is literal text in both contexts, so the HTML block broke nothing:

<div>
The element at arr[i][j] is copied to matrix[0][1].
</div>

This is what separates a real dead link from the bracket grammar that fills HTML tables in reference documentation, where [ a | b ] and [a-z][0-9] are common and none of it is a link. A reference link with a definition is reported, because that one really would have resolved:

<div>
[Documentation][docs]
</div>

[docs]: https://example.com/docs

Anything inside a tag. <div title="see [docs](/docs)"> puts the construct in an attribute value, which no markdown parser reads in any context.

<code> elements and backtick spans. An inline <code> element does not stop markdown, so <code>[a](b)</code> outside a block genuinely does produce a link inside the code element, and a backtick span outside a block genuinely does become <code>. Both are nevertheless left alone, because an author who wraps a construct in either is asking for it to be shown rather than followed, and inside the block that is what they get. Reporting a broken link there would name the wrong problem.

Raw-text elements. pre, script, style and textarea hold literal text by design, so markdown-looking characters in them are content:

<pre>
[not a link](nowhere)
</pre>

markdown="1" containers. kramdown, Python-Markdown and MkDocs parse those bodies as markdown, so for those users the markdown really is markdown:

<div markdown="1">
[Documentation](https://example.com/docs)
</div>

HTML comments. Nothing inside one reaches the page.

Only links and images are reported. Emphasis, headings, lists and other constructs are also inert inside an HTML block, but they degrade to text that still reads correctly, while a link degrades to a URL the reader cannot follow.

Automatic fixes

None. A blank line after the opening tag is usually what the author wanted, but not always: it splits one HTML block into two, which changes what CSS selectors and align attributes apply to, and in a <table> it produces invalid markup that browsers relocate. Converting the link to an <a> tag instead is a different document. Which of those is right depends on what the block is for, so the choice stays with the author.