Answer: The HTML to Markdown Converter produces your output instantly from the input you provide โ everything runs in your browser, free, with no signup required.
Paste HTML on the left, clean GitHub-Flavored Markdown appears on the right
<h1> becomes #, <strong> becomes **bold**, links become [text](url), tables become pipe tables, and <pre> blocks become fenced code. Runs fully in your browser, nothing uploaded. Going the other way? The Markdown to HTML converter reverses it.
| HTML | Markdown | Notes |
|---|---|---|
<h1> โฆ <h6> | # โฆ ###### | One # per level |
<p> | plain text + blank line | Blank line separates paragraphs |
<strong>, <b> | **bold** | Double asterisks |
<em>, <i> | *italic* | Single asterisks |
<del>, <s> | ~~strike~~ | GFM extension |
<a href> | [text](url) | Title kept if present; javascript: links become plain text |
<img> |  | Alt kept, title dropped. Only inside a paragraph, link, or other inline text |
<ul> / <ol> | - item / 1. item | Nesting indents 2 spaces; numbering always starts at 1 |
<li><label><input checkbox> | - [x] done | GFM task list. The checkbox must sit inside a label or p; a bare checkbox in the li is dropped |
<blockquote> | > quoted text | Every line prefixed |
<pre><code> | ``` fenced ``` | Keeps a language-* class on the code or pre |
<code> | `code` | Backticks, inline |
<hr> | --- | Horizontal rule |
<table> | pipe table | Pipes in cells are escaped |
<br> | two trailing spaces | Hard line break inside paragraphs. In list items it's dropped |
Tags with no Markdown equivalent (div, span, font) pass their text through. Scripts, styles, and comments are dropped.
The tool parses your markup with the browser's native DOMParser, walks the resulting tree, and rewrites each element as its Markdown equivalent. Because the real HTML parser does the reading, sloppy input (unclosed tags, missing quotes, stray entities) gets normalized before conversion instead of breaking it.
All six heading levels, bold, italic, strikethrough, links with titles, images inside text, nested ordered and unordered lists, GFM task lists (when the checkbox is wrapped in a label), blockquotes (including nested ones), fenced code blocks with language detection from class="language-โฆ", horizontal rules, and pipe tables. The first row of a table always becomes the header row, whether it uses th or td. Pipes inside table cells get escaped so the table doesn't break.
Feed it this snippet:
<h2>Feeding Guide</h2> <p>Adult dogs eat <strong>2-3% of body weight</strong> per day. <a href="/raw-dog-food-calculator/">See the calculator</a> for portions.</p> <ul><li>Muscle meat: 80%</li><li>Edible bone: 10%</li></ul>
You get this Markdown back:
## Feeding Guide Adult dogs eat **2-3% of body weight** per day. [See the calculator](/raw-dog-food-calculator/) for portions. - Muscle meat: 80% - Edible bone: 10%
The blank line between blocks is what makes it valid Markdown rather than one long paragraph, and the link survives with its path intact.
Classic cases: moving a legacy HTML page into a Markdown-based static site or docs toolchain (Jekyll, Hugo, Docusaurus), cleaning up rich-text-editor export soup before committing it, and lifting formatted content out of CMSes whose only export is HTML. For recurring conversion inside a build pipeline, a library like Turndown (MIT) is the right tool; for one-off pastes, this page does it without installing anything.
Every row below was run through this page's converter. Most real-world HTML converts cleanly, but these are the spots where the output surprises people. Knowing them saves you a round of hand-editing.
| HTML you paste | Markdown you get | What to do about it |
|---|---|---|
<p>One<br>Two</p> | One, two spaces, line break, Two | Nothing. That's a correct GFM hard break |
<li>One<br>Two</li> | - OneTwo | The break is lost and the words join. Put a space or a separate item there |
<h2>One<br>Two</h2> | ## One with Two on the next line | Markdown headings are one line, so "Two" reads as body text. Remove the break |
<li><input type="checkbox" checked> done</li> | - done | Bare checkboxes are dropped. Wrap them: <label><input ...> done</label> gives - [x] done |
<ol start="5"> | 1., 2. ... | The start number is ignored. Edit the first number by hand |
<div><img src="a.png" alt="A"></div> | (nothing) | Images only convert inside text. Wrap the image in <p> |
<figure><img ...><figcaption>Cap</figcaption></figure> | *Cap* | The caption survives as italics, the image doesn't. Same fix: wrap the image in <p> |
<div>Hello <b>world</b></div> | Hello and world as two paragraphs, no bold | Loose text directly inside a div or section gets split and loses bold, italics and links. Wrap it in <p> |
<u>, <mark>, <sup>, <sub>, <kbd> | plain text | No Markdown syntax for these, and the tool never writes raw HTML. x<sub>2</sub> becomes x2 |
<code>a`b</code> | `a`b` | Broken inline code. Change it to `` a`b `` by hand |
<pre> containing ``` | a fence that closes early | Swap the outer fence for ```` (four backticks) or ~~~ |
<th colspan="2">H</th> | | H | | | Spans and align are ignored; short rows get padded with empty cells |
Table with no <th> | first row used as header | GFM tables need a header, so add a header row to the source if row one is data |
| Table inside a table cell | inner rows repeated below the outer table | Flatten nested tables first. Markdown can't nest them anyway |
<p>Use *stars* and # signs</p> | Use *stars* and # signs | No escaping, so *stars* renders as italics. Add backslashes where it matters |
&, <tag>, © | &, <tag>, © | Entities are decoded. A decoded <tag> may be read as HTML by your Markdown renderer |
<details><summary>, <dl> | plain paragraphs | Summary, terms and definitions each become their own paragraph |
Tested with the converter script that runs on this page. If you spot output that doesn't match this table, the table is wrong and we'd like to know.
A minute of cleanup on the input usually beats ten minutes fixing the output. Here's what helps most.
Navigation, footers, cookie banners and sidebars all convert too, and you'll just delete them later. In your browser's dev tools, right-click the article element and choose Copy, then Copy outerHTML. That gives you the body text and nothing around it.
Page builders and rich-text editors often leave text and images directly inside <div> wrappers. As the table above shows, that's where formatting and images go missing. A quick find-and-replace that wraps those runs in <p> fixes most of it.
Fenced blocks copy the code's text exactly, which is what you want. Just look for backticks inside inline code and triple backticks inside blocks, since those are the two cases that break. If your highlighter uses lang-python or highlight-python instead of language-python, the fence won't get a language tag, so add it yourself.
The fastest check is to paste the Markdown into the Markdown to HTML converter and eyeball the preview. For long documents, run the before and after text through the Diff Checker to catch dropped sentences. The HTML to Markdown cheat sheet covers the syntax side if you're fixing things by hand.
Paste your HTML into the left box on this page and the Markdown appears on the right instantly. The converter reads your markup with the browser's own HTML parser and rewrites each element as GitHub-Flavored Markdown: h1 becomes #, strong becomes **, a tags become [text](url), tables become pipe tables, and pre blocks become fenced code blocks. Nothing is uploaded; the conversion happens in your browser.
Headings h1-h6, paragraphs, bold and italic, strikethrough, inline code, links with titles, images inside text, ordered and unordered lists (nested too), task list checkboxes when they're wrapped in a label or paragraph, blockquotes, horizontal rules, and GFM pipe tables. Code blocks keep their language when the source has a class like language-python. Tags with no Markdown equivalent, like span or div, pass their content through.
Markdown has no representation for scripts, styles, forms, iframes, or inline styling, so those are dropped. Table cell spans (colspan, rowspan) and alignment attributes have no GFM equivalent either, so they're ignored. The converter never falls back to raw HTML, which means tags like u, mark, sup and sub keep their text but lose their formatting.
It does the same job as Turndown, the MIT-licensed library many pipelines use, but it's a single self-contained script with no install. Turndown is the right call inside a build pipeline; this page is the right call when you just need to paste a chunk of HTML and copy Markdown out once, or when you can't add dependencies.
No. The converter uses the DOMParser built into your browser, and its code never sends your input anywhere. That makes it safe for drafts, private docs, and client content. The page itself still loads the same analytics and ad scripts as the rest of the site, but the conversion runs locally.
Use the sister tool, the Markdown to HTML converter, which renders Markdown into styled HTML in the same paste-and-copy flow. Keeping both directions in one toolbox covers the two most common format-shuffling jobs in documentation work.
Images only convert when they sit inside text, like a paragraph or a link. An img tag directly inside a div, a figure, or at the top level of what you paste produces no output. Wrap it in a p tag and paste again, and you'll get  as expected.
No. Asterisks, underscores, pound signs and backticks in your text are copied as they are. Usually that's fine, but a phrase like *note* will render as italics. If the text has to show those characters literally, put a backslash in front of each one after converting.
The converter only picks up a checkbox when it's wrapped in another element, such as a label or a p inside the list item. A bare input sitting directly in the li is skipped, so you get - done instead of - [x] done. Wrapping the input and its text in a label fixes it.