All skills
sanity-io avatar

/sanity-migration

@fc0e709 official
by Sanitysanity-io/agent-toolkit187 stars
30

Plans, implements, and reviews migrations from other CMSes and content systems into Sanity. Use when migrating or replatforming to Sanity from AEM, Adobe Experience Manager, Contentful, Strapi, Webflow, WordPress, Payload, Drupal, Markdown/MDX/frontmatter files, WXR/XML exports, CMS APIs, database dumps, static HTML, or when designing extraction, transformation, Portable Text conversion, asset migration, redirects, validation, and cutover workflows.

Use this Skill: https://skilld.dev/gh/sanity-io/agent-toolkit/sanity-migration

This session only. Nothing lands on disk.

referencesmarkdown.md

≈1.3k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Markdown to Sanity

What to Determine First

Before writing migration code, determine:

  • Source shape: Markdown files, MDX, GitHub README-style content, static-site content, AI-generated Markdown, or Markdown exported from a CMS.
  • File count, directory structure, and whether paths encode slugs, dates, sections, or locales.
  • Frontmatter fields and formats.
  • Markdown extensions: GFM tables, callouts, directives, footnotes, raw HTML, code fences, embeds, local images, and MDX JSX.
  • Target Sanity document type and Portable Text field name.
  • Allowed Portable Text styles, lists, decorators, annotations, and custom object types.
  • Whether tags, authors, categories, and images are references, inline fields, or separate documents.

Default Conversion Path

Use direct Markdown -> Portable Text conversion with @portabletext/markdown.

npm install @portabletext/markdown
import {markdownToPortableText} from '@portabletext/markdown'

const body = markdownToPortableText(markdown)

Do not call Markdown -> HTML -> htmlToBlocks "direct conversion." That is an HTML fallback path. Use it only when the Markdown pipeline cannot handle source-specific syntax that is easier to normalize as HTML.

Schema-Constrained Conversion

For a Sanity block array schema, convert it with @portabletext/sanity-bridge:

npm install @portabletext/markdown @portabletext/sanity-bridge
import {markdownToPortableText} from '@portabletext/markdown'
import {sanitySchemaToPortableTextSchema} from '@portabletext/sanity-bridge'

const schema = sanitySchemaToPortableTextSchema(sanityBlockArraySchema)
const body = markdownToPortableText(markdown, {schema})

Use custom matchers when Markdown syntax must become specific Sanity objects:

const body = markdownToPortableText(markdown, {
  schema,
  types: {
    table: ({context, value}) => ({
      _type: 'table',
      _key: context.keyGenerator(),
      rows: value.rows,
      headerRows: value.headerRows,
    }),
  },
})

Only emit custom types that exist in the target schema.

Frontmatter Mapping

Use a frontmatter parser such as gray-matter:

npm install gray-matter

Map common fields deliberately:

  • title -> document title.
  • slug or file path -> slug.current.
  • date, published, updated -> explicit datetime fields such as publishedAt.
  • author -> reference, string, or author document depending on target schema.
  • tags and categories -> reference documents when they power filtering/navigation.
  • description, canonical, ogImage -> SEO object if the target schema has one.
  • Source file path -> migration metadata for debugging.

Do not put frontmatter into the Portable Text body unless it is actual editorial content.

Asset Handling

Markdown images need explicit handling:

  • Remote image: use _sanityAsset: "image@https://..." in NDJSON or upload through the client.
  • Local image: use an absolute file:///... URI in NDJSON, package assets into a tarball, or upload through the client.
  • Prefer the largest original image, not resized derivatives.
  • Preserve alt text from ![alt](src) and title text when present.
  • Rewrite image paths before conversion if Markdown uses relative paths.
  • Log unresolved images; do not silently keep broken local paths.

If images should remain inline inside Portable Text, ensure the Portable Text schema allows an image object. If images belong in a separate field such as mainImage, extract them before conversion.

Document IDs and Import

Use stable IDs:

  • File path slug: post-my-folder-my-file.
  • Frontmatter ID: post-<sourceId>.
  • Hash only when no stable source ID/path exists.

For bulk imports, prefer NDJSON:

{"_id":"post-example","_type":"post","title":"Example","body":[{"_type":"block","_key":"a","style":"normal","children":[{"_type":"span","_key":"b","text":"Hello","marks":[]}],"markDefs":[]}]}

Then import with:

npx sanity datasets import import.ndjson <dataset> --replace

Use client createOrReplace for smaller imports or incremental syncs.

Keep Markdown Native

Only keep Markdown native when the project intentionally wants Markdown editing/rendering instead of Portable Text querying and structured blocks.

If keeping Markdown native, use a Markdown field/plugin and document the tradeoff: simpler migration and editing for Markdown-first teams, but weaker structured querying, references, annotations, and block-level content reuse.

Validation Checklist

  • Confirm every document has _id, _type, title/slug where required, and a Portable Text array.
  • Confirm no body field is a raw Markdown string unless the target intentionally stores native Markdown.
  • Confirm all Portable Text custom _type values exist in the schema.
  • Confirm all image references resolve to remote URLs, absolute local file URLs, or uploaded Sanity asset IDs.
  • Confirm frontmatter dates parse to valid ISO datetime strings.
  • Confirm tags/authors/categories resolve according to the target model.
  • Confirm rerunning the transform produces the same IDs.
  • Confirm MDX JSX is mapped to Sanity objects, stripped, or logged for manual cleanup.

Source: SKILL.md on GitHub

No alerts2mo3 checks · Risk SAFE
  • Gen Agent Trust Hub2mo

    This skill provides a robust framework for migrating content from various CMS platforms to Sanity. It emphasizes security best practices by requiring user confirmation for credential access and advocating for a human-in-the-loop workflow where generated scripts are reviewed by the user before execution.

  • Socket2mo

    No alerts

  • Snyk2mo

    Risk: LOW · No issues

Signed by skilld at fc0e709. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 weeks ago.

Activeupdated 2 months ago
  • sanity
  • cms
  • migration
  • etl
  • contentful
  • wordpress
  • strapi
  • payload
  • drupal
  • portable-text

README badge

README badge for sanity-io/agent-toolkit/sanity-migration

Plans and implements CMS-to-Sanity migrations from AEM, Contentful, Strapi, Webflow, WordPress, Payload, Drupal, Markdown, and database exports. Covers content inventory, schema mapping, extraction, Portable Text conversion, asset migration, validation, and cutover workflows with deterministic IDs and repeatable scripts.

Generated from the current SKILL.md.

Does this skill handle migrations from WordPress, Contentful, Strapi, and other specific CMSes?
Yes. The skill includes platform-specific guides for AEM, Contentful, Strapi, Webflow, WordPress, Payload, Drupal, and Markdown/MDX sources. For other systems, it provides a general migration framework to adapt.
What does this skill actually produce—code, a plan, or both?
The skill guides you to produce a migration plan first (content inventory, source-to-Sanity mapping, extraction approach, transform/import plan, cutover plan), then helps you write deterministic, repeatable migration scripts and validation checks.
Does this handle asset migration and rich text conversion?
Yes. The skill requires converting rich text to Portable Text, uploading assets to Sanity instead of relying on legacy CDN URLs, and producing validation checks before cutover.
Can I use this for one-off content operations or bulk migrations through MCP tools?
No. The skill explicitly prefers deterministic, repeatable scripts over one-off operations and discourages running bulk migrations through MCP content tools when NDJSON or scripts are appropriate.

Generated from the current SKILL.md. These answers refresh after source changes.