portable-text-conversion — Claude Code skill: HTML and Markdown
Turns HTML and Markdown into Portable Text blocks for Sanity. Useful when migrating content off a legacy CMS or building an import pipeline.
npx skills add sanity-io/agent-toolkit --skill portable-text-conversion -a claude-codeWhat portable-text-conversion does
The skill lays out three conversion routes. Markdown goes through markdownToPortableText from @portabletext/markdown, which the author recommends as the default choice. HTML goes through htmlToBlocks from @portabletext/block-tools. The third route is manual: build blocks yourself when the source is an API or a database rather than markup.
It also documents the Portable Text spec — an array of blocks, each with _type, _key, children holding spans, and markDefs for annotations. Lists live on regular blocks via listItem and level. The rules sit in separate files: rules/markdown-to-pt.md, rules/html-to-pt.md, rules/manual-construction.md. The agent reads whichever matches your source format.
Watch the package names. @sanity/block-tools is the legacy one; new projects should pull @portabletext/block-tools, and the API is identical. If your repo still carries the old package, check whether it comes in through another dependency.
What it helps with
- migrate articles from a legacy CMS into Sanity
- import Markdown files as Portable Text documents
- build a pipeline that ingests HTML from an external source
- construct blocks by hand from API or database rows
- figure out how
_key,markDefsand lists work in Portable Text
Example prompts for the agent
- Convert this Markdown into Portable Text blocks with
markdownToPortableText - Parse this HTML page into PT blocks using
htmlToBlocksand show the output - Build a Portable Text block array by hand from this data
How to install portable-text-conversion
1Skills CLI (Vercel)
npx skills add sanity-io/agent-toolkit --skill portable-text-conversion -a claude-code2Manually: put the files in place
~/.claude/skills/portable-text-conversion/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/portable-text-conversion/
1Skills CLI (Vercel)
npx skills add sanity-io/agent-toolkit --skill portable-text-conversion -a codex2Manually: put the files in place
.agents/skills/portable-text-conversion/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add sanity-io/agent-toolkit --skill portable-text-conversion -a cursor2Manually: put the files in place
.cursor/skills/portable-text-conversion/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install sanity-io/agent-toolkit portable-text-conversion2Manually: put the files in place
.github/skills/portable-text-conversion/SKILL.md1Skills CLI (Vercel)
npx skills add sanity-io/agent-toolkit --skill portable-text-conversion -a gemini-cli2Platform CLI
gemini skills install https://github.com/sanity-io/agent-toolkit --path skills/portable-text-conversionManually: put the files in place
.devin/skills/portable-text-conversion/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add sanity-io/agent-toolkit --skill portable-text-conversion -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about portable-text-conversion
How do I install portable-text-conversion in Claude?
Run npx skills add sanity-io/agent-toolkit --skill portable-text-conversion -a claude-code. The skill lands in ~/.claude/skills/portable-text-conversion/SKILL.md.
What does portable-text-conversion do?
It gives the agent the functions, block structure and markup rules for converting HTML and Markdown into Portable Text blocks for Sanity.
How is @portabletext/block-tools different from @sanity/block-tools?
Same API, different package name. @sanity/block-tools is the legacy one, and new projects should use @portabletext/block-tools.
Source text
SKILL.md · MIT
---
name: portable-text-conversion
description: Convert HTML and Markdown content into Portable Text blocks for Sanity. Use when migrating content from legacy CMSs, importing HTML or Markdown into Sanity, building content pipelines that ingest external content, converting rich text between formats, or programmatically creating Portable Text documents. Covers @portabletext/markdown (markdownToPortableText), @portabletext/block-tools (htmlToBlocks), custom deserializers, and the Portable Text specification for manual block construction.
license: MIT
metadata:
author: sanity
version: "1.0.0"
---
# Portable Text Conversion
Convert external content (HTML, Markdown) into Portable Text for Sanity. Three main approaches:
1. **`markdownToPortableText`** — Convert Markdown directly using `@portabletext/markdown` (recommended for Markdown)
2. **`htmlToBlocks`** — Parse HTML into PT blocks using `@portabletext/block-tools` (for HTML migration)
3. **Manual construction** — Build PT blocks directly from any source (APIs, databases, etc.)
## Portable Text Specification
Understand the target format before converting. PT is an array of blocks:
```json
[
{
"_type": "block",
"_key": "abc123",
"style": "normal",
"children": [
{"_type": "span", "_key": "def456", "text": "Hello ", "marks": []},
{"_type": "span", "_key": "ghi789", "text": "world", "marks": ["strong"]}
],
"markDefs": []
},
{
"_type": "block",
"_key": "jkl012",
"style": "h2",
"children": [
{"_type": "span", "_key": "mno345", "text": "A heading", "marks": []}
],
"markDefs": []
},
{
"_type": "image",
"_key": "pqr678",
"asset": {"_type": "reference", "_ref": "image-abc-200x200-png"}
}
]
```
**Key rules:**
- Every block and span needs `_key` (unique within the array)
- `_type: "block"` is for text blocks; custom types use their own `_type`
- `markDefs` holds annotation data; `marks` on spans reference `markDefs[*]._key` or are decorator strings
- Lists use `listItem` ("bullet" | "number") and `level` (1, 2, 3...) on regular blocks
## Conversion Rules
Read the rule file matching your source format:
- **Markdown → Portable Text**: `rules/markdown-to-pt.md` — `@portabletext/markdown` with `markdownToPortableText` (recommended)
- **HTML → Portable Text**: `rules/html-to-pt.md` — `@portabletext/block-tools` with `htmlToBlocks`
- **Manual PT Construction**: `rules/manual-construction.md` — build blocks programmatically from any source
> **Note:** `@sanity/block-tools` is the legacy package name. Always use `@portabletext/block-tools` for new projects. The API is the same.
© sanity · MIT · sanity-io/agent-toolkit