Every docs migration hits the same moment: 40 HTML files in, one table with merged cells, and a converter that quietly eats your code fence. Here's the full tag map, the three conversions that always break, and how to get clean GitHub-Flavored Markdown out the other side.
Most modern docs stacks (GitHub, GitLab, Hugo, Jekyll, Docusaurus) treat Markdown as the source of truth. Markdown diffs line by line in pull requests, survives merges better than HTML, and reads fine unrendered. If the content lives in an old CMS, a rich-text editor, or an export from a tool that only speaks HTML, conversion is the bridge.
The usual workflow: export the HTML, convert it, then fix the handful of constructs Markdown can't express. Knowing which constructs those are beforehand is the whole game.
| HTML | Markdown (GFM) |
|---|---|
<h1> to <h6> | # to ###### |
<p> | paragraph text, blank line between |
<strong> / <b> | **bold** |
<em> / <i> | *italic* |
<del> / <s> | ~~struck~~ |
<a href="…"> | [text](url) |
<img src alt> |  |
<ul> / <ol> | - item / 1. item, nested by 2 spaces |
<blockquote> | > prefix on each line |
<pre><code class="language-py"> | ```py fenced block``` |
<code> | `inline code` |
<hr> | --- |
<br> | two trailing spaces + newline |
<table> | pipe table, pipes in cells escaped as \| |
<li><input type=checkbox> | - [x] task list item |
Everything on the left converts with a one-line tool. Try it live on the HTML to Markdown converter as you read; paste any row's HTML and watch it come out the right side.
Headings, links, tables, code fences, nested lists. Runs in your browser, nothing uploaded.
HTML to Markdown Converter →colspan and rowspan have no GFM equivalent. Split them into separate rows before converting, or the table comes out lopsided.<em> inside <strong> inside a link usually survives, but <b><i><b> nesting collapses to a single level because Markdown can't express the third. Re-add by hand where it matters.<dl>/<dt>/<dd> have no standard Markdown form (a few flavors add one). Expect them to flatten into paragraphs.Scripts, styles, iframes, and inline CSS vanish by design. That's usually what you want, but skim the output once for anything that lived inside those tags, like a styled callout box.
More than people expect. There are two breeds: regex-based converters that pattern-match tags, and parser-based ones that read the markup into a DOM tree first and walk it. Sloppy HTML (unclosed <li>, attributes without quotes, HTML entities like ) breaks regex converters silently and passes through parser-based ones cleanly. Browser tools get the parser for free, since the browser ships one. In a pipeline, Turndown (MIT) is the standard parser-based library; it's what most wrappers use under the hood.
Whichever you use, check three things on output: blank lines between blocks (or your paragraphs collapse into one), the language tag on code fences (it usually lives in a class="language-…" attribute), and pipes inside table cells (unescaped pipes shred the table into columns).
Markdown to HTML is the easier direction and every static-site generator does it at build time. When you need it ad hoc, the Markdown to HTML converter is the mirror tool: paste GFM, copy rendered HTML. Keeping both directions in one place covers the round trip, and if your HTML comes from an API or a JSON blob, run it through the JSON formatter first to see what you're actually holding.
No. Markdown covers structure (headings, lists, quotes, code) but has no representation for scripts, styles, forms, iframes, or inline styling, so converters drop them. Semantic containers like div and span pass their inner content through. GFM extensions add tables, strikethrough, and task lists, which is why GFM is the usual target format.
Usually colspan, rowspan, or cells with embedded lists. GFM pipe tables are strictly a header row plus same-width body rows, so any spanning or multi-line cell has no target. Fix it before converting: split merged cells into plain rows, or lift cell lists out into paragraphs under the table.
They convert cleanly as long as the indentation is consistent: each nesting level indents the child items two spaces past the parent marker. The common failure is HTML where the nested ul sits outside the closing li tag, which browsers tolerate but produces a broken list in Markdown. A parser-based converter normalizes this; regex-based ones don't.
For a one-off chunk of HTML, a browser tool like the ToolAspect converter is faster than setting up a project. For recurring conversion in a build pipeline, use Turndown (MIT licensed) or the converter inside your static-site generator, so the rules run identically on every commit.