Sanity Migration
SanityOfficialPublished by Sanity
- Skill
- Tool integration
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a systematic approach to migrating content from various CMS platforms into Sanity. It treats migration as an ETL (Extract, Transform, Load) project, emphasizing deterministic scripts, content modeling, and validation over manual operations. The skill includes specific guidance for platforms such as AEM, Contentful, Strapi, Webflow, WordPress, Payload, Drupal, and Markdown-based systems, ensuring that content is properly mapped, transformed into Portable Text, and validated before final cutover.
What it does
- Provides a structured workflow for planning and executing CMS-to-Sanity migrations.
- Offers platform-specific guidance for extracting data from common CMS environments.
- Defines best practices for mapping source content to Sanity schemas and Portable Text.
- Requires the creation of migration plans, content inventories, and validation reports.
- Encourages the use of deterministic scripts for repeatable and reliable data imports.
- Includes procedures for handling assets, redirects, and final cutover synchronization.
When to use it
- When replatforming a website or application to use Sanity as the content backend.
- When moving content from legacy systems like AEM, Drupal, or WordPress.
- When migrating static files like Markdown or MDX into a structured CMS.
- When you need to perform a complex data transformation and cleanup during an import.
- When you need to ensure data integrity through validation checks and delta syncs.
Advantages
- Reduces risk by mandating planning and validation steps before execution.
- Promotes repeatable migrations through script-based ETL patterns.
- Ensures content is modeled for Sanity's capabilities rather than just copied.
- Provides clear paths for handling complex data like rich text and assets.
- Supports iterative development through deterministic ID management.
Usage examples
- “I need to migrate my site from Contentful to Sanity. Where do I start?”
- The agent will guide you through the Contentful-specific extraction process, help you plan your content mapping, and suggest using the contentful-to-sanity toolkit for an initial pass.
- “How should I handle AEM content fragments during a migration?”
- The agent will provide instructions on using AEM GraphQL to extract fragments and mapping them to Sanity document types or page builder blocks.
- “Create a migration plan for my WordPress site.”
- The agent will outline the steps to export WXR data, map WordPress posts to Sanity schemas, convert HTML to Portable Text, and set up redirects.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/sanity-io/sanity-migration/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip sanity-migration.zip -d .agents/skills
Documentation by Sanity
Sanity Migration
Use this skill for CMS-to-Sanity migration work. Treat migration as a content strategy and ETL project, not a blind lift-and-shift.
Required Workflow
Read
references/general.mdfirst.If the source platform is known, also read its guide:
AEM / Adobe Experience Manager:
references/aem.mdContentful:
references/contentful.mdStrapi:
references/strapi.mdWebflow:
references/webflow.mdWordPress / WXR / Elementor:
references/wordpress.mdPayload:
references/payload.mdDrupal:
references/drupal.mdMarkdown / MDX / frontmatter files:
references/markdown.md
Before writing code, produce a short migration plan covering source access, content scope, schema decisions, extraction, transformation, import, validation, redirects, and cutover.
Prefer deterministic, repeatable scripts for real migrations. Write and review migration scripts, mappings, and validation checks; do not rely on one-off content operations for large content volumes.
Deliverables to Produce
For implementation or planning tasks, produce these artifacts or explain why they are not needed:
Content inventory: source types, counts, locales, status/draft scope, assets, and relationship types.
Source-to-Sanity mapping: document types, object types, references, Portable Text fields, asset fields, IDs, and skipped content.
Extraction approach: credentials/access needed, API/export commands, raw snapshot location, and known blind spots.
Transform/import plan: deterministic IDs, write order, asset handling, rich text conversion, validation, and rerun strategy.
Cutover plan: delta sync/content freeze, redirects, broken-link checks, SEO metadata, and manual cleanup.
Defaults
Use stable document IDs derived from source IDs, slugs, paths, or hashes.
Use
createOrReplace,createIfNotExists, orsanity datasets import --replaceso reruns converge.Snapshot extracted source data to disk before transforming it.
Import or create referenced documents before documents that reference them.
Convert rich text to Portable Text instead of storing raw HTML or Markdown strings.
Upload assets to Sanity or the Media Library; do not leave production content dependent on legacy CDN URLs.
Track per-document quality issues and produce a validation summary before cutover.
Preserve legacy URLs and source IDs for redirects, QA, and future debugging.
Sanity Guardrails
Model what content is, not how the old site rendered it.
Use documents for reusable or independently managed entities; use objects for content owned by one document.
Use
defineType,defineField, anddefineArrayMemberif authoring Sanity schemas.Use image/file fields with uploaded Sanity assets or Media Library assets, not legacy CDN URLs.
Use Portable Text arrays for rich text and custom blocks; do not store raw HTML as the canonical body.
Run schema extraction and TypeGen after schema or GROQ query changes when the project uses TypeScript.
Deploy or apply schema changes before using MCP/content tools against the target dataset.
For deeper Sanity implementation guidance, use sanity-best-practices if it is already available. If it is not installed, tell the user they can add it with:
npx skills add sanity-io/agent-toolkit --skill sanity-best-practicesStop and Ask
Stop before coding when any of these are unclear:
Source access path, credentials, export file, or database connection.
Target Sanity project/dataset or whether a scratch dataset should be used.
Draft, archived, scheduled, locale, or version history scope.
Whether media files should be migrated and whether asset URLs/files are accessible.
Whether the destination schema exists or should be designed as part of the migration.
Do Not Do This
Do not create random IDs for source-backed documents.
Do not fetch-then-create referenced documents; use deterministic IDs and
createIfNotExists/createOrReplace.Do not run bulk migrations through MCP content tools when NDJSON or scripts are appropriate.
Do not flatten locale fallback values into translations unless requested.
Do not leave TODOs for required media, authors, references, or rich text conversion.
Do not declare a migration done without count checks, sample checks, reference checks, and route/redirect checks.
Reference Map
Use references/general.md for shared migration principles and the platform references for source-specific extraction routes, modeling traps, and validation checks.
For source systems not explicitly covered, apply references/general.md and adapt the closest platform pattern:
API-first CMSes: start from Contentful, Strapi, or Payload.
Monolithic/page-builder systems: start from WordPress, Drupal, Webflow, or AEM.
HTML-heavy exports: start from the WordPress and Webflow rich-text guidance.
Markdown-first sources: start from
references/markdown.md.
Files
- ATTRIBUTION.mdAdded by AutoGineer863 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md5.3 KiB
- references/aem.md6.8 KiB
- references/contentful.md9.5 KiB
- references/drupal.md4.4 KiB
- references/general.md12.6 KiB
- references/markdown.md5.1 KiB
- references/payload.md7.5 KiB
- references/strapi.md6.3 KiB
- references/webflow.md5.7 KiB
- references/wordpress.md12.5 KiB
references/payload.md
Payload to Sanity
What to Determine First
Before writing migration code, determine:
Payload version and whether the app is Payload standalone or integrated with Next.js.
Whether repository access is available. Payload config is the best source of truth for collections, globals, fields, uploads, localization, access control, drafts, versions, and hooks.
Access level: Local API, REST API, GraphQL API, database dump, or the official import/export plugin.
Which collections, globals, upload collections, drafts, versions, localized fields, and hidden fields are in scope.
Which rich text editor is used, such as Lexical or a legacy editor, and whether custom blocks are present.
Which database and upload storage adapter are used.
Extraction Paths
Prefer source paths in this order:
Repository + Local API: best path when available. Import Payload with
getPayload({ config }), then usepayload.find,payload.findGlobal, and version APIs from a script running in the project context.Import/export plugin: official
@payloadcms/plugin-import-exportcan export collection data as JSON or CSV. Prefer JSON for migration because it preserves nested objects, arrays, rich text structures, relationship references, and native field shapes.REST API: every collection and global has generated REST endpoints under the API route prefix, usually
/api. Usedepth,locale,fallback-locale,select,populate,limit,page,sort, andwhere.GraphQL API: useful if enabled and if the schema exposes all needed fields.
Database dump: complete but usually lower-level than Payload's APIs. Use when the app cannot run or when API access is insufficient.
The Local API has server-only options that are useful for migration: depth, locale, fallbackLocale, select, populate, overrideAccess, showHiddenFields, and pagination: false.
Important: Local API operations skip access control by default. Do not export auth collections, user secrets, password hashes, tokens, or hidden operational fields unless the migration explicitly requires them and the user has approved the scope.
Import/Export Plugin Details
The official @payloadcms/plugin-import-export can export collection data as CSV or JSON. Use JSON for Sanity migrations unless there is a strong reason to flatten to CSV.
Export routes/options to know:
Direct download streams a file from
POST /api/exports/download.Saved exports create an upload document in the
exportscollection.Local API can create an export document, for example with collection slug and
format: "json".Jobs Queue can run
createCollectionExport; ensure a job runner exists or set synchronous behavior for small/test exports.Export parameters include
localeanddrafts;locale: "all"is useful for multi-locale export, anddrafts: trueincludes draft versions for collections with drafts enabled.
JSON export preserves nested structures, rich text, relationship references, and native field shapes. CSV export flattens nested fields with underscore notation and coerces values, so it is worse for rich text and relationship-heavy migrations.
Local API Examples
Initialize Payload in a script:
import {getPayload} from 'payload'
import config from '@payload-config'
const payload = await getPayload({config})Extract a collection:
const result = await payload.find({
collection: 'pages',
depth: 0,
page: 1,
limit: 100,
locale: 'en',
fallbackLocale: false,
showHiddenFields: true,
})Extract a global:
const header = await payload.findGlobal({
slug: 'header',
depth: 0,
locale: 'en',
fallbackLocale: false,
showHiddenFields: true,
})Use depth: 0 when building deterministic Sanity references from relationship fields; populated relationship objects are useful for inspection but can hide the source ID shape and create circular/large payloads.
For draft-enabled collections, draft: true returns the latest version, which may be a draft. It is not "drafts only." Decide whether to migrate that latest draft as drafts.<id>, published content as <id>, or both.
Mapping to Sanity
Payload collections usually become Sanity document types.
Payload globals usually become Sanity singleton documents with fixed IDs.
Payload blocks map to Sanity named object types, page builder objects, or Portable Text custom objects.
Payload relationships map to Sanity references.
Payload uploads map to Sanity image/file fields and assets.
Payload localized fields can map to Sanity field-level localization or document-level localization depending on the target workflow.
Payload drafts and versions require an explicit decision: migrate only published content, migrate draft variants as Sanity drafts, or archive version history outside Sanity.
Use Payload config files to distinguish actual content architecture from incidental database shape.
Transformation Notes
Use collection slug and source document ID for deterministic Sanity IDs, such as
post-<payloadId>or<collectionSlug>-<payloadId>.For localized document-level migrations, prefer IDs such as
payload.<collection>.<id>.<locale>or the project's existing translation ID convention.Build maps for relationships and uploads before transforming documents that reference them.
Export one locale at a time or export all locales and normalize into the chosen Sanity localization model.
For globals, assign fixed IDs such as
global-header,settings, or the existing project convention.For uploads, extract original file URL/path, filename, mime type, alt text, dimensions, and any focal/crop metadata if present.
For rich text, map the editor's JSON tree structurally. Do not stringify Lexical or block JSON into Sanity fields.
If using the import/export plugin, configure export format as JSON and include locale/draft options when needed.
Gotchas
REST and GraphQL responses are governed by access control and selected fields. Local API with
overrideAccessandshowHiddenFieldsis better for complete migration snapshots.Hooks can mutate values during API reads or writes. Decide whether to run through Payload APIs for normalized behavior or read raw database rows for historical fidelity.
Uploads may be stored locally, in S3, Cloudinary, or another adapter. API documents may not contain directly fetchable public URLs.
depthcontrols relationship/upload population. Too shallow loses data; too deep can make huge payloads or circular shapes.Draft and version data are separate concerns. The import/export plugin can include drafts for draft-enabled collections; version history may need separate extraction through version APIs if it is in scope.
Localized fields can be exported as a single locale or all locales. Do not accidentally collapse fallback values into translations.
JSON export preserves nested structures; CSV flattens and coerces values and is less suitable for rich migration transforms.
Validation Checklist
Compare collection, global, upload, locale, draft, and version counts against Payload admin/API/database.
Confirm every collection and global in the Payload config is mapped, skipped, or intentionally consolidated.
Confirm relationships resolve to deterministic Sanity IDs.
Confirm upload URLs or files are accessible to the Sanity import/upload process.
Spot-check rich text with custom blocks, links, embeds, and nested structures.
Confirm localized content preserves missing translations and fallback behavior according to the migration plan.
Confirm singleton/global documents have stable IDs and frontend queries can retrieve them.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Node.js
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Sanity
- Publisher
- Sanity (Official)
- Source
- sanity-io/agent-toolkit · skills/sanity-migration · commit 3ed3a14
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Sanity.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download sanity-migration.zip