3.8 KiB
3.8 KiB
title, description, tags
| title | description | tags | ||||
|---|---|---|---|---|---|---|
| Serialize Portable Text to Markdown | Convert Portable Text to Markdown strings using @portabletext/markdown |
|
Serialize Portable Text to Markdown
Use @portabletext/markdown to convert PT blocks to Markdown strings. Useful for AI/LLM pipelines, static site generators, README generation, and anywhere Markdown is the target format.
npm install @portabletext/markdown
Basic Usage
import {portableTextToMarkdown} from '@portabletext/markdown'
const markdown = portableTextToMarkdown(portableTextBlocks)
Built-in Support
Out of the box, portableTextToMarkdown handles:
- Headings (h1–h6)
- Paragraphs
- Bold (
**), italic (_), inline code (`), strikethrough (~~) - Links (
[text](url)) - Blockquotes (
>) - Ordered and unordered lists (including nested)
- Code blocks (fenced with language)
- Horizontal rules (
---) - Images (
) - Tables (GFM)
Built-in Type Renderers
The library exports default renderers for common block object types. Enable them explicitly:
import {
portableTextToMarkdown,
DefaultCodeBlockRenderer,
DefaultImageRenderer,
DefaultHorizontalRuleRenderer,
DefaultTableRenderer,
DefaultHtmlRenderer,
} from '@portabletext/markdown'
const markdown = portableTextToMarkdown(blocks, {
types: {
'code': DefaultCodeBlockRenderer, // {code, language?} → fenced code block
'image': DefaultImageRenderer, // {src, alt?, title?} → 
'horizontal-rule': DefaultHorizontalRuleRenderer, // → ---
'table': DefaultTableRenderer, // {rows, headerRows?} → GFM table
'html': DefaultHtmlRenderer, // {html} → raw HTML
},
})
Custom Renderers
Handle custom block types and marks with renderer functions:
const markdown = portableTextToMarkdown(blocks, {
// Custom block types — receives {value, index, isInline}
types: {
callout: ({value}) => `> **${value.title}**\n> ${value.text}`,
image: ({value, isInline}) => {
if (isInline) return ''
return ``
},
},
// Custom block style renderers — receives {value, children, index}
block: {
h1: ({children}) => `# ${children}`,
blockquote: ({children}) => `> ${children}`,
},
// Custom mark renderers — receives {value, children, text, markType, markKey}
marks: {
highlight: ({children}) => `==${children}==`,
internalLink: ({children, value}) => `[${children}](/docs/${value.slug})`,
},
// Custom list item renderer — receives {value, children, listIndex}
listItem: ({children}) => children,
// Control spacing between blocks — function, not string
blockSpacing: ({current, next}) => {
if (current.listItem && next.listItem) return '\n'
return undefined // use default (\n\n)
},
// Handle unknown types gracefully
unknownType: ({value}) => `<!-- Unknown type: ${value._type} -->`,
unknownMark: ({children}) => children,
})
Use Cases
| Use Case | Why Markdown |
|---|---|
| AI/LLM context | Models work well with Markdown input |
| Static site generators | Hugo, Jekyll, Eleventy consume Markdown |
| README generation | Generate docs from Sanity content |
| Email (with converter) | Markdown → HTML for email templates |
| Export/backup | Human-readable content export |
| Documentation pipelines | Sanity as docs CMS, output as Markdown |
Bidirectional: Also Converts Markdown → PT
The same package also provides markdownToPortableText() for the reverse direction. See the portable-text-conversion skill for details.
Reference
- @portabletext/markdown
- Part of the portabletext/editor monorepo