Markdown
@portabletext/markdown renders Portable Text blocks as Markdown strings. Use it to generate Markdown for static site generators, README files, AI prompts, or any system that consumes Markdown.
Looking to convert Markdown into Portable Text? See the Markdown to Portable Text conversion guide.
Install
Section titled “Install”npm i @portabletext/markdownpnpm add @portabletext/markdownyarn add @portabletext/markdownBasic usage
Section titled “Basic usage”portableTextToMarkdown takes an array of Portable Text blocks and returns a Markdown string. Standard block styles, decorators, and links are handled automatically.
import {portableTextToMarkdown} from '@portabletext/markdown'
const markdown = portableTextToMarkdown(blocks)Custom type renderers
Section titled “Custom type renderers”Custom block types (objects in the blocks array) are not rendered by default. Register a renderer for each type you use.
portableTextToMarkdown(blocks, { types: { chart: ({value}) => ``, },})Type renderers receive value (the block object), index (position in the array), and isInline (whether the object appears inline or as a block). Return an empty string to skip an element entirely.
portableTextToMarkdown(blocks, { types: { image: ({value, isInline}) => { if (isInline) return '' return `` }, },})Custom block style renderers
Section titled “Custom block style renderers”Override how block styles render by providing a block map. Each renderer receives value (the block) and children (the already-rendered content of the block).
portableTextToMarkdown(blocks, { block: { h1: ({children}) => `# ${children} #`, blockquote: ({children}) => `<blockquote>${children}</blockquote>`, },})Built-in type renderers
Section titled “Built-in type renderers”The package exports default renderers for common block types. Import and register the ones you need.
import { DefaultCalloutRenderer, DefaultCodeBlockRenderer, DefaultHorizontalRuleRenderer, DefaultHtmlRenderer, DefaultImageRenderer, DefaultTableRenderer, portableTextToMarkdown,} from '@portabletext/markdown'
portableTextToMarkdown(blocks, { types: { 'callout': DefaultCalloutRenderer, 'code': DefaultCodeBlockRenderer, 'horizontal-rule': DefaultHorizontalRuleRenderer, 'html': DefaultHtmlRenderer, 'image': DefaultImageRenderer, 'table': DefaultTableRenderer, },})| Renderer | Expected fields | Output |
|---|---|---|
DefaultCalloutRenderer | tone, content | > [!TYPE]\n> content |
DefaultCodeBlockRenderer | code, language? | ```lang\ncode\n``` |
DefaultHorizontalRuleRenderer | (none required) | --- |
DefaultHtmlRenderer | html | Raw HTML string |
DefaultImageRenderer | src, alt?, title? |  |
DefaultTableRenderer | rows, headerRows? | Markdown table |
Renderer map keys
Section titled “Renderer map keys”| Key | What it renders |
|---|---|
types | Custom block and inline objects |
marks | Annotations and decorators |
block | Block styles (headings, blockquotes, etc.) |
listItem | List items |
hardBreak | Line breaks within blocks |
unknownType | Fallback for unregistered types |
unknownBlockStyle | Fallback for unregistered block styles |
unknownListItem | Fallback for unregistered list items |
unknownMark | Fallback for unregistered marks |
By default, unknown types render as JSON code blocks. Unknown marks, block styles, and list items pass through their children unchanged.
Supported features
Section titled “Supported features”| Feature | PT to Markdown |
|---|---|
| Headings (h1-h6) | ✅ |
| Paragraphs | ✅ |
| Bold | ✅ |
| Italic | ✅ |
| Inline code | ✅ |
| Strikethrough | ✅ |
| Links | ✅ |
| Blockquotes | ✅ |
| Ordered lists | ✅ |
| Unordered lists | ✅ |
| Nested lists | ✅ |
| Code blocks | ✅* |
| Horizontal rules | ✅* |
| Images | ✅* |
| Tables | ✅* |
| HTML blocks | ✅* |
| Callouts | ✅* |
* Requires registering the built-in renderer (see above).
Further reading
Section titled “Further reading”- Markdown to Portable Text for converting Markdown into PT blocks
@portabletext/markdownon GitHub for full API documentation and changelog