How to Convert Markdown to HTML
How Markdown-to-HTML conversion works, common syntax to know, and why sanitizing the output matters when Markdown comes from an untrusted source.
Markdown has become the default way developers write formatted text, READMEs, documentation, comments on GitHub and countless other platforms, and increasingly, content management systems that store rich text as Markdown rather than raw HTML. Converting that Markdown into HTML is a well-understood process, but doing it safely, especially when the Markdown comes from an untrusted source, requires understanding a step that's easy to skip: sanitization.
What Markdown-to-HTML Conversion Actually Does
Markdown was designed by John Gruber as "a text-to-HTML conversion tool," and that's exactly what a Markdown processor does under the hood: it parses a restricted, easy-to-type syntax and generates the equivalent HTML markup. A line starting with ## becomes an <h2> element. Text wrapped in **asterisks** becomes <strong>. A line starting with - becomes a list item inside a <ul>. The mapping is mostly mechanical, which is exactly why Markdown is popular, it's fast to type and the resulting formatting is predictable.
Common Markdown Syntax
A practical subset covers the vast majority of real-world Markdown:
- Headings:
# H1,## H2, through###### H6 - Bold:
**bold text**or__bold text__ - Italic:
*italic text*or_italic text_ - Links:
[link text](https://example.com) - Images:
 - Lists: lines starting with
-,*, or+for unordered lists;1.,2., etc. for ordered lists - Blockquotes: lines starting with
> - Inline code: text wrapped in single backticks, like
`code` - Code blocks: text wrapped in triple backticks, optionally with a language identifier for syntax highlighting
- Tables (in GitHub-flavored Markdown): pipe-delimited rows with a header separator row of dashes
GitHub-flavored Markdown (GFM) extends the original specification with tables, strikethrough (~~text~~), task lists, and automatic linking of bare URLs, this extended dialect is what most modern tools, including GitHub itself, actually implement.
Why Sanitization Matters
Here's the part that's easy to overlook: Markdown explicitly allows raw HTML to be embedded directly in the source. This was an intentional design decision by Markdown's original author, meant to let authors drop into HTML for anything Markdown's syntax doesn't cover. In practice, it means a Markdown-to-HTML converter, run naively, will pass through any HTML present in the input, including <script> tags, onerror and onclick attributes on other elements, and other constructs that can execute arbitrary JavaScript in the context of whatever page renders the output.
This becomes a real security problem the moment Markdown comes from a source you don't fully control, a user comment, a public wiki, an imported document, or content pasted from somewhere unknown. Rendering unsanitized converter output directly into a page is a textbook cross-site scripting (XSS) vulnerability: an attacker writes Markdown containing a malicious script, your converter faithfully turns it into HTML, and that HTML executes in every visitor's browser.
The mistake to avoid: treating Markdown conversion as "just formatting" and rendering the output HTML directly with something like dangerouslySetInnerHTML in React (or the equivalent in any framework) without an intermediate sanitization step.
How Sanitization Works
A sanitizer takes HTML as input and returns HTML with dangerous elements and attributes removed, based on an allowlist of what's considered safe. A reasonable allowlist for converted Markdown typically includes:
- Safe structural tags: headings, paragraphs, lists, tables, blockquotes, code blocks
- Safe inline tags: bold, italic, strikethrough, inline code, links, images
- Safe attributes:
hrefandtitleon links,srcandalton images, but not event handler attributes likeonclickoronerror, and notjavascript:URLs inhreforsrc
Anything not on the allowlist, <script>, <iframe>, <style>, inline event handlers, data: URIs used maliciously, and similar constructs, gets stripped entirely, not merely escaped. The result is HTML that preserves all the legitimate formatting Markdown produces while eliminating anything that could execute code or otherwise misbehave when rendered.
A Safe Conversion Workflow
- Parse the Markdown into HTML using a standard parser (implementations like
marked,markdown-it, orremarkin the JavaScript ecosystem all do this well). - Sanitize the resulting HTML through a dedicated sanitization library (like DOMPurify) rather than attempting to write allowlist logic from scratch, HTML sanitization has many subtle edge cases, and a maintained library will handle them far more reliably than a hand-rolled regex-based approach.
- Render the sanitized output, either by inserting it into the DOM directly or, if the target isn't a browser, by treating it as trusted HTML for output.
Skipping step 2 is the single most common mistake in Markdown-to-HTML pipelines that accept content from outside your own team.
When Sanitization Matters Less
If the Markdown source is fully trusted, content you or your team wrote yourselves, checked into version control, reviewed like code, the security risk from raw HTML is much lower, since you're not defending against an adversarial input. Even then, sanitizing by default is a reasonable habit: it costs almost nothing, and it means the same pipeline stays safe if the content source ever changes (for example, if a "trusted, internal" Markdown field later gets exposed to broader user input without anyone updating the rendering logic).
Try It
Our Markdown to HTML tool converts GitHub-flavored Markdown to HTML with a live preview, and every bit of output HTML is run through sanitization before it's rendered or made available to copy or download, so you can safely experiment with untrusted Markdown, including content specifically designed to test XSS handling, without risk. Everything happens locally in your browser.