Paste any HTML fragment and get GFM Markdown instantly: <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.

Convert

0 chars
0 chars
Ad

HTML Tag to Markdown Cheat Sheet

HTMLMarkdownNotes
<h1> โ€ฆ <h6># โ€ฆ ######One # per level
<p>plain text + blank lineBlank 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](src)Alt kept, title dropped. Only inside a paragraph, link, or other inline text
<ul> / <ol>- item / 1. itemNesting indents 2 spaces; numbering always starts at 1
<li><label><input checkbox>- [x] doneGFM task list. The checkbox must sit inside a label or p; a bare checkbox in the li is dropped
<blockquote>> quoted textEvery line prefixed
<pre><code>``` fenced ```Keeps a language-* class on the code or pre
<code>`code`Backticks, inline
<hr>---Horizontal rule
<table>pipe tablePipes in cells are escaped
<br>two trailing spacesHard 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.

How the HTML to Markdown Converter Works

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.

What it handles

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.

A worked example

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.

When you'd use it

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.

Edge Cases: What This Converter Actually Outputs

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 pasteMarkdown you getWhat to do about it
<p>One<br>Two</p>One, two spaces, line break, TwoNothing. That's a correct GFM hard break
<li>One<br>Two</li>- OneTwoThe 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 lineMarkdown headings are one line, so "Two" reads as body text. Remove the break
<li><input type="checkbox" checked> done</li>- doneBare 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 boldLoose 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 textNo 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 earlySwap 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 headerGFM tables need a header, so add a header row to the source if row one is data
Table inside a table cellinner rows repeated below the outer tableFlatten nested tables first. Markdown can't nest them anyway
<p>Use *stars* and # signs</p>Use *stars* and # signsNo escaping, so *stars* renders as italics. Add backslashes where it matters
&amp;, &lt;tag&gt;, &copy;&, <tag>, ©Entities are decoded. A decoded <tag> may be read as HTML by your Markdown renderer
<details><summary>, <dl>plain paragraphsSummary, 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.

Prepping HTML for a Clean Conversion

A minute of cleanup on the input usually beats ten minutes fixing the output. Here's what helps most.

Copy the content, not the whole page

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.

Make sure loose text sits in paragraphs

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.

Check code samples before you trust them

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.

Round-trip it

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.

Frequently Asked Questions

How do I convert HTML to Markdown?

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.

Which HTML tags does this convert to Markdown?

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.

What can't be converted to Markdown?

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.

Is this a Turndown alternative?

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.

Does my HTML get uploaded anywhere?

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.

How do I convert Markdown back to HTML?

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.

Why did my image disappear from the Markdown?

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 ![alt](src) as expected.

Does it escape Markdown characters in my text?

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.

Why are my task list checkboxes missing?

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.