Skills
Skill 4 of 8
Render and serialize Portable Text to React, Svelte, Vue, Astro, HTML, Markdown, and plain text.
1 minute · 258 words · 9 sections
Install
npx skills add sanity-io/agent-toolkit --skill portable-text-serializationnpx skills add sanity-io/agent-toolkit/plugin marketplace add sanity-io/agent-toolkitThe first command installs just this skill, by the name in its SKILL.md; the second installs the whole repository.
Render Portable Text content across frameworks using the @portabletext/* library family. Each library follows the same component-mapping pattern: you provide a components object that maps PT node types to framework-specific renderers.
PT is an array of blocks. Each block has _type, optional style, children (spans), markDefs, listItem, and level.
Root array
├── block (_type: "block")
│ ├── style: "normal" | "h1" | "h2" | "blockquote" | ...
│ ├── children: [span, span, ...]
│ │ └── span: { _type: "span", text: "...", marks: ["strong", "<markDefKey>"] }
│ ├── markDefs: [{ _key, _type: "link", href: "..." }, ...]
│ ├── listItem: "bullet" | "number" (optional)
│ └── level: 1, 2, 3... (optional, for nested lists)
├── custom block (_type: "image" | "code" | any custom type)
└── ...more blocksMarks come in two forms:
marks[] like "strong", "em", "underline", "code"marks[] referencing entries in markDefs[] (e.g., links, internal references)Every @portabletext/* library accepts a components object with these keys:
| Key | Renders | Props/Data |
|---|---|---|
types | Custom block/inline types (image, code, CTA) | value (the block data) |
marks | Decorators + annotations | children + value (mark data) |
block | Block styles (h1, normal, blockquote) | children |
list | List wrappers (ul, ol) | children |
listItem | List items | children |
hardBreak | Line breaks within a block | — |
Read the rule file matching your framework:
rules/react.md — @portabletext/react or next-sanityrules/svelte.md — @portabletext/svelterules/vue.md — @portabletext/vuerules/astro.md — astro-portabletextrules/html.md — @portabletext/to-htmlrules/markdown.md — @portabletext/markdownrules/plain-text.md — @portabletext/toolkitThese are listed on portabletext.org (opens in a new tab) but don’t have dedicated rule files:
| Target | Package |
|---|---|
| React Native | @portabletext/react-native-portabletext |
| React PDF | @portabletext/react-pdf-portabletext |
| Solid | solid-portabletext |
| Qwik | portabletext-qwik |
| Shopify Liquid | portable-text-to-liquid |
| PHP | sanity-php (SanityBlockContent class) |
| Python | portabletext-html |
| C# / .NET | dotnet-portable-text |
| Dart / Flutter | flutter_sanity_portable_text |
PT renderers only handle standard blocks by default. Custom types (image, code, callToAction, etc.) require explicit component mappings — they won’t render otherwise.
In React/Vue, define components outside the render function or memoize it. Recreating on every render causes unnecessary re-renders.
All libraries accept onMissingComponent to control behavior when encountering unknown types:
false — suppress warningsAlways expand references inside custom blocks:
body[]{
...,
_type == "image" => {
...,
asset->
},
markDefs[]{
...,
_type == "internalLink" => {
...,
"slug": @.reference->slug.current
}
}
}Render and serialize Portable Text to React, Svelte, Vue, Astro, HTML, Markdown, and plain text. Use when implementing Portable Text rendering in any frontend framework, building custom serializers for non-standard block types, converting Portable Text to HTML strings server-side, converting Portable Text to Markdown, extracting plain text from Portable Text, or troubleshooting rendering issues with marks, blocks, lists, or custom types.
The verbatim description from this skill’s front matter — the string an agent matches on to decide whether to load it.
skills/portable-text-serialization/SKILL.mdmain, last pushed 16 September 2026.SKILL.md, not by matching a directory convention. One layout observed: skills/*/SKILL.md.h1 and no skipped levels:.claude-plugin/marketplace.json by Sanity, declaring 1 plugin. It is read for editorial metadata only — never as the skill index, which is always the repository tree./sanity-io/agent-toolkit.md, and each skill at its own .md URL.7 files · 20 KB
Everything this skill ships beside its prose. All of it is set here, as subchapters of skill 4.
Everything else published alongside the skill.