Markdown to HTML Converter
Convert GitHub-flavored Markdown to clean, sanitized HTML with a faithful preview — entirely on this device.
Applies to the downloaded .html file only.
Advanced
Raw HTML passes to the copied and downloaded source unsanitized. The preview stays sanitized.
The rendered preview appears here as you type.
Press ? for shortcuts
Do it offline:$ pandoc -f gfm README.md -o readme.html
How to use it
- Paste Markdown or drop a .md file — binary files are refused, and a dropped file never triggers a network request.
- Set the GFM toggles; tables, strikethrough, task lists and autolinks start on, anchor ids and hard breaks off.
- Read the HTML tab or switch to Preview; a notice itemizes anything removed.
- Copy the source or download it as .html, optionally wrapped as a complete document.
Paste GitHub-flavored Markdown and read the finished HTML beside a rendered preview. The parser speaks CommonMark 0.31.2 plus the GFM extensions — tables, task lists, strikethrough, autolinks, footnotes — and the preview passes through DOMPurify before it touches the page. Whatever sanitization strips is itemized in a notice above the output, never silently edited away. Conversion runs on this device.
How it works
markdown-it parses the document against CommonMark 0.31.2 with the GFM extensions layered on; unsafe link targets — javascript:, vbscript:, most data: — are refused inside that parse, each refusal recorded.
DOMPurify then sanitizes the rendered markup before the preview shows it; with passthrough off the same string feeds Copy and Download, and removals from both passes fold into one counted notice.
Examples
| Item | Done |
| --- | --- |
| Docs | yes |
<table>
<thead>
<tr>
<th>Item</th>
<th>Done</th>
</tr>
</thead>
<tbody>
<tr>
<td>Docs</td>
<td>yes</td>
</tr>
</tbody>
</table>
The delimiter row fixes the column count, and the pipes become a real table element with thead and tbody — markup DOMPurify passes byte-for-byte, so pane and preview agree.
- [x] ship W1
- [ ] tag release
<ul>
<li class="task-list-item"><input type="checkbox" disabled="" checked=""> ship W1</li>
<li class="task-list-item"><input type="checkbox" disabled=""> tag release</li>
</ul>
Each item renders a checkbox matching its bracket, every one carrying the disabled attribute — the GFM shape, deliberately inert wherever the markup lands.
# Demo
<script>alert(1)</script>
Text between.
<script>alert(2)</script>
[click](javascript:alert(1))
<h1>Demo</h1>
<p>Text between.</p>
<p>[click](javascript:alert(1))</p>
Even with Raw HTML passthrough on, the preview shows this sanitized output and reports "Removed during sanitization: 2 <script> tags, 1 javascript: URL" — the javascript: link never parses as a link, neutralized before DOMPurify ever ran.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Raw HTML stays escaped until you opt in
Off, raw segments become visible escaped text, counted by a chip. On, raw markup reaches only the copied and downloaded source — and a block that opens the document is hoisted into the head by the browser's parser, leaving the preview without a notice line; the chip still counts it.
Reference links and footnotes both resolve
A [text][label] link takes its URL from a definition line elsewhere. Footnotes are the reference-style subset: [^label] with a [^label]: definition builds the numbered end section, inline ^[…] footnotes are out of scope, and an undefined reference stays literal.
CRLF and LF convert byte-identically
Windows line endings produce exactly the HTML their LF twin does — normalization happens before parsing, pinned by a golden vector.
Nesting past 100 levels degrades, never crashes
markdown-it bounds nesting at 100; a blockquote ladder 300 deep still returns HTML instead of overflowing.
Right-to-left text previews as it should
The preview region carries dir="auto", so a document opening in Arabic or Hebrew lays out right-to-left while the HTML tab stays a code view.
Two routes to a line break
Two spaces at a line's end always yield a br element, per CommonMark; the line-breaks option breaks every newline — chat transcripts yes, wrapped READMEs no.
Multi-megabyte input moves off the main thread
From 256 KB the parse runs in a worker so typing never stalls; sanitizing stays on the page. Past 4 MB the tool refuses rather than truncating.
Exactly which URLs autolink
Absolute http, https and mailto URLs, www. hosts and bare e-mail addresses become links; a bare domain such as example.com stays text, as does a www. host whose top-level domain the linkifier's list lacks.
Common errors
Input is larger than the 4 MB limit for this tool.- The one hard failure conversion has — CommonMark assigns meaning to every input, so only size refuses. Nothing is truncated; the island banner adds the pandoc line.
passthrough off: 1 raw HTML segment(s) escaped — reported, non-blocking- The default posture turned a raw segment into visible escaped text. The count is exact — segments, not lines — and conversion continued.
passthrough on: 2 raw HTML segment(s) passed through unsanitized — the preview stays sanitized- The advanced toggle's honesty chip: raw markup entered only the copied and downloaded source. The preview, and everything printed here, stays the sanitized string.
input contains an unpaired surrogate; it was encoded as U+FFFD- Half a UTF-16 surrogate pair arrived alone, usually a string sliced mid-emoji upstream; U+FFFD keeps the HTML well-formed.
FAQ
Is the HTML safe to paste into my site?
The output you see is cleaned twice: unsafe link schemes are refused during the parse, and DOMPurify strips scripts and event handlers before the preview renders. An empty notice means nothing was removed. Only the advanced passthrough toggle hands raw markup to the copied source, warning while on.
Which Markdown flavor is this?
CommonMark 0.31.2 is the core grammar, with the GFM extensions layered on — tables, strikethrough, task lists, extended autolinks — each behind its own toggle. Reference-style footnotes, which GitHub renders without the GFM spec formalizing them, stay on.
Why does GitHub render my README slightly differently?
GitHub adds layout wrappers and prefixes its heading ids; this page prints what the grammar produces. Two scope differences: single-tilde ~x~ stays literal here, and bare domains without www. do not autolink.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Re-run the conversion at any input size.
- ⌘/Ctrl + S
- Download the output as .html, wrapped when that option is on.
- ⌘/Ctrl + C
- Copy the whole HTML source when nothing is selected.
- ⌘/Ctrl + Shift + C
- Copy the whole HTML source despite a selection.
- t
- Cycle the theme palette (idle inside text fields).
- ? or ⌘/Ctrl + /
- Open the shortcut overlay.
- Esc
- Close the topmost dialog; input always stays.