Converting Markdown to Sanity: HTML intermediary vs direct conversion?
I hear you—the markdown migration process can definitely be tricky! You're actually on a good path, and there are multiple approaches depending on your content structure.
Markdown → HTML → Portable Text vs. Direct Markdown → Portable Text
Both approaches work, but here's what to consider:
Going the HTML route (what you're exploring):
- Use
@sanity/block-toolswith thehtmlToBlocksfunction - This is actually a solid approach because HTML is more structured than markdown
- The Ghost HTML migration guide you linked uses this pattern and it's battle-tested
- The package handles basic rich text formatting (headings, paragraphs, lists) automatically
Direct markdown route:
- You'd need to parse markdown to MDAST (Markdown Abstract Syntax Tree) using something like
remark - Then write custom transformation logic to convert MDAST to Portable Text blocks
- There isn't a well-maintained "markdown-to-portable-text" package in the ecosystem
- The
@portabletextpackages mostly work in reverse (Portable Text → other formats)
My recommendation: Stick with the HTML approach you're exploring. Here's why:
- Better tooling support:
@sanity/block-toolshas proven utilities for HTML → Portable Text conversion - Easier to handle complex content: Markdown parsers vary in how they handle edge cases, but HTML is more standardized
- Custom deserialization rules: You can define custom rules to handle specific HTML elements like
<figure>or<img>tags and transform them into custom block types - The Ghost guide pattern works: Convert markdown → HTML (using
marked,remark-html, or similar), then usehtmlToBlocks
Quick implementation pattern:
import {htmlToBlocks} from '@sanity/block-tools'
import {JSDOM} from 'jsdom'
import {marked} from 'marked' // or your preferred markdown parser
// Convert markdown to HTML first
const html = marked(yourMarkdownContent)
// Then to Portable Text
const blocks = htmlToBlocks(html, blockContentType, {
parseHtml: html => new JSDOM(html).window.document
})Pro tips:
- Test with your most complex markdown first (nested lists, code blocks, images, inline styles)
- You may need custom deserialization rules for specific markdown features
- Keep your original markdown files as backup during migration
- Remember that
htmlToBlocksis synchronous, so you'll need to handle image uploads and asset references in a separate post-processing step
The two-step process (markdown → HTML → Portable Text) gives you more control and better debugging when things don't convert perfectly. Good luck with your migration!
Show original thread32 replies
Sanity – Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.