
HTML ↔ Markdown Converter
Convert between HTML and Markdown in either direction. 100% client-side.
Last reviewed: April 2026New to this tool? Click here for instructions
How to Use the HTML Markdown Converter
To use the HTML Markdown Converter, follow these steps:
1. Choose a direction - select "HTML to Markdown" or "Markdown to HTML" using the chips above the editor.
2. Paste your content into the input area on the left (or top on mobile).
3. View the result - the converted output appears instantly on the right as you type.
4. Copy or download - use the buttons above the output pane to copy to clipboard or download the result file.
When to Use the Tool in Real Workflows
This tool is ideal for developers, technical writers, and content managers who need to convert content between HTML and Markdown formats. It's particularly useful when working with HTML email templates, CMS content, or migrating content between platforms.
How It Works
The HTML Markdown Converter uses a combination of DOMParser for HTML to Markdown conversion and an inline parser for Markdown to HTML conversion. This ensures accurate and standards-compliant results, even for complex nested elements and malformed HTML.
Tips, Edge Cases, or Limitations
This tool handles most common HTML and Markdown elements, but it may not support all edge cases. For complex or custom HTML structures, manual adjustments may be necessary.
The tool runs entirely in your browser, so your data is never sent to a server, ensuring privacy and security.
Frequently Asked Questions
Convert HTML to Markdown or Markdown to HTML instantly in your browser — no uploads, no signup. Paste any HTML fragment or full document and get clean, CommonMark-compliant Markdown out. Flip the direction toggle to go the other way. GitHub Flavored Markdown (GFM) extensions — pipe tables with column alignment, fenced code blocks, and strikethrough — are supported throughout.
What This Tool Does
The converter runs bidirectionally: paste HTML and get Markdown, or paste Markdown and get valid HTML5 fragment output. The baseline format target is the CommonMark specification, with GFM extensions layered on top for pipe tables (including left/center/right column alignment), fenced code blocks with language tags, and ~~strikethrough~~. Conversion happens entirely inside your browser using the DOMParser API — no file is uploaded, no request hits a server, and nothing from your input leaves your local session.
🔒 Privacy: This tool runs 100% in your browser. Your input is not uploaded, stored, or logged anywhere outside your local session.
How to Use It
Step-by-step instructions
- Paste your HTML or Markdown into the left input pane, or click Try Example to auto-populate a realistic GitHub-flavored HTML snippet.
- Confirm the direction toggle is set to your intended conversion: HTML → Markdown or Markdown → HTML.
- Click Convert. Output appears immediately in the right pane.
- Check the status bar below the output for any warnings — for example, a colspan/rowspan notice if the tool encountered a table it had to flatten.
- Click Copy to send the output to your clipboard, or Download to save it as a
.mdor.htmlfile.
Worked example: GitHub-flavored HTML snippet → Markdown output
The following example covers the most common real-world conversion scenarios in one block: a heading, a paragraph with bold and italic, a nested unordered list, a JavaScript fenced code block, a GFM-aligned table, a blockquote, and an image reference.
- Input HTML
-
<h2>Deploy Checklist</h2> <p>Run <strong>all tests</strong> before pushing to <em>production</em>.</p> <ul> <li>Unit tests <ul> <li>Run <code>npm test</code></li> <li>Check coverage ≥ 80%</li> </ul> </li> <li>Integration tests</li> </ul> <pre><code class="language-javascript">const deploy = async () => { await runChecks(); return ship(); }; </code></pre> <table> <thead><tr><th align="left">Step</th><th align="center">Owner</th><th align="right">ETA</th></tr></thead> <tbody><tr><td>Build</td><td>CI Bot</td><td>2 min</td></tr></tbody> </table> <blockquote><p>Never deploy on a Friday.</p></blockquote> <img src="/assets/deploy-flow.png" alt="Deployment flow diagram">
After clicking Convert, the tool calls DOMParser.parseFromString() on the raw HTML, walks the resulting DOM tree depth-first, and serializes each node into its Markdown equivalent. Here is what each element becomes:
<h2>→##heading prefix<strong>/<em>→**all tests**and*production*- Nested
<ul>→ two-space indented-bullets per CommonMark <pre><code class="language-javascript">→ fenced block with thejavascriptlanguage tag intact<table>withalignattributes → GFM pipe table with:----,:----:, and----:alignment markers<blockquote>→>prefix<img>→
- Expected Markdown output
-
## Deploy Checklist Run **all tests** before pushing to *production*. - Unit tests - Run `npm test` - Check coverage ≥ 80% - Integration tests ```javascript const deploy = async () => { await runChecks(); return ship(); }; ``` | Step | Owner | ETA | | :---- | :----: | ----: | | Build | CI Bot | 2 min | > Never deploy on a Friday. 
To reverse the conversion, toggle the direction selector to Markdown → HTML, paste the .md output back into the left pane, and click Convert again to get a clean HTML5 fragment. Round-tripping (HTML → MD → HTML) will not reproduce the original HTML byte-for-byte — attributes and presentational markup are lost in the Markdown step.
Supported HTML Elements and Markdown Equivalents
Block-level elements
Block elements map cleanly to Markdown structural syntax. Headings <h1> through <h6> become one to six leading # characters. Paragraphs are separated by blank lines. Unordered lists use - bullets; ordered lists use 1. numbering. Nested lists indent two spaces per level, per the CommonMark spec. A <blockquote> gains a > prefix on each line, and <hr> becomes ---.
Inline elements
<strong> and <b> both serialize to **text**; <em> and <i> become *text*. Anchor tags (<a href="url">text</a>) become [text](url) with the href preserved. Images map to  using the alt and src attributes. Inline <code> wraps in backticks; <br> produces two trailing spaces or a blank line depending on context.
GFM extensions
GitHub Flavored Markdown adds three extensions beyond CommonMark that this tool supports. Fenced code blocks from <pre><code class="language-*"> pairs emit triple-backtick fences with the language identifier extracted from the class name. GFM pipe tables render with alignment colons derived from each <th> element's align attribute (left → :---, center → :---:, right → ---:). Strikethrough via <del> or <s> becomes ~~text~~.
| HTML Element / Pattern | Markdown / GFM Output | Fidelity Notes |
|---|---|---|
<h1> | # Heading | Full fidelity |
<h2>–<h6> | ##–###### | Full fidelity |
<p> | Blank-line-separated text block | Full fidelity |
<strong>, <b> | **text** | Full fidelity |
<em>, <i> | *text* | Full fidelity |
<a href="url"> | [text](url) | Only href preserved; title, class, etc. dropped |
<img src="" alt=""> |  | Only src and alt preserved |
<ul><li> | - item | Nested: 2-space indent per level |
<ol><li> | 1. item | Nested: 2-space indent per level |
<blockquote> | > text | Full fidelity |
<pre><code class="language-js"> | ```js … ``` | Language tag extracted from class name |
<code> (inline) | `code` | Full fidelity |
<table> with align | GFM pipe table with alignment colons | colspan/rowspan not supported |
<del>, <s> | ~~text~~ | GFM extension |
<hr> | --- | Full fidelity |
<br> | Two trailing spaces or blank line | Context-dependent |
<div>, <span> | Inner text only | Tags and attributes stripped |
<script>, <style> | (empty) | Entire block dropped |
HTML entities (&, ©) | Unicode literal (&, ©) | Decoded by DOMParser before walk |
HTML comments (<!-- -->) | (empty) | Stripped entirely |
Edge Cases and Conversion Gotchas
Elements that lose fidelity
Generic container elements like <div> and <span> have no Markdown equivalent, so the converter strips their tags and passes through their text content. Inline styles (style="color:red") and CSS class names are silently dropped — Markdown has no per-element styling syntax. Documents that use styling to communicate meaning (red text for errors, a bold class for callouts) will lose that semantic signal on conversion. HTML comments (<!-- -->) are removed entirely during the DOMParser step, before the node walker runs. Entire <script> and <style> blocks are also dropped.
Attributes that are silently dropped
Of all HTML attributes, only href (on anchors), src and alt (on images), and the language class on <code> blocks have Markdown analogs. Everything else — id, class, data-*, aria-*, title, style — is dropped without warning. If attribute preservation matters (for example, you rely on id anchors for in-page links), Markdown is the wrong target format.
Nested and malformed HTML behavior
Nested lists work correctly up to three levels deep using two-space indentation per CommonMark. Beyond that, the Markdown output is technically valid but several renderers — including GitHub's — will not visually distinguish the deeper nesting. GFM tables that include colspan or rowspan attributes cannot be represented as pipe tables; the converter flattens the affected cells and displays a warning badge in the status bar. Malformed HTML — unclosed tags, mismatched nesting — is corrected automatically by DOMParser's error-recovery logic before conversion begins, so the Markdown output may differ from what a naive string-search of the raw HTML would suggest. HTML entities such as &, <, ©, and are decoded to their Unicode equivalents by DOMParser; the resulting Markdown contains the literal characters. Any Markdown special characters appearing in text content (*, _, [, ], backtick) are automatically escaped with a backslash to prevent unintended formatting in the output.
| HTML Pattern | Conversion Behavior | Recommended Workaround |
|---|---|---|
<div> / <span> wrappers |
Tags stripped; inner text preserved | Refactor to semantic elements (<p>, <section>) before converting |
style="..." inline styles |
Attribute dropped entirely | Apply styling post-conversion via a CSS framework or Markdown-native callout syntax |
<!-- comments --> |
Stripped before node walk | Replace comments with visible prose before converting if content must survive |
colspan / rowspan in tables |
Cells flattened; warning shown in status bar | Restructure table to single-row-per-record layout, or keep as HTML |
| Nested lists beyond 3 levels | Markdown produced, but some renderers ignore deep indent | Flatten hierarchy or use a numbered section structure |
id, class, data-*, aria-* attributes |
Dropped silently — no Markdown equivalent | Stay with HTML if attribute semantics must be preserved |
HTML entities (&, ©, ) |
Decoded to Unicode literals by DOMParser | Expected behavior; no action needed in most cases |
<script> and <style> blocks |
Entire block content dropped | Extract scripts/styles separately before converting the document |
When to Convert HTML to Markdown vs Staying With HTML
Use Markdown when
Markdown suits content that lives in a Git repository, gets reviewed in pull requests, or targets a static site generator. README files on GitHub and GitLab render natively from .md files. Generators like Jekyll, Hugo, and Astro accept Markdown source and apply their own HTML templates around it. Documentation platforms — Notion, Confluence, Readme.io — ingest Markdown directly, giving writers a portable format not locked to one CMS. Plain-text readability is another reason to prefer Markdown: a .md file is human-readable in any text editor without rendering.
Stay with HTML when
HTML is irreplaceable for complex table layouts using merged cells, multi-column designs, custom CSS styling, or embedded media with specific attributes. WordPress and Drupal store and serve HTML for rendered pages; converting that HTML to Markdown and back introduces attribute loss and formatting changes. Any document that depends on id anchors for in-page navigation, aria-* attributes for accessibility, or data-* attributes for JavaScript hooks must stay in HTML. Round-trip fidelity is also a practical concern: converting HTML → MD → HTML will not reproduce the original markup byte-for-byte, and doing so repeatedly compounds attribute loss.
Behind the Scenes: DOMParser and Recursive Node Walking
HTML→Markdown pipeline
Clicking Convert in HTML→Markdown mode triggers DOMParser.parseFromString(input, 'text/html'), producing a live DOM tree in-browser — including the browser's own error-correction for malformed HTML. A recursive depth-first walker then traverses every node. For each node, the walker checks Node.nodeType (text vs element) and, for elements, the lowercased tagName. Each tag dispatches to a serializer function: h2 prepends ##, strong wraps content in **, and table triggers the GFM table serializer. GFM table detection reads each <th> element's align attribute and maps it to the correct alignment colon syntax (:---, :---:, ---:). Text nodes are scanned for Markdown special characters — *, _, [, ], and backtick — which are backslash-escaped to prevent unintended formatting in the output.
Markdown→HTML pipeline
The reverse direction runs a CommonMark-compliant parser (targeting the CommonMark 0.31.2 specification) inside a Web Worker to avoid blocking the main UI thread during larger conversions. The parser tokenizes the Markdown input according to the spec's block-then-inline parsing order, then emits a valid HTML5 fragment. GFM extensions (pipe tables, fenced code, strikethrough) are handled by a GFM layer on top of the CommonMark base. The output is a clean HTML fragment — no <html>, <head>, or <body> wrappers — ready to paste into a CMS or template directly.
Related Tools
Use the Markdown Preview to render your converted Markdown output before publishing — paste the result directly and see formatted output side-by-side. Other tools that fit naturally into the same workflow:
- Format and validate API responses with the JSON Formatter before embedding JSON data blocks in your Markdown docs.
- Convert configuration files between formats using the YAML Converter, then document them in Markdown.
- Transform tabular data exports with CSV to JSON before writing comparison tables in Markdown.
- Test and debug content extraction patterns with the Regex Tester when batch-processing HTML documents.
- Normalize language-specific formatting in code samples using the Code Formatter before dropping them into fenced code blocks.