Toolium

HTML to Markdown: The Migration Tool I Wish Existed Sooner

Hemanth Gedda6 min read

The first time I had to do a serious content migration - moving about 80 blog posts from a hosted WordPress instance to a static site generator - I spent an embarrassing afternoon converting HTML to Markdown by hand. Open a post, copy the HTML, manually replace <h2> with ##, <strong> with **, <a href="..."> with [text](url), and so on. After three posts I was tired. After ten posts I gave up and found Turndown, the HTML-to-Markdown conversion library that I should have used from the start.

Toolium's HTML to Markdown tool uses Turndown. If you ever need to do this kind of migration, please learn from my mistake and use a converter.

What converts cleanly

The standard HTML elements map directly to Markdown:

  • Headings. <h1> through <h6> become # through ######.
  • Paragraphs. <p> becomes a paragraph separated by blank lines.
  • Inline formatting. <strong> and <b> become **bold**. <em> and <i> become *italic*. <code> becomes `inline code`.
  • Links. <a href="URL">text</a> becomes [text](URL).
  • Images. <img src="URL" alt="text"> becomes ![text](URL).
  • Lists. Unordered lists become - bullets; ordered lists become 1. 2. 3.
  • Blockquotes. <blockquote> becomes > lines.
  • Code blocks. <pre><code> becomes a fenced code block. If the code element has a class like "language-python" or "lang-js," the fence gets the language hint (handy for syntax highlighting in the destination).
  • Tables. <table> with <thead> and <tbody> becomes a GFM-style Markdown table.
  • Horizontal rules. <hr> becomes ---.

For typical blog post content - prose with formatting, links, embedded images, occasional code blocks - the conversion handles 95% of the work and the result is genuinely clean Markdown ready to commit.

What does not convert

  • Custom CSS styling. Inline styles (font, color, alignment) are dropped because Markdown has no equivalent. If your post depended on specific CSS to look right, the Markdown version will look different and you have to redo that layer.
  • Divs with class names. Most CSS layouts use divs with classes for structure. Markdown has no semantic equivalent; the divs get dropped and only the contents survive.
  • Embedded videos. Most platforms use iframes for video embeds. Markdown has no iframe support; the video links need to be re-added manually in the destination.
  • JavaScript. Any script tags get stripped. Markdown is content-only.
  • Custom HTML elements. Web components, MathJax, custom blocks. Most of these survive in the Markdown as HTML (Markdown allows embedded HTML), but they look ugly in the source and may not render in some Markdown viewers.

The CMS-export caveat

Most CMS export tools produce HTML that includes a lot of CMS-specific wrapper markup. WordPress, for example, adds inline styles, paragraph tags around line breaks, and "wp-" prefixed classes everywhere. Squarespace adds its own block IDs. Medium adds <p data-selectable-paragraph=""> to every paragraph.

The Turndown library handles most of this gracefully - it ignores attributes that have no Markdown equivalent. But if you find your converted Markdown is unexpectedly cluttered with HTML attributes, that is usually CMS-specific markup that needs to be stripped before conversion.

A useful workflow for cleaning CMS HTML before conversion: paste the HTML into the browser's developer tools, manually remove the wrapper elements you do not need, then paste the cleaned HTML into the converter. Tedious but produces much better results.

Round-tripping is asymmetric

Converting HTML to Markdown loses information. Converting that Markdown back to HTML produces a simpler, cleaner version of the original HTML - usually missing the custom styling, the class names, the divs. This is not a bug; Markdown's whole point is to be simpler.

For migrations where preserving the original visual layout matters, you have to apply CSS in the destination to recreate the styling. For migrations where the content is what matters and the layout will be redesigned anyway, the loss is fine.

The migration workflow I use

For migrating content from CMS to static site:

  1. Export the source content as HTML (most CMS platforms have an export option).
  2. Run each post's HTML through the converter.
  3. Save the Markdown output as a file in the new repo.
  4. Add frontmatter (title, date, slug, etc.) at the top.
  5. Spot-check the converted Markdown for any HTML that survived because Turndown could not convert it.
  6. Manually fix the things that did not convert: video embeds, custom layouts, special styling.

For bulk migrations, this is scriptable using the same Turndown library directly in Node.js. The Toolium tool is best for one-off conversions where setting up a script is more work than just pasting and copying.

Privacy

The converter runs in your browser. Turndown is a JavaScript library that does the conversion locally. The HTML you paste does not leave your computer. If you are migrating content from a private wiki, internal documentation, or a draft blog post, the contents stay on your machine.

Try the tool mentioned in this article

Open tool