Raw HTML inside Markdown: what renders and what doesn't
Bobby Huang ·
Markdown files can hold raw HTML. The CommonMark spec passes HTML tags through to the output without escaping them, both inside a sentence and as whole blocks. The spec says nothing about filtering, so each viewer picks its own policy. Some render the tags, some strip them, and some print the tags as visible text. Markdown inside an HTML block is not parsed until the block ends. For block tags like div, that end is a blank line. So leave an empty line below the opening tag and above the closing one if you want Markdown in between. A block that opens with pre, script, style or textarea is different: a closing pre, script, style or textarea tag ends it, and blank lines do not. The safest choice for files many people read is plain Markdown.
Key takeaways
- The spec passes raw HTML through as-is. It does not say whether a viewer must render, strip or escape it.
- Inside an HTML block, Markdown stays literal until the block ends. For tags like div and details, a blank line ends the block.
- Blank lines around the inner text let Markdown render inside a tag such as details.
- Common tags like b, i, a, h2 and ul have a plain Markdown form.
- The v1 plan for Sashmark, which is in development and has not been released, treats raw HTML as inert in its live render.
Agents sometimes write HTML inside Markdown. A collapsed test log, a keyboard key, or a small table can come out as tags. Whether those tags show up right depends on two things: the parsing rules and the viewer you open the file in.
Our Markdown syntax guide gives raw HTML a short section. Below, we look at how the spec treats HTML, the blank-line rule for Markdown inside a tag, and how to rewrite an agent's tags as plain Markdown.
Two ways HTML shows up
The CommonMark spec splits raw HTML into two kinds. One lives inside a sentence. The other takes up whole lines.
HTML inside a sentence
Water is H<sub>2</sub>O. Press <kbd>Enter</kbd> to save.
CommonMark produces one paragraph. The <sub> and <kbd> tags pass into the output as they are, with no escaping. If the viewer keeps the tags, a browser may show a small 2 and a styled key, unless the page's styles change it.
Tag names are not checked against a list of real HTML tags. A made-up tag counts too.
Build result: <status>green</status>
CommonMark passes <status> and </status> through as raw HTML. A browser does not know that tag, so a viewer that keeps it will likely show just the word "green".
HTML blocks
An HTML block is a run of lines that the spec treats as HTML from start to end. The spec lists seven kinds. They differ in how they start and stop. Three of them matter most for agent files.
- Block tags like
div,details,summary,table,p,section,ul,liandh1toh6. Any line opening with one of these tags starts a block. The next blank line closes the block. - Comments, which start with
<!--. The block ends at-->. pre,script,styleandtextarea. A line holding a closing pre, script, style or textarea tag ends the block, even if blank lines come first. That closing tag does not have to match the opening one.
There is also a catch-all kind for any other complete tag that sits alone on a line. A blank line closes it as well. Unlike the block tags above, it can't break into the middle of a paragraph.
The blank-line rule
Inside an HTML block, the spec passes each line through as-is. Markdown in those lines is not parsed. Parsing picks up again only after the block ends.
Here is a collapsed section the way an agent might write it:
<details>
<summary>Test log</summary>
**3 tests failed** in `auth.test.ts`.
</details>
CommonMark reads all four lines as one HTML block, because there is no blank line to end it. The asterisks and backticks reach the output as plain characters.
Now add two blank lines:
<details>
<summary>Test log</summary>
**3 tests failed** in `auth.test.ts`.
</details>
CommonMark now sees three parts. The first two lines form an HTML block that ends at the blank line. The middle line is a normal paragraph, so "3 tests failed" turns bold and the file name becomes code. The closing </details> is its own small HTML block. The same fix works with div and other block tags.
The blank lines do nothing for pre and the other tags in its group:
<pre>
Step 1: **run the tests**
Step 2: check the log
</pre>
CommonMark treats everything from <pre> to </pre> as one HTML block, blank line and all. The asterisks stay literal. A browser may show the text in a fixed-width font with the line breaks kept, unless the page's styles change it.
Comments work the same way. Everything between <!-- and --> passes through, and a browser does not normally display comments. That makes them easy to miss. The post on rendered versus raw agent Markdown covers why hidden notes are worth a search.
What a viewer may do with the HTML
The spec says what HTML the parser passes along. It says nothing about filtering, sanitizing or blocking it. That choice belongs to each viewer, and viewers differ a lot.
- Render it. The tags become real HTML on the page. A details tag may fold, and a table may draw.
- Strip it. The tags are removed. Some viewers keep the text between them, and some drop it.
- Escape it. The tags show on the page as plain text, angle brackets and all.
- Filter part of it. Some viewers keep harmless tags like
suband drop tags that could run code or load outside content.
So the same file can look tidy in one app and messy in another. If a file relies on HTML to read well, open it in the viewer your readers will use before you share it.
A file from someone else can carry tags meant to load outside content or run code. Know what your viewer does with HTML before you open that file rendered.
Rewrite an agent's HTML as plain Markdown
Plain Markdown reads much the same in the raw file and on the page. Use this pass to convert a file.
- Find the tags. Run
grep -nE '<[a-zA-Z/!]' notes.mdto list lines with an angle bracket followed by a letter, a slash or!. This also catches links written in angle brackets and tags inside code blocks, so skim the hits. - Swap the inline tags.
<b>and<strong>become**text**.<i>and<em>become*text*.<code>becomes single backticks.<a href="https://example.com">site</a>becomes[site](https://example.com). - Swap the block tags.
<h2>becomes a line starting with##. A<ul>with<li>items becomes lines that start with-. An<img>becomes; see Markdown images for paths. An HTML table can become a Markdown table. - Replace
<br>. Start a new paragraph or a new list item instead. Neither one needs a tag. - Decide on
details. Markdown has no collapsed section. Either keep the tag and add the blank lines from the example above, or turn it into a heading with the text under it. - Drop tags with no job. A
<span>, a<div>or a made-up tag that only wraps text can go. Keep the text. - Check the result. Open the file rendered and scroll the whole page. Any angle brackets left on screen are worth a look. Some are plain text or code, and some are tags that slipped through.
To stop the HTML at the source, add a rule to your agent's instruction file: write Markdown, and use HTML only for a collapsed section. Then check the next file it hands you. There is more on agent instruction files in the post about docs an agent can follow. For a full review pass, see the agent Markdown review guide.
Where Sashmark fits
The Sashmark desktop app is being made to open and edit the Markdown your agents write. It is in development and has not been released. The app itself has no AI features. Its v1 plan includes a live render where raw HTML is inert. The same plan skips fetching remote images. See the planned features, the help pages and the roadmap for where each part stands.
You can see what Sashmark is and follow along at sashmark.com.
Questions
- Why do my asterisks show up inside a details tag?
- The details tag starts an HTML block, and the spec passes the lines of that block through without reading them as Markdown. Leave one empty line below the summary line and one more above the closing details tag. The text between them then parses as normal Markdown.
- Can HTML in a Markdown file be unsafe?
- That depends on the viewer. Nothing in the spec filters HTML. Some viewers strip or escape risky tags, and some render what they get. If you did not write the file, look at its source text before rendering it, or pick a viewer that never runs HTML.
- Should I tell my AI agent not to write HTML in Markdown?
- For notes, plans and handoff files, yes. Plain Markdown tends to read the same in more places. You can write a rule in the agent's instruction file that asks for Markdown only, with HTML kept to cases that truly need it, such as a collapsed section.