Convert HTML to Clean Markdown (GFM)
.html file or a pasted fragment into clean, GitHub-flavoured Markdown. DOMPurify sanitises the markup first, then Turndown maps semantic HTML5 — article, table, pre, code, blockquote — to CommonMark and GFM, entirely inside your browser.Processing happens 100% in your browser — nothing is uploaded.
Presentation is discarded, and spans cannot be expressed
How to convert HTML to Markdown
- Input: drop an .html or .htm file onto the dropzone, or paste raw markup — a full document or a single fragment both work.
- Process: DOMPurify sanitises the markup, then Turndown maps it to GitHub-flavoured Markdown locally in your browser.
- Copy: send the Markdown to your clipboard or save it as a .md file.
Why Convert HTML to Markdown with MD Convert?
Sanitised before it is converted, never after
DOMPurify runs first, so scripts, event handlers, and javascript-scheme URLs are gone before Turndown or the preview ever sees the markup. Style and form elements plus every style attribute are explicitly forbidden — you can convert customer-supplied or scraped HTML without executing any of it.
GFM tables with pipe escaping and header synthesis
The first row is promoted to a header and a delimiter row is generated from its cell count, so tables that omit a thead still produce valid GFM. Literal pipes inside cells are escaped and cell newlines collapse, so one value can never split its row and shift your data.
Semantic structure in, portable Markdown out
ATX headings, fenced code blocks with the language lifted from the code element's class, hyphen bullets, nesting blockquotes, inlined links, and hard breaks as two trailing spaces. The output needs no reformatting to travel between GitHub, GitLab, Obsidian, Hugo, and Jekyll.
100% local — your markup never leaves the device
Both libraries are ordinary browser JavaScript. Nothing is uploaded, nothing is retained, and conversion keeps working with the network disconnected — which is what makes it usable for internal templates and proprietary markup.
HTML to Markdown: Before and After
Semantic HTML5 in, GitHub-Flavored Markdown out. Note the presentational span and the inline style attribute: both are discarded, because Markdown has nowhere to put them.
<article>
<h2>Release notes</h2>
<p style="color:#c00">Shipped <strong>v2.1</strong> with
<em>streaming</em> parsers.</p>
<blockquote><p>Peak memory is now flat.</p></blockquote>
<table>
<tr><th>Engine</th><th>Status</th></tr>
<tr><td>pdf</td><td>stable</td></tr>
<tr><td>xml</td><td>beta | preview</td></tr>
</table>
<pre><code class="language-ts">const md = convert(file);</code></pre>
<span class="badge">decorative</span>
</article>## Release notes
Shipped **v2.1** with _streaming_ parsers.
> Peak memory is now flat.
| Engine | Status |
| --- | --- |
| pdf | stable |
| xml | beta \| preview |
```ts
const md = convert(file);
```
decorativeUnderstanding the HTML Format: WHATWG HTML Living Standard
- Format
- HyperText Markup Language
- Specification
- WHATWG HTML Living Standard
- Media type
text/html- Parser used
- Turndown + DOMPurify
Technical Specifications & HTML5 Mapping Rules
- Inert Sanitisation Order: DOMPurify executes before Markdown conversion to strip scripts, event handlers, style blocks, and dangerous URL schemes; unsanitised markup never touches the preview DOM.
- GFM Table Synthesis & Spans: The first row promotes to a header with a synthesised delimiter row; in-cell pipes escape to \|; row/colspans flatten to ordinary cells (GFM tables cannot express merged cells).
- Presentational & CSS Stripping: Inline styles, stylesheets, and presentational tags (<font>, <span>) are discarded; structural tags map directly to semantic Markdown elements.
- Code Fencing & Language Classes: Preformatted <code> blocks preserve whitespace byte-for-byte; language classes (e.g., language-typescript) are lifted directly into fenced code-block info strings.
- Whitespace Collapsing Rules: Runs of whitespace within block elements collapse to single spaces per HTML standards; line breaks require explicit <br> or separate paragraph blocks.
Developer Alternative: CLI (Pandoc), Node.js & Python Extraction
For batch jobs or server-side pipelines, Pandoc, Turndown, and markdownify produce the same kind of Markdown outside the browser.
# Convert HTML file directly to GitHub-Flavored Markdown using Pandoc
pandoc -f html -t gfm input.html -o output.md// Programmatic HTML to Markdown conversion using Node.js & Turndown
import TurndownService from "turndown";
const turndownService = new TurndownService({ headingStyle: "atx", codeBlockStyle: "fenced" });
const markdown = turndownService.turndown("<article><h1>Hello</h1><p>Text</p></article>");
console.log(markdown);# Programmatic HTML to Markdown conversion using Python markdownify
# requires: pip install markdownify
from markdownify import markdownify as md
markdown_text = md("<article><h1>Hello</h1><p>Text</p></article>", heading_style="ATX")
print(markdown_text)HTML to Markdown: Comparing the Practical Approaches
| Method | Privacy | GFM tables | Sanitisation | Setup | Batch / automation | Cost |
|---|---|---|---|---|---|---|
| This tool (Turndown + DOMPurify) | Highest — markup never leaves the tab | Yes, with pipe escaping and header synthesis | Built in, and runs before conversion | None — paste or drop a file | One document at a time | Free |
| Pandoc CLI | High — runs locally | Yes, via the gfm writer | None; it converts what you give it | Local install | Strong — scriptable | Free |
| Turndown in your own Node script | High — your infrastructure | Only if you add the plugin or rules | Only if you add a sanitiser | Code plus dependencies | Strong | Free |
| Hosted conversion APIs | Low — markup is uploaded to a third party | Varies by provider | Opaque | API key | Strong | Per-request |
| Asking a chat model to convert | Low — the markup becomes a prompt | Usually, but silently reformats content | Not a sanitiser | None | Poor at scale | Per-token |
How You Can Verify the Privacy Claim
Zero server upload
Conversion runs inside your browser tab. Open DevTools, switch to the Network panel, and convert a file: for every format except URL to Markdown you will see no request carrying your document — because there is no endpoint to send it to.
Parses on the main thread
Parsing stays inside your browser tab — this format’s parser needs DOM APIs. Nothing is uploaded. Everything is plain JavaScript — no native plugin, no WebAssembly toolchain, nothing to install.
Nothing to sign up for
No login, no quota, no paywall, and no tracking tied to your files. Analytics are cookieless and aggregate only. Read the privacy policy for the full data-flow breakdown, including the one proxied exception.
The parsers doing the work
No proprietary black box: each format is handled by a widely audited open-source library, running client-side at the version pinned in our lockfile.
- Turndown (HTML to GFM Markdown)HTML to Markdown conversion
- DOMPurify (Client-side HTML sanitisation)HTML sanitisation
HTML to Markdown — FAQ
Are HTML tables converted to GFM pipe tables?
Yes. Custom rules emit pipe tables, promote the first row to a header even when the markup has no thead, escape literal pipes inside cells, and pass table sections through so the header and body stay contiguous. Row and column spans cannot be represented and become ordinary cells.
What happens to inline styles and style attributes?
They are removed during sanitisation, along with stylesheet, form, input, and button elements. Markdown has no styling layer, so only semantic structure survives — this is intentional, not a gap.
Is it safe to convert HTML from an untrusted source?
Yes. Sanitisation runs before conversion, not after, so scripts, event handlers, and javascript-scheme links are removed before anything is rendered or converted. Nothing in the input is executed at any stage.
Do code blocks keep their language for syntax highlighting?
Yes. A code element inside a pre becomes a fenced block, and the language is lifted from the conventional language- class name, so markup from a syntax highlighter keeps its tag. A pre with no code element inside is fenced without a language hint.