← All Posts

How to Convert HTML to Markdown (Free Online Converter)

Published on February 10, 2026

Markdown has become the de facto standard for writing content on the web. From GitHub READMEs to documentation sites, blog posts to knowledge bases, Markdown offers a clean, readable syntax that is easy to write and easy to convert into HTML. But what about the reverse? When you have existing HTML content and need to convert it to Markdown, the process can be tedious if done by hand.

This guide covers everything you need to know about converting HTML to Markdown: what Markdown is, how each HTML element maps to Markdown syntax, common use cases, and how to handle tricky conversions like tables and nested lists.

What Is Markdown?

Markdown is a lightweight markup language created by John Gruber in 2004. Its primary design goal was to be as readable as possible in its raw form. Unlike HTML, where content is buried in angle brackets and tag names, Markdown uses simple punctuation characters to indicate formatting. A heading is a line starting with #, bold text is wrapped in **, and links use a bracket-parenthesis syntax.

The beauty of Markdown is that even someone who has never seen the syntax before can read a Markdown file and understand the structure and content. This readability is why platforms like GitHub, GitLab, Stack Overflow, Reddit, Notion, and countless documentation tools have adopted it.

HTML to Markdown Element Mapping

The core of any HTML-to-Markdown conversion is understanding how each HTML element translates. Here is a comprehensive mapping of the most common elements.

Headings

HTML headings map directly to Markdown headings using the hash symbol. The number of hash symbols corresponds to the heading level.

<!-- HTML -->
<h1>Main Title</h1>
<h2>Section Title</h2>
<h3>Subsection</h3>
<h4>Sub-subsection</h4>
<h5>Minor heading</h5>
<h6>Smallest heading</h6>

<!-- Markdown equivalent -->
# Main Title
## Section Title
### Subsection
#### Sub-subsection
##### Minor heading
###### Smallest heading

Paragraphs and Line Breaks

HTML paragraphs become plain text separated by blank lines. The <br> tag converts to either two trailing spaces or a backslash at the end of a line.

<!-- HTML -->
<p>First paragraph with some text.</p>
<p>Second paragraph below it.</p>
<p>Line one<br>Line two (same paragraph)</p>

<!-- Markdown equivalent -->
First paragraph with some text.

Second paragraph below it.

Line one\
Line two (same paragraph)

Inline Formatting

<!-- HTML -->
<strong>bold text</strong> or <b>bold text</b>
<em>italic text</em> or <i>italic text</i>
<del>strikethrough</del> or <s>strikethrough</s>
<code>inline code</code>

<!-- Markdown equivalent -->
**bold text**
*italic text*
~~strikethrough~~
`inline code`

Links and Images

Links and images have a similar syntax in Markdown. Images are prefixed with an exclamation mark.

<!-- HTML links -->
<a href="https://example.com">Visit Example</a>
<a href="https://example.com" title="Example Site">Visit Example</a>

<!-- Markdown links -->
[Visit Example](https://example.com)
[Visit Example](https://example.com "Example Site")

<!-- HTML images -->
<img src="photo.jpg" alt="A sunset">
<img src="photo.jpg" alt="A sunset" title="Beautiful sunset">

<!-- Markdown images -->
![A sunset](photo.jpg)
![A sunset](photo.jpg "Beautiful sunset")

Lists

Both ordered and unordered HTML lists have direct Markdown equivalents. Nested lists use indentation (typically two or four spaces).

<!-- HTML unordered list -->
<ul>
  <li>First item</li>
  <li>Second item
    <ul>
      <li>Nested item</li>
    </ul>
  </li>
</ul>

<!-- Markdown equivalent -->
- First item
- Second item
  - Nested item

<!-- HTML ordered list -->
<ol>
  <li>Step one</li>
  <li>Step two</li>
  <li>Step three</li>
</ol>

<!-- Markdown equivalent -->
1. Step one
2. Step two
3. Step three

Code Blocks

<!-- HTML -->
<pre><code class="language-javascript">
function greet(name) {
  return "Hello, " + name;
}
</code></pre>

<!-- Markdown equivalent -->
```javascript
function greet(name) {
  return "Hello, " + name;
}
```

Blockquotes

<!-- HTML -->
<blockquote>
  <p>The best way to predict the future is to invent it.</p>
  <p>— Alan Kay</p>
</blockquote>

<!-- Markdown equivalent -->
> The best way to predict the future is to invent it.
>
> — Alan Kay

Converting HTML Tables to Markdown

Tables are one of the trickiest elements to convert because Markdown tables have significant limitations compared to HTML. Markdown tables do not support colspan, rowspan, or complex cell content. However, simple tables convert cleanly.

<!-- HTML table -->
<table>
  <thead>
    <tr>
      <th>Feature</th>
      <th>HTML</th>
      <th>Markdown</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Headings</td>
      <td>&lt;h1&gt; to &lt;h6&gt;</td>
      <td># to ######</td>
    </tr>
    <tr>
      <td>Bold</td>
      <td>&lt;strong&gt;</td>
      <td>**text**</td>
    </tr>
    <tr>
      <td>Links</td>
      <td>&lt;a href=""&gt;</td>
      <td>[text](url)</td>
    </tr>
  </tbody>
</table>

<!-- Markdown equivalent -->
| Feature  | HTML            | Markdown       |
| -------- | --------------- | -------------- |
| Headings | <h1> to <h6>    | # to ######    |
| Bold     | <strong>         | **text**       |
| Links    | <a href="">      | [text](url)    |

You can also control column alignment in Markdown tables using colons in the separator row: :--- for left-aligned, :---: for centered, and ---: for right-aligned.

Common Use Cases

CMS Migration

When migrating from a traditional CMS (WordPress, Drupal, Joomla) to a modern Markdown-based system (Hugo, Gatsby, Astro, Next.js with MDX), you need to convert hundreds or thousands of HTML articles to Markdown. An automated converter handles the bulk of the work, and you clean up edge cases manually.

// Example: Batch converting WordPress exports with Turndown
import TurndownService from "turndown";
import fs from "fs";

const turndown = new TurndownService({
  headingStyle: "atx",        // Use # style headings
  codeBlockStyle: "fenced",   // Use triple backtick code blocks
  bulletListMarker: "-",      // Use dashes for unordered lists
});

// Convert a single HTML string
const html = fs.readFileSync("wp-export/post-42.html", "utf-8");
const markdown = turndown.turndown(html);
fs.writeFileSync("content/posts/post-42.md", markdown);

console.log("Converted successfully!");

Documentation Generation

Many APIs return HTML-formatted descriptions or documentation. Converting this to Markdown makes it easy to include in README files, developer portals, or Markdown-based documentation sites like Docusaurus or VitePress.

Email to Note-Taking

When you copy content from HTML emails or web pages into note-taking apps like Obsidian, Logseq, or Bear (all Markdown-based), converting the HTML to Markdown first preserves formatting without the bloat of pasted HTML markup.

Handling Edge Cases

Real-world HTML is messy. Here are common edge cases and how to handle them during conversion.

  • Inline styles and classes — Markdown has no concept of CSS classes or inline styles. These are stripped during conversion. If styling is critical, consider keeping those elements as raw HTML within your Markdown file (most Markdown renderers support inline HTML).
  • Divs and spans — Generic container elements like <div> and <span> have no Markdown equivalent. Their content is extracted and the wrappers are discarded.
  • Iframes and embeds — Embedded videos, maps, and widgets cannot be represented in standard Markdown. These are typically preserved as raw HTML or replaced with placeholder links.
  • Nested blockquotes — Markdown supports nested blockquotes with multiple > characters, but deeply nested structures can become hard to read.
  • HTML entities — Characters like &amp;, &lt;, and &nbsp; need to be decoded to their literal characters during conversion.

Programmatic Conversion

For automated pipelines, several well-maintained libraries handle HTML-to-Markdown conversion.

// JavaScript/TypeScript — Turndown
import TurndownService from "turndown";
import { gfm } from "turndown-plugin-gfm";

const turndown = new TurndownService();
turndown.use(gfm); // Adds GitHub Flavored Markdown support
                    // (tables, strikethrough, task lists)

const html = `
  <h2>Features</h2>
  <ul>
    <li><strong>Fast</strong> — converts in milliseconds</li>
    <li><em>Accurate</em> — handles nested structures</li>
    <li><del>Deprecated feature</del></li>
  </ul>
  <table>
    <tr><th>Name</th><th>Type</th></tr>
    <tr><td>id</td><td>number</td></tr>
  </table>
`;

console.log(turndown.turndown(html));
// ## Features
//
// - **Fast** — converts in milliseconds
// - *Accurate* — handles nested structures
// - ~~Deprecated feature~~
//
// | Name | Type   |
// | ---- | ------ |
// | id   | number |
# Python — markdownify
from markdownify import markdownify as md

html = """
<h1>Welcome</h1>
<p>This is a <a href="https://example.com">link</a>
with <strong>bold</strong> text.</p>
"""

result = md(html, heading_style="ATX")
print(result)
# # Welcome
#
# This is a [link](https://example.com)
# with **bold** text.

Need to extract data from websites?

PulpMiner turns any webpage into structured JSON data. No scraping code needed.

Try PulpMiner Free

No credit card required