Calcoid
Tech

HTML to Markdown Converter

Convert HTML to clean Markdown in your browser. Supports headings, bold, italic, code, links, images, lists, blockquotes, and rules. Decodes entities, escapes Markdown characters, and strips script and style blocks.

HTML input
Markdown output
Markdown will appear here as you type.
Options

Ordered lists are always renumbered from 1.

Keep newlines inside paragraphs instead of collapsing them to single spaces. Useful for ASCII art or pre-formatted text outside <pre>.

Stats
Input characters0
Output characters0
Elements converted0
Headings0
Paragraphs0
Links0
Images0
Code blocks0
List items0

Frequently Asked Questions about the HTML to Markdown Converter

Will the output round-trip cleanly through the Markdown to HTML converter?
For the supported subset, yes. Take HTML, convert it here, paste the Markdown into the Markdown to HTML converter on Calcoid, and you get equivalent HTML back. The element counts will match and the rendered text will read the same. The round trip is not byte-identical because HTML allows several attributes (id, class, style, data-* and so on) that have no Markdown equivalent, so they are dropped on the way out. If you need a guaranteed byte-perfect round trip, store the source as Markdown and render to HTML on demand instead of the other direction.
Which HTML tags does this converter understand?
Headings h1 through h6, paragraphs (p), bold (strong, b), italic (em, i), inline code (code), code blocks (pre, pre + code with a language- class), links (a with href), images (img with src and alt), unordered lists (ul + li), ordered lists (ol + li), blockquotes, horizontal rules (hr), and hard line breaks (br). Wrapper tags (span, div, sub, sup, small, mark, u, s, del, ins, kbd, abbr, cite, q, big, tt, var, samp) are stripped but their text content is kept. Script and style blocks are dropped entirely so a copy-paste from a real page never produces executable Markdown. Anything else is treated as an unknown tag: the wrapper is dropped, the text inside survives. Tables, definition lists, forms, iframes, and SVG are not converted; they show up as plain text or get stripped, depending on the tag.
Why does regex-based parsing have edge cases?
HTML is not a regular language. A real parser builds a tree from the source and walks it; a regex pipeline pattern-matches on the source string and hopes the patterns line up with the structure. For clean, well-formed HTML the two approaches agree, which is why this converter handles 95 percent of real paste cases. The edge cases that break: nested lists (a ul inside a li), tags that span across other block-level constructs (a strong that crosses a paragraph boundary), unclosed tags inside other tags, CDATA sections, and any HTML that contains a literal '</p>' inside an attribute value. If your source is malformed or you need rock-solid nested-list support, paste it through a real parser like turndown (npm install turndown) or run it through a proper DOM in Node (jsdom + a custom walker). This tool stays regex-based because the trade-off keeps the bundle at zero and the behaviour predictable.
How are HTML entities decoded?
Named entities &amp;, &lt;, &gt;, &quot;, &apos;, and &nbsp; are decoded back to their literal characters (&, <, >, double quote, single quote, space). Numeric entities are decoded too: &#65; becomes A and &#x1F4A9; becomes the pile-of-poo emoji. The order matters: &amp; is decoded last so &amp;lt; survives as the literal text '&lt;' instead of being double-decoded into '<'. Other named entities (&copy;, &reg;, &trade;, every Greek letter, every accented vowel) are left as-is; the use cases that hit this converter rarely need them and the lookup table would balloon the file. If you need the full entity table, run the source through a real parser instead.
Why does the Markdown output have backslashes in front of regular characters?
Characters that have meaning in Markdown when they appear in regular prose get a leading backslash so the output round-trips without surprises. The escaped set is backslash, backticks, asterisks, underscores, hashes, square brackets, and parentheses. So 'cost is $5 *plus* tax' becomes 'cost is $5 \*plus\* tax' in the output, which still renders as the literal text 'cost is $5 *plus* tax' when fed to a Markdown parser. Without the escapes, a Markdown parser would treat the asterisks as italic markers and emit '<em>plus</em>' instead. Characters that only have meaning at the start of a line (a leading dash for a list, a leading > for a quote) are not escaped: they only matter if the line starts with them, and we keep prose readable instead of overly defensive.