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://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/strapi.md
Strapi to Sanity
What to Determine First
Before writing migration code, determine:
Strapi version, especially v4 vs v5.
Access level: public REST, authenticated REST, GraphQL, Document Service / Entity Service, repository access, admin token, or database.
Whether drafts, private fields, unpublished entries, and hidden types are in scope.
Whether i18n is enabled and how locales are represented.
Rich text/body format: Strapi blocks, Markdown, HTML, CKEditor, or custom JSON.
Dynamic zones and the complete set of
__componentvalues.Asset provider: local disk, S3, Cloudinary, Strapi Cloud media CDN, or another provider.
Extraction Paths
Access changes extraction completeness more than downstream complexity:
| Access | Best use | Main risk |
|---|---|---|
| Repository | Prove schemas, components, dynamic zones | Does not contain content values |
| Public REST | Fast published-content extraction | Omits drafts/private fields and cannot prove completeness |
| Admin/authenticated API | Drafts, unpublished entries, private fields | Requires approved credentials |
| GraphQL | Typed extraction when enabled | Introspection may be disabled |
| Database | Completeness checks, hidden data | Requires manual joins through relation/morph tables |
REST with populate is often better than raw database extraction because Strapi returns joined relationships and components.
Snapshot all extracted records before transformation. For REST extraction, explicitly request deep population for nested components and dynamic zones.
Schema Discovery Commands
When repository or admin access is available, prove the schema before sampling content:
# Content type schemas
ls src/api/*/content-types/*/schema.json
# Component schemas
ls src/components/*/*.json
# Strapi configuration snapshot
npx strapi configuration:dump --file strapi-config.jsonWith admin API access, inspect the content-type builder:
curl "http://localhost:1337/admin/content-type-builder/content-types" \
-H "Authorization: Bearer <admin-token>"Each Strapi schema file contains info, attributes, and options. Use these to find field types, validations, draft/publish settings, timestamps, components, dynamic zones, media, and relations.
Mapping to Sanity
Strapi collection types become Sanity document types when they are independently managed.
Strapi single types usually become Sanity singleton documents with fixed IDs.
Strapi components become Sanity object types.
Strapi dynamic zones become arrays of named object types, one Sanity
_typeper__component.Strapi relations become Sanity references.
Strapi media objects become Sanity image/file fields and assets.
Strapi v5
documentIdis the best stable source key. Use it for deterministic Sanity IDs and locale grouping.Strapi i18n variants usually map well to document-level localization with
languagefields and translation metadata.
For localized Strapi v5 content, a useful deterministic ID pattern is <type>.<documentId>.<locale>.
Field mapping defaults:
stringmaps tostring.textmaps totext.richtextmaps to Portable Text.number,integer,float, anddecimalmap tonumber.date,datetime, andtimemap to the closest Sanity date/datetime strategy the frontend expects.mediamaps toimage,file, or arrays of those.relationmaps toreferenceor an array of references.componentmaps to a named object type.dynamiczonemaps to an array of union object types.
Transformation Notes
Enumerate
__componentvalues across all records, not a sample.Derive IDs from
documentIdin Strapi v5. For v4, choose a stable fallback such as content type plus numeric ID or slug, and document the risk.Build maps for relationships and media before transforming documents that reference them.
Deduplicate assets by media
hashwhen available.Keep the raw source body temporarily until rich text fidelity is verified.
Use weak references or deterministic IDs during bulk import so order does not create duplicate referenced documents.
Body conversion depends on editor format:
Strapi blocks: map structurally to Portable Text or custom Portable Text objects.
Markdown: use
@portabletext/markdown, with custom handling for GFM tables, fenced code, and inline images.HTML / CKEditor: use
@portabletext/block-toolswithJSDOMor a custom converter if the HTML is inconsistent.
Asset Pipeline
Extract every Strapi media object from fields, dynamic zones, and rich text bodies.
Dedupe by
hashwhen present; otherwise use source URL/path plus filename.Download from the configured provider: local uploads, S3, Cloudinary, Strapi Cloud media CDN, or another adapter.
Upload once to Sanity and store
oldUrl/hash -> asset _id.Rewrite image/file fields and inline body media to Sanity asset references.
Log missing or private media URLs as quality issues, not silent omissions.
Gotchas
Public REST returns published content only. Draft state requires admin/API/database access.
Strapi v5
documentIdis stable across locales and draft/published variants; numeric IDs are less useful for cross-locale identity.populatedepth limits can hide nested dynamic-zone data. Test deeply nested examples.The API can only infer shapes from returned content; repo access proves the full schema.
Components can drift between object and array shapes depending on configuration and historical data.
GraphQL may be disabled or too restricted for migration use.
Some pages may be hardcoded in the frontend rather than Strapi-driven. Audit frontend routes before assuming Strapi contains every page.
If only frontend code is available, inspect API calls,
populateparameters, component usage, custom plugin fields, and expected response shapes.
Validation Checklist
Compare collection, single type, locale, and asset counts with Strapi admin/API/database counts.
Confirm every component and dynamic-zone
__componentvalue is mapped or intentionally skipped.Confirm draft/unpublished scope is explicitly handled.
Spot-check content with deepest nested dynamic zones.
Confirm relationship refs use deterministic Sanity IDs and resolve.
Confirm media deduplication and all migrated assets render in Studio and the frontend.
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 or Sanity.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download sanity-migration.zip