How to Convert HTML to Markdown: 5 Methods + Cleanup Tips
Convert HTML to Markdown with a browser tool, Turndown, Pandoc, or Python, then review tables, images, embeds, styles, and other lossy output.
On this page
Convert article-style HTML by extracting the main content, running it through a converter, and reviewing the rendered Markdown. The process is lossy whenever the source uses layout, forms, styles, embedded media, or other HTML that Markdown cannot express.
For a one-off conversion, start with the HTML to Markdown tool. For a migration, script the conversion and keep a manual review step.
What converts cleanly
These simple HTML patterns usually have direct Markdown equivalents, though whitespace and attributes may still change:
| HTML | Markdown |
|---|---|
<h1> through <h6> | # through ###### |
<p>Text</p> | Text + blank line |
<strong> or <b> | **bold** |
<em> or <i> | *italic* |
<del> or <s> | ~~strikethrough~~ (GFM) |
<code> | `code` |
<pre><code class="language-js"> | ```javascript (GFM) |
<a href="..."> | [text](url) |
<img src="..." alt="..."> |  |
<ul><li> | - item |
<ol><li> | 1. item |
<blockquote> | > quote |
<hr> | --- |
<table><thead><tbody> | GFM table |
If your HTML is mostly these elements, automated conversion is a useful first pass.
What doesn't convert
| HTML | What happens in Markdown |
|---|---|
<form>, <input>, <button> | Cannot be represented. Stripped or left as HTML. |
<iframe>, <video>, <audio> | Left as raw HTML (valid in most Markdown processors). |
<div class="..."><span style="..."> | Classes and styles stripped. Content kept. |
<details><summary> | Usually left as raw HTML. |
<kbd>, <abbr>, <sub>, <sup> | Left as raw HTML. |
| Custom layout (grids, flexbox) | Collapsed into flat content. Layout lost. |
Inline style="..." | Stripped. |
<script>, <style> | Stripped (and should be — security). |
You'll end up with Markdown that has occasional HTML sprinkled through it. That's normal and expected.
Tools
1. Browser tool (easiest)
Our HTML to Markdown converter uses Turndown with the GitHub Flavored Markdown plugin.
- Paste HTML → get Markdown instantly
- Runs in your browser (nothing uploaded)
- Copy output with one click
- Free, no signup
Good for: single articles, pasted snippets, quick exploratory conversions.
2. Turndown (Node.js, scriptable)
npm install turndown turndown-plugin-gfm
import TurndownService from "turndown"; import { gfm } from "turndown-plugin-gfm"; const turndown = new TurndownService({ headingStyle: "atx", // # instead of === codeBlockStyle: "fenced", // ```js instead of indented bulletListMarker: "-", // - instead of * }); turndown.use(gfm); const md = turndown.turndown("<h1>Hello</h1><p>World</p>"); console.log(md); // # Hello // // World
Good for: bulk conversion, CI/CD pipelines, integrating into a larger script.
3. Pandoc (battle-tested)
pandoc input.html -o output.md # Or from stdin: pandoc -f html -t gfm < input.html > output.md
Flags worth knowing:
-t gfm— output GitHub Flavored Markdown (vs. plainmarkdown).-t commonmark— strict CommonMark output.--wrap=none— don't hard-wrap long lines (nicer for editing).
Good for: multi-format pipelines and documents that need Pandoc's conversion controls. HTML-to-Markdown-to-HTML is not a lossless round trip.
4. Python: markdownify
pip install markdownify
from markdownify import markdownify as md print(md("<h1>Hello</h1>")) # # Hello
Good for: Python pipelines, Jupyter notebooks, integrating with scraping tools.
5. Manual cleanup for difficult pages
For one complex page, copy the rendered article as plain text and reapply headings, links, images, and code manually. This avoids preserving navigation and layout wrappers that do not belong in the article.
A realistic migration workflow
If you're moving content out of a CMS (say, WordPress → a static site), here's the sequence that works:
Step 1: Export raw HTML
Start with the CMS's documented export or API. Export formats differ, and access controls or site terms may limit automated retrieval, so confirm that you are authorized to process the content.
Step 2: Extract only the article body
HTML exports usually include headers, sidebars, footers, and navigation. You don't want any of that in Markdown. Use a query selector:
// Node.js with cheerio import * as cheerio from "cheerio"; import fs from "fs"; const html = fs.readFileSync("post.html", "utf8"); const $ = cheerio.load(html); const articleHTML = $("article").html(); // or ".post-content", etc.
Step 3: Convert to Markdown
import TurndownService from "turndown"; import { gfm } from "turndown-plugin-gfm"; const turndown = new TurndownService({ headingStyle: "atx" }); turndown.use(gfm); const markdown = turndown.turndown(articleHTML);
Step 4: Add front matter
Static site generators need YAML front matter for metadata:
const frontMatter = `--- title: "${title}" date: "${date}" tags: [${tags.map((t) => `"${t}"`).join(", ")}] --- `; fs.writeFileSync(`posts/${slug}.md`, frontMatter + markdown);
Step 5: Fix images
Image URLs may point to the old CMS (/wp-content/uploads/...). Rewrite them to your new image hosting or download and place them in a static assets folder. A regex pass on the Markdown works:
markdown = markdown.replace( /!\[([^\]]*)\]\(\/wp-content\/uploads\/([^)]+)\)/g, "" );
Step 6: Review by hand
Always review. For a batch, inspect high-value pages plus a representative sample of different layouts. Look for:
- Broken image paths
- Stripped formatting that mattered
- HTML that should have been converted but wasn't
- Encoding issues (smart quotes, em dashes)
- Empty paragraphs from nested divs
Common gotchas
Nested inline tags
<strong><em>bold italic</em></strong>
Some converters produce ***bold italic*** (correct). Some produce **_bold italic_** (ugly but valid). Check your tool's config.
Line breaks
<br> inside a paragraph converts to (two trailing spaces) in Markdown — which is invisible and easy to delete accidentally. If line breaks matter, you may want to keep them as <br> in the Markdown. Turndown has a br: " " option.
Tables with spans
<td colspan="2">Merged</td>
Markdown tables don't support colspan or rowspan. Your converter will either drop the attribute (breaking the merge) or leave the raw HTML. Decide which is worse for your content.
Code block language detection
<pre><code class="language-javascript"> const x = 1; </code></pre>
Modern converters read class="language-*" and emit ```javascript. But some old CMSes use class="lang-js" or class="prettyprint" — you may need custom logic.
Smart quotes and typography
HTML often contains smart quotes (', "), em dashes (—), and non-breaking spaces ( ). These survive conversion — which is good — but some editors and git diffs render them oddly. If you want ASCII-only Markdown, run a sed or regex pass after conversion.
When to skip automatic conversion
Sometimes the HTML is so polluted (deeply nested divs, inline styles everywhere, WYSIWYG editor cruft) that conversion produces garbage. In that case:
- View the rendered page in a browser.
- Copy the visible text (Cmd/Ctrl + A → C).
- Paste as plain text into your Markdown editor.
- Re-apply formatting (headings, links, code) manually.
This is faster than fighting a converter for a single stubborn page, and you end up with cleaner Markdown.
After conversion
Once you have Markdown, run it through the other direction as a sanity check:
- Use our Markdown to HTML tool to render the result.
- Compare side-by-side with the original.
- Fix what's broken, re-run.
For a large migration, automate link and render checks across the collection, then manually inspect representative and high-value pages before going live.
Summary
- Most HTML converts cleanly to Markdown. Forms, layout, inline styles do not.
- Use our browser tool for one-offs, Turndown or Pandoc for scripting.
- Always review converted output; unsupported HTML and content-specific defects need human judgment.
- For CMS migrations, the real work is in the pipeline around the converter (extraction, front matter, image paths), not the converter itself.
Master the workflow once and you'll never feel trapped by a CMS again.
Frequently Asked Questions
Is HTML to Markdown conversion lossless?+
Which tool produces the cleanest output?+
How do I handle tables, code blocks, and images?+
Can I convert a whole website?+
What about inline styles like color or font size?+
Keep reading
Markdown vs HTML: Differences, Use Cases & Examples
Compare Markdown and HTML syntax, portability, rendering, security, accessibility, and use cases, with examples and a practical format decision table.
Markdown Cheat Sheet: Syntax, Examples & GFM Reference
Copy Markdown syntax for headings, lists, links, images, code, tables, and task lists, with clear CommonMark and GitHub Flavored Markdown labels.
How to Blog with Markdown: A Publishing Workflow
Build a Markdown blogging workflow for capturing ideas, drafting, editing, previewing, publishing, distributing, and maintaining posts you control.