Why turn web pages into Markdown
Three scenarios carry the real demand for HTML-to-Markdown conversion:
- Note archiving: saving a good article into Obsidian/Notion — their "paste web page" often drops formatting or drags in ad junk; copying the HTML source and converting to Markdown is the cleanest path
- Blog migration: an old site outputs HTML (WordPress themes, hand-written pages); moving to Markdown-driven static blogs like Hugo/Hexo/Astro means converting every post
- Material prep for LLMs: feeding a page to a model for summary/translation/rewrite — a clean Markdown context stripped of nav-bar and footer junk costs the fewest tokens with the least noise
The common thread: structure and body text yes, styling and junk no — which is exactly how the conversion rules are designed.
Rule one: structure and emphasis mapping
The direct translations are uncontroversial: h1-h6 map to #-######, strong to **, em to *, inline code to backticks. The interesting parts are the boundaries:
- Wrapper tags pass through: div/section/span have no Markdown counterpart; only their content survives
- script/iframe/forms dropped: interactive elements have nowhere to live in Markdown, and shouldn't survive for safety reasons either
- Inline styles all lost: Markdown's philosophy separates content from presentation; color and font-size information is destined to disappear
Rule two: faithful list restoration
Lists are the easiest structure to mangle. Three points matter:
- Nesting indentation: sub-lists expand with two spaces per level; one wrong level and the whole tree falls apart
- Ordered start numbers:
<ol start="5">must output5.first — the numbering is content semantics, losing it is an error - Task lists: a checkbox as the list item's first element becomes
- [x]/- [ ](a GFM extension); checkboxes buried in paragraphs drop as form elements
Rule three: tables and code blocks
Tables go through the GFM extension: header row + alignment row + data rows, with alignment mapped from the HTML align attribute (:---/:---:/---:). The honest caveat: modern pages put alignment in CSS classes, so mapped alignment is the minority case — "table structure preserved" is the promise, "visual alignment preserved" is not.
Code blocks have two rules: extract the language from the language-xxx class to annotate the fence (```python), and upgrade the fence — when the content itself contains triple backticks (say, the page is teaching Markdown syntax), the outer fence rises to four backticks or the block gets cut in half.
Rule four: escaping — preserving "renders as it reads"
The most underrated rule, and the one that decides output quality. A stray *, _, [, or a leading # or - in text will accidentally become emphasis, a link, a heading, or a list. Escaping judges by position:
| Case | Handling |
|---|---|
* or [ in text |
prepend a backslash |
_ inside snake_case |
keep (letters/digits on both sides means it isn't emphasis) |
leading # - 1. |
escape, so it doesn't render as heading/list |
| inside table cells |
escape, so it doesn't split the column |
| pre/code content | never escaped (it's meant to be literal) |
Pairing with the Markdown ecosystem
The HTML to Markdown converter implements all the rules above: paste source for live conversion, with four toggles (bullet, emphasis style, keep images, GFM tables) matching your target editor's conventions. To preview how the result renders, check it in the opposite direction with the Markdown reader; for README-style document workflows see this guide. For batch programmatic scenarios, reach for libraries like markdownify or turndown — for one-off tasks, a paste-and-get online tool has the lowest cost.
Frequently Asked Questions
What gets lost when converting HTML to Markdown?
Four categories: inline styles and class/id (Markdown has no syntax for them); merged cells and nested structures inside table cells (GFM tables don't support them); interactive elements like script/iframe/form controls (unsupported both for safety and syntax); and CSS-driven visual information (alignment usually lives in classes — only the HTML align attribute maps). Structure and text content survive, which is exactly what material-archiving needs.
Why do underscores and asterisks need escaping?
Markdown treats * and _ as emphasis, [ ] as links, and leading # or - as headings and lists — plain text that happens to contain these characters would render differently than intended (snake_case going italic, a leading number-dot becoming a list). A converter escapes by position: underscores inside words stay, leading syntax characters get a backslash, and pipes in table cells escape — preserving the first principle that the rendered result matches the original.
What happens with triple backticks inside a code block?
Fence upgrading: when the content itself contains ```, the outer fence automatically rises to four or more backticks until it no longer conflicts — the standard CommonMark approach. Inline code works the same way, wrapped in double backticks when it contains one. This rule is a hard requirement when converting pages that teach Markdown syntax.
Which programming libraries do the same conversion?
In Python: markdownify and html2text (the latter usable from the command line), or a hand-written BeautifulSoup4 traversal. In JavaScript/Node: turndown (browser and Node) and the unified ecosystem's rehype-remark pipeline. Static site generators (Hugo/Hexo) run similar libraries inside their importers. Online tools win on zero setup: paste and get, ideal for one-off or low-frequency tasks.