Skip to content
Dev Utilities

HTML to Markdown

Convert an HTML fragment into Markdown

0 chars
0 chars

How the conversion works

  • Parsing uses DOMParser, which builds an inert document: scripts in the input are never executed, <img src> and <link href> are never fetched, and no layout runs. So pasting a page you do not trust costs you nothing, and nothing leaves this tab.
  • Because it is the real HTML parser, broken markup still yields a sensible tree — implied <tbody>, unclosed <p>, mis-nested emphasis — and character references such as &amp; and &#8212; arrive already decoded.
  • Handled: headings, paragraphs, hard breaks, emphasis, strong, strikethrough, inline code, <pre> as a fenced block with its language class, links (including bare-URL autolinks), images, nested ordered and unordered lists, blockquotes, tables and horizontal rules.
  • <script>, <style>, <noscript>, <template>, <svg>, media and form controls are dropped whole — their contents are not prose. Everything else with no Markdown equivalent (<span>, class, style, id) keeps its text and loses its markup, rather than being smuggled through as raw HTML.
  • Text is escaped so a round trip is stable: a literal 2 * 3 comes out as 2 \* 3 and will not turn into emphasis. Underscores inside words are left alone, because some_variable_name is not emphasis in any CommonMark implementation and escaping it makes the output unreadable.
  • This converts, it does not sanitise. A javascript: href in the input comes out as a javascript: link target in the Markdown, because faithfully reporting what the HTML said is the job here. Markdown text is inert, but whatever renders it later needs its own URL policy — the Markdown preview tool on this site refuses those schemes.
  • Known limits: table cells are flattened to one line (pipe tables cannot hold paragraphs), a table with no header row gets an empty one because GFM requires it, definition lists degrade to bold terms, and data:image URIs are kept verbatim and can be very long. Anything the walk had to compromise on is listed in a comment at the end of the output.