add skills
Build and Deploy / build-and-deploy (push) Successful in 2m53s

This commit is contained in:
2026-05-18 08:39:42 +02:00
parent 92a80e8759
commit 57af0b8386
53 changed files with 10190 additions and 0 deletions
@@ -0,0 +1,123 @@
---
title: Serialize Portable Text to Markdown
description: Convert Portable Text to Markdown strings using @portabletext/markdown
tags: [portable-text, markdown, serialization, conversion]
---
# 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.
```bash
npm install @portabletext/markdown
```
## Basic Usage
```ts
import {portableTextToMarkdown} from '@portabletext/markdown'
const markdown = portableTextToMarkdown(portableTextBlocks)
```
## Built-in Support
Out of the box, `portableTextToMarkdown` handles:
- Headings (h1h6)
- Paragraphs
- Bold (`**`), italic (`_`), inline code (`` ` ``), strikethrough (`~~`)
- Links (`[text](url)`)
- Blockquotes (`>`)
- Ordered and unordered lists (including nested)
- Code blocks (fenced with language)
- Horizontal rules (`---`)
- Images (`![alt](url)`)
- Tables (GFM)
## Built-in Type Renderers
The library exports default renderers for common block object types. Enable them explicitly:
```ts
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?} → ![alt](src "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:
```ts
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 `![${value.alt || ''}](${value.url})`
},
},
// 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](https://github.com/portabletext/editor/tree/main/packages/markdown)
- Part of the [portabletext/editor](https://github.com/portabletext/editor) monorepo