How to convert HTML to Markdown in three steps
- Text, headings, lists, links, images and quotes all survive the trip.
- Classes, inline styles, IDs, forms and iframes do not. Markdown has no words for them.
- Nested and merged tables stay as raw HTML, which is the correct outcome.
- turndown.js runs in your tab, so the markup never leaves your machine.
The output panel already has your Markdown in it. The job now is checking what came through, because this direction always drops something. The rest of this page shows you exactly what.
Three steps back to plain text
- Paste your HTML into the input panel. It can be a page saved from a browser, a fragment copied out of a CMS editor, an exported help-desk article, an email template, or a single
<table>you want as pipe syntax. Pulling from a live page? Copy the element you want, not the whole document shell. - Read the Markdown in the output panel. Headings collapse to
#marks, bold becomes**, links become[text](url), and the wrapper divs vanish. Scan it once. This is the moment you find out what got flattened. - Copy or download the result. Copy puts the Markdown on your clipboard for a README, a wiki, a static site post or an Obsidian note. Download saves a
.mdfile you can commit straight into a repository.
What turndown.js is doing
The engine is turndown.js, the library behind the "paste as Markdown" feature in editors you already use. Your browser builds a DOM from the markup you pasted. Turndown then walks that tree and rewrites each node as its Markdown equivalent.
Nothing is sent to a server at any stage. Once the output looks right, the free Markdown editor on our homepage is the natural next stop for tidying it up with a live preview beside you.
A worked example: HTML in, Markdown out
Here is a realistic fragment, complete with the wrapper div and inline style a real CMS would hand you:
<div class="post">
<h2>Release Notes</h2>
<p>We shipped <strong>dark mode</strong> and fixed the <em>export</em> bug.</p>
<ul>
<li>Faster preview</li>
<li>Smaller <code>bundle.js</code></li>
</ul>
<p style="color:#888">See the <a href="https://example.com/changelog">changelog</a>.</p>
</div>
The Markdown that comes back
Eleven lines of markup become six lines of text, and the whole thing is suddenly readable without a browser:
## Release Notes
We shipped **dark mode** and fixed the *export* bug.
- Faster preview
- Smaller `bundle.js`
See the [changelog](https://example.com/changelog).
What quietly disappeared
The content is intact, but two things are missing. Look for them:
<div class="post">is gone. Turndown unwraps containers and keeps the text inside them.style="color:#888"is gone. Markdown has no vocabulary for colour, so the grey note is now ordinary text.
That is not a bug. It is the deal you accept when you move down to a simpler format. The output follows GitHub Flavored Markdown conventions throughout: ATX headings written with #, - as the bullet marker, and fenced code blocks rather than four-space indentation.
Those defaults mean the result pastes cleanly into a README, a GitLab wiki or any static site generator. If the difference between the two formats is new to you, Markdown vs HTML sets out what each one is for, and what Markdown is covers the format from scratch.
Every common HTML tag and its Markdown equivalent
This is the mapping the converter applies. The notes column matters most, because it tells you what quietly changes on the way through:
| HTML tag | Markdown equivalent | Notes |
|---|---|---|
<h1> to <h6> | # to ###### | Heading levels are preserved exactly |
<p> | Text with a blank line after it | Any class or style is dropped |
<strong>, <b> | **text** | Both tags collapse to the same syntax |
<em>, <i> | *text* | Semantic distinction between the pairs is lost |
<a href> | [text](url) | A title survives; target and rel do not |
<img> |  | width, height and loading are discarded |
<ul> and <li> | - item | Nesting is kept through indentation |
<ol> and <li> | 1. item | A start attribute is lost; numbering restarts at 1 |
<code> | Backtick-wrapped text | Backticks inside get an extra fence automatically |
<pre><code class="language-js"> | Fenced block tagged js | Only when the language is in a recognisable class |
<blockquote> | > text | Nested quotes become >> |
<hr> | --- | Straightforward, no options lost |
<br> | Two trailing spaces plus a newline | Invisible in the source, so easy to delete by accident |
<del>, <s> | ~~text~~ | GFM only; plain Markdown has no strikethrough |
<table> | Pipe table | Simple grids only, and the first row becomes the header |
<div>, <span>, <section> | Nothing; contents kept | The wrapper is unwrapped and its attributes vanish |
<script>, <style>, <noscript> | Removed entirely | Content and tag are both discarded |
<iframe>, <form>, <input>, <video> | Kept as raw HTML or dropped | Markdown has no equivalent syntax at all |
Two rows to read twice
- The
<br>row. A hard line break becomes trailing whitespace, and most editors strip trailing whitespace on save. It is the most fragile thing in a converted file. - The wrapper row. This explains almost every "where did my layout go" reaction. Layout lives in containers and classes, and Markdown has neither.
The cheat sheet shows the target syntax at a glance, and the complete Markdown guide explains each element properly. Converting anything grid-shaped? Read the tables guide first, because pipe syntax is stricter than it looks.
What does not convert cleanly, and why
HTML is a much larger language than Markdown, so this direction is lossy by definition. Knowing where the loss happens lets you plan around it.
Things Markdown has no words for
- Nested and merged tables. A table using
colspan,rowspanor a nested list inside a cell has no pipe equivalent. Turndown either flattens it into a lopsided grid or leaves the<table>in place as raw HTML. - Inline styles, classes and IDs.
style="color:red",class="callout"andid="section-3"all disappear, because Markdown expresses structure rather than appearance. - Forms, iframes and media embeds.
<form>,<input>,<iframe>,<video>and<audio>have no syntax at all, so they are kept as raw HTML or dropped.
Leaving a complex table as HTML is the fix, not a failure: Markdown renderers pass block-level HTML through untouched, so the table still displays correctly. If an anchor like id="section-3" matters, add a small <span id="section-3"></span> to the Markdown by hand. And note that GitHub strips <iframe> from rendered READMEs, so a video embed that survives conversion can still vanish on publication.
Things that change quietly
- Position-dependent layout. Multi-column grids, floats and absolutely positioned blocks collapse into one linear stream in source order, which is often not reading order.
- Invisible characters. Non-breaking spaces and zero-width characters survive without showing themselves, then cause mysterious diffs weeks later.
- Entities and escapes.
is decoded to a real character, and a literal*,_or#in your text gets a backslash so it does not become formatting.
Careful: Never publish a converted layout page without reading it top to bottom first. Source order and reading order are the same thing in Markdown, and columns are exactly where they part company.
The habit that saves you
Convert, then read the whole file once in a preview before you commit it. Paste the output into the editor, compare it against the original page, and patch the few places that moved.
The return trip is the Markdown to HTML converter, and that direction loses nothing. The FAQ on converting Markdown to HTML explains why the two directions are not symmetrical.
This tool, Pandoc, or turndown in Node?
Three tools do this job, and they aim at genuinely different situations. Picking the wrong one wastes an afternoon.
This page: one document at a time
Zero installation, instant results, works on a locked-down work laptop, and your markup never leaves the machine. That covers most real tasks: rescuing an article from a CMS, turning a copied table into pipe syntax, tidying an email before it becomes a wiki page.
Pandoc: archives and unusual formats
Pandoc is a command-line document converter with the widest format support anywhere. One line does the job:
pandoc -f html -t gfm page.html -o page.md
It handles definition lists, footnotes and complex tables more gracefully than a browser library. It also reads DOCX, LaTeX and EPUB, so it is the right tool when your input is not really HTML. The costs are a local install and an opinionated normaliser that reformats output in ways you may not want. A shell loop over a folder turns it into a batch tool.
Turndown in Node: repeatable migrations
This is the answer for hundreds of pages out of a legacy CMS, or a docs site that has to be re-exported every release:
npm install turndown turndown-plugin-gfm
It is the same engine as this page, so the output matches what you previewed here. The real reason to reach for Node is custom rules: turn every <div class="callout"> into a blockquote, or strip tracking parameters from every link. Run it in CI and a thousand pages convert identically every time.
Tip: Use two of them. Prove the transformation here on three representative pages, then encode the rules you found in a Node script for the other nine hundred and ninety-seven. If you only ever needed one page, stop at step one.
Our FAQ answers the questions this page does not, and the Markdown viewer is a quick way to check that a converted file renders the way you expect.
Support data verified
Frequently Asked Questions
Is HTML to Markdown conversion lossy?
Yes, unavoidably. Markdown has no syntax for classes, inline styles, IDs, forms, iframes or merged table cells, so those are dropped or preserved as raw HTML. Text, headings, lists, links, images, quotes and simple tables all survive intact.
Does my HTML leave my browser?
No. Turndown.js runs as ordinary JavaScript in your tab and converts the markup locally. Nothing is uploaded or logged, so it is safe for internal pages and client work, and it keeps working offline once the page has loaded.
Why did my table come out as raw HTML?
Because it uses something pipe tables cannot express, usually colspan, rowspan or a nested list inside a cell. Leaving it as HTML is the correct outcome: Markdown renderers pass block HTML straight through, so it still displays. See the tables guide for what pipe syntax can and cannot do.
Can I convert a whole site or many files at once?
Not in this browser tool, which handles one document at a time. For batches, run turndown from a Node script or use Pandoc from the command line - Both are covered in the comparison section above.
How do I turn the Markdown back into HTML?
Use the sibling tool, the Markdown to HTML converter, or the Copy HTML and .html buttons in the editor. That direction is not lossy, since Markdown is a subset of what HTML can express.
Keep learning
Markdown to HTML Converter
The return trip: paste Markdown and get clean semantic HTML to copy or download.
Markdown vs HTML
What each format is genuinely for, and why converting between them loses detail.
Complete Markdown Guide
Every element of the syntax you are converting into, explained with examples.
Try the Editor
Open Editor