Skip to main content
skill CX Codex

portable-text-serialization — Claude Code skill for Portable Text

A skill that shows the agent how to render and serialize Portable Text to React, Svelte, Vue, Astro, HTML, Markdown and plain text.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add sanity-io/agent-toolkit --skill portable-text-serialization -a claude-code
Open on GitHub

What portable-text-serialization does

Portable Text is an array of blocks from Sanity: each block carries _type, a style, children with spans, markDefs, and for lists also listItem and level. The skill walks the agent through that shape. From there it points to the @portabletext/* family, where every library uses the same trick — you hand over a components object that maps node types to framework-specific renderers.

Separate rule files cover each target: rules/react.md for React and Next.js, rules/svelte.md, rules/vue.md, rules/astro.md, plus the server-side rules/html.md, rules/markdown.md and rules/plain-text.md. The components keys are the same everywhere: types for custom blocks like images or code, marks for decorators and annotations, then block, list, listItem and hardBreak.

Custom types are where this breaks. Renderers handle standard blocks out of the box, so an image, code or callToAction block stays invisible until you map it explicitly. In React and Vue, keep the components object outside the render function or memoize it — recreating it on every render causes needless re-renders. All the libraries accept onMissingComponent, so you can silence warnings for unknown types or route them to a log.

What it helps with

  • render a Sanity article body in React or Next.js
  • build an HTML string from Portable Text on the server
  • convert Portable Text to Markdown
  • pull plain text out of a block without markup
  • write a serializer for a custom block such as an image or code

Example prompts for the agent

  • Render this Portable Text in React and wire the markDefs links through next/link
  • Convert the document to Markdown for an export
  • Extract just the text from this block, no marks or links

How to install portable-text-serialization

1Skills CLI (Vercel)

npx skills add sanity-io/agent-toolkit --skill portable-text-serialization -a claude-code

2Manually: put the files in place

~/.claude/skills/portable-text-serialization/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/portable-text-serialization/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about portable-text-serialization

How do I install portable-text-serialization in Claude?

Run npx skills add sanity-io/agent-toolkit --skill portable-text-serialization -a claude-code. The skill lands at ~/.claude/skills/portable-text-serialization/SKILL.md.

What does portable-text-serialization do?

It teaches the agent to render and serialize Portable Text: it reads the block, span and markDefs structure and picks the right @portabletext/* library for your framework.

Why do my custom blocks not show up?

Renderers only cover standard blocks. For image, code or your own types you have to map a component under the types key of the components object.

Source text

SKILL.md · MIT
---
name: portable-text-serialization
description: 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.
license: MIT
metadata:
  author: sanity
  version: "1.0.0"
---

# Portable Text Serialization

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.

## Portable Text Structure (Quick Reference)

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 blocks
```

**Marks** come in two forms:
- **Decorators**: string values in `marks[]` like `"strong"`, `"em"`, `"underline"`, `"code"`
- **Annotations**: keys in `marks[]` referencing entries in `markDefs[]` (e.g., links, internal references)

## Component Mapping Pattern (All Frameworks)

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 | — |

## Framework-Specific Rules

Read the rule file matching your framework:

- **React / Next.js**: `rules/react.md` — `@portabletext/react` or `next-sanity`
- **Svelte / SvelteKit**: `rules/svelte.md` — `@portabletext/svelte`
- **Vue / Nuxt**: `rules/vue.md` — `@portabletext/vue`
- **Astro**: `rules/astro.md` — `astro-portabletext`
- **HTML (server-side)**: `rules/html.md` — `@portabletext/to-html`
- **Markdown**: `rules/markdown.md` — `@portabletext/markdown`
- **Plain text extraction**: `rules/plain-text.md` — `@portabletext/toolkit`

### Additional Community Serializers

These are listed on [portabletext.org](https://www.portabletext.org/integrations/serializers/) 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` |

## Common Patterns (All Frameworks)

### Custom Types Need Explicit Components

PT renderers only handle standard blocks by default. Custom types (`image`, `code`, `callToAction`, etc.) require explicit component mappings — they won't render otherwise.

### Keep Components Object Stable

In React/Vue, define `components` outside the render function or memoize it. Recreating on every render causes unnecessary re-renders.

### Handle Missing Components Gracefully

All libraries accept `onMissingComponent` to control behavior when encountering unknown types:
- `false` — suppress warnings
- Custom function — log or report

### Querying PT with GROQ

Always expand references inside custom blocks:

```groq
body[]{
  ...,
  _type == "image" => {
    ...,
    asset->
  },
  markDefs[]{
    ...,
    _type == "internalLink" => {
      ...,
      "slug": @.reference->slug.current
    }
  }
}
```

© sanity · MIT · sanity-io/agent-toolkit

Similar skills

Everything for Frontend and UI →

From the same set9

sanity-best-practices Sanity's own rules for an AI agent working on a Sanity codebase: schemas, GROQ, TypeGen, visual editing, migrations and framework wiring. Useful the moment the agent touches your content layer. CX Codex +1 31,800installs sanity-migration Plans and executes content migrations from legacy CMS platforms into Sanity. Guides schema mapping, Portable Text conversion, asset uploads, and cutover checks. CX Codex +1 22,200installs seo-aeo-best-practices A skill with SEO and AEO guidance: metadata, Open Graph, sitemaps, robots.txt, hreflang, JSON-LD and EEAT. Useful while you build pages and want search engines and AI answers to pick them up. CX Codex +1 7,300installs content-modeling-best-practices Guides your agent through content type design in headless CMS: what to reference, what to embed, how to stop duplicating data. CX Codex +1 4,800installs content-experimentation-best-practices This skill guides content A/B test planning, metric selection, and result analysis. Use it when working with CMS-managed variants or frontend experiments. CX Codex +1 3,800installs portable-text-conversion Turns HTML and Markdown into Portable Text blocks for Sanity. Useful when migrating content off a legacy CMS or building an import pipeline. CX Codex +1 2,700installs sanity-studio-upgrade The skill inspects your repo and reports only the breaking changes that actually apply to it. Handy when your Studio sits several majors behind. CX Codex +1 524installs Sanity Connects your agent to a Sanity project: content, datasets, releases and schemas. Useful when CMS edits happen in chat instead of Studio. CX Codex +1 ★ 194repo stars

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor