Skip to content

feat: add Portable Text serialization and conversion skills#16

Open
kmelve wants to merge 4 commits intomainfrom
feat/portable-text-skills
Open

feat: add Portable Text serialization and conversion skills#16
kmelve wants to merge 4 commits intomainfrom
feat/portable-text-skills

Conversation

@kmelve
Copy link
Copy Markdown
Member

@kmelve kmelve commented Feb 12, 2026

Summary

Two new skills covering Portable Text workflows outside the editor — serialization to all supported frameworks and conversion from external formats.

portable-text-serialization

Render PT content across frameworks. Each rule covers typed components, custom types/marks/blocks, performance patterns, and framework-specific idioms.

Rule Framework Package
rules/react.md React / Next.js @portabletext/react
rules/svelte.md Svelte 5 / SvelteKit @portabletext/svelte
rules/vue.md Vue 3 / Nuxt @portabletext/vue
rules/html.md Server-side HTML @portabletext/to-html
rules/plain-text.md Plain text extraction toPlainText()

SKILL.md includes the PT structure quick reference and the universal component mapping pattern.

portable-text-conversion

Convert external content into PT blocks.

Rule Source Approach
rules/html-to-pt.md HTML htmlToBlocks + JSDOM + custom deserializers
rules/markdown-to-pt.md Markdown MD→HTML→PT or MD→AST→PT
rules/manual-construction.md Any Build blocks programmatically

SKILL.md includes the full PT spec as a reference for manual construction.

Gap addressed

The existing toolkit had only pte-custom-components.md (React-only rendering) and migration-html-import.md (basic HTML import). These new skills cover the full ecosystem: Svelte, Vue, server-side HTML, plain text, Markdown→PT, and manual block construction.

Validation

$ npm run validate Valid skill: ./skills/sanity-best-practices Valid skill: ./skills/content-modeling-best-practices Valid skill: ./skills/seo-aeo-best-practices Valid skill: ./skills/content-experimentation-best-practices Valid skill: ./skills/portable-text-serialization Valid skill: ./skills/portable-text-conversion 
Add two new skills covering Portable Text workflows outside the editor: ## portable-text-serialization Render PT content across frameworks: - React / Next.js (@portabletext/react) - Svelte / SvelteKit (@portabletext/svelte) - Vue / Nuxt (@portabletext/vue) - HTML strings (@portabletext/to-html) - Plain text extraction (toPlainText) Each rule covers typed components, custom types/marks/blocks, performance patterns, and framework-specific idioms. ## portable-text-conversion Convert external content into PT blocks: - HTML → PT via @portabletext/block-tools (htmlToBlocks) - Markdown → PT via HTML intermediate or AST-based approach - Manual PT block construction for any data source Includes custom deserializer patterns, pre-processing, image upload during migration, and a validation checklist. Both skills pass `npm run validate` via skills-ref.
Major corrections based on thorough research of portabletext.org, the portabletext/editor monorepo, and all @portabletext/* packages: ## Key changes ### @portabletext/markdown — NEW - Added rules/markdown.md to serialization skill (PT → Markdown) - Rewrote rules/markdown-to-pt.md to use markdownToPortableText() as the primary approach instead of the MD→HTML→PT workaround - portableTextToMarkdown() and markdownToPortableText() are both covered, with custom renderer/matcher examples ### Astro support — NEW - Added rules/astro.md covering astro-portabletext (officially recommended by Sanity for Astro projects) ### Legacy package corrections - Clarified @sanity/block-tools is legacy, @portabletext/block-tools is current (same API) - Fixed Schema import in html-to-pt.md - Added deprecation notes throughout ### Ecosystem completeness - Added community serializer table (React Native, React PDF, Solid, Qwik, Liquid, PHP, Python, C#, Dart/Flutter) - Referenced portabletext.org and portabletext/editor monorepo - Listed editor plugins ecosystem All 6 skills pass npm run validate.
Markdown-to-PT (markdown-to-pt.md): - Fix matchers API: block/marks/types are top-level options, not nested under 'matchers' - Add @portabletext/sanity-bridge for using Sanity Studio schemas - Show matcher context props (context.schema, context.keyGenerator) PT-to-Markdown (serialization/markdown.md): - Add built-in DefaultCodeBlockRenderer, DefaultImageRenderer, etc. - Fix blockSpacing: takes a function ({current, next}), not a string - Document renderer prop signatures (value, children, index, isInline, etc.) sanity-migration.mdc: - Replace deprecated @sanity/block-content-to-markdown with @portabletext/markdown - Add markdownToPortableText example for direct conversion - Note @sanity/block-tools deprecation in favor of @portabletext/block-tools
- html-to-pt: Add inline comment showing how JSDOM connects to parseHtml option - html-to-pt: Type client parameter as SanityClient with import - manual-construction: Replace broad sanity.io/docs link with specific @sanity/util repo link - html serialization: Explain why htm/vhtml is safer (auto-escapes attributes, prevents XSS)
@kmelve
Copy link
Copy Markdown
Member Author

kmelve commented Feb 12, 2026

Thanks for the review, @jonahsnider!

Pushed fixes for (almost) all your comments:

  • JSDOM: added inline comment connecting the import to the parseHtml option
  • Client typed as SanityClient
  • Replaced the broad docs link with a specific @sanity/util link
  • Explained the auto-escaping benefit of htm/vhtml

Re: the package.json validation pattern — agreed, but out of scope for this one.

Re: static site generators — yeah, we have folks using Sanity with 11ty, Hugo etc. so that's a real use case.

Copy link
Copy Markdown
Member

@jonahsnider jonahsnider left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the quick turnaround on feedback - lgtm!


## Custom Matchers

Matchers are top-level options (not nested under a `matchers` key). Each receives `{context, value}` where `context.schema` lets you validate against the schema:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a skills expert so bear with me, but it sticks out to me that a lot of this is documentation that could go stale. What happens if we release breaking changes to some of these libraries? Would it be better and more future proof to briefly describe what libraries exist and what high level purpose they serve?

Copy link
Copy Markdown
Member

@christianhg christianhg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After thinking about #16 (comment) some more, I think the right move is to simply link to the respective repository READMEs. That way, the info doesn't go stale as the libraries progress.

Copy link
Copy Markdown
Member

@christianhg christianhg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll have to keep these up-to-date

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants