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/general.md
General Sanity Migration Playbook
Core Stance
Replatforming to Sanity is a chance to improve the content model, editorial workflow, and frontend data contract. Do not recreate the source CMS shape unless the project explicitly needs a temporary lift-and-shift phase.
Use a two-phase approach when risk or timeline is high:
Stabilize: migrate into a close, understandable model with stable IDs, complete assets, and working frontend queries.
Improve: remodel page-shaped content into semantic documents, references, page builder sections, or reusable entities after fidelity is proven.
For large migrations, write deterministic migration scripts, review mappings carefully, and identify edge cases before import. The migration must be repeatable and idempotent.
Discovery Questions
Before writing code, establish:
Source access: live API, admin API, export file, database dump, repository access, static HTML, crawler, or manual exports.
Content scope: which content types, locales, drafts, archived content, redirects, menus, authors, taxonomies, assets, and metadata are in scope.
Volume: document counts per type, asset counts, and which types are high-risk because of rich text or nested components.
Rich text formats: HTML, Markdown, WordPress blocks, Strapi blocks, Lexical, Contentful Rich Text, custom JSON, or page-builder structures.
Relationships: authors, taxonomies, products, reusable CTAs, testimonials, media, related content, and cross-locale links.
Destination schema: existing Sanity schema, new schema, temporary lift-and-shift schema, or a planned remodel.
Frontend contract: which routes and queries must keep working, which legacy URLs need redirects, and which pages can be intentionally retired.
Cutover strategy: one-time import, delta sync, content freeze, incremental rollout, or parallel publishing.
Migration Phases
Inventory: enumerate source types, fields, volumes, relationships, assets, URLs, locales, and data quality issues.
Map: decide Sanity document types, object types, references, Portable Text fields, asset fields, localization strategy, and migration metadata.
Extract: pull source data into raw JSON/XML/CSV files and keep them as repeatable snapshots.
Transform: convert raw source records into Sanity-shaped documents with deterministic IDs and explicit quality logging.
Load: import documents and assets using the CLI, migration tooling, or client mutations depending on volume and workflow.
Validate: compare counts, references, Portable Text shape, asset availability, required fields, frontend rendering, redirects, and editor usability.
Cut over: rerun against the latest source content, apply redirects, monitor broken links, and keep legacy source IDs for debugging.
Agent Workflow
For agent-assisted migrations, require one human checkpoint before anything writes to Sanity:
Read the source export/API shape and the target Sanity project.
Print a migration plan: content types, fields, relationships, assets, locales, draft/status handling, and proposed Sanity types.
Flag judgment calls for review, such as page-shaped content, free-text taxonomies, rich text blocks, and localization strategy.
After approval, generate schemas, transform scripts, import scripts, config, and validation scripts.
Stop before running destructive or high-volume writes. The user should review the generated files and choose when to run them.
The useful AI pattern is "analyze, plan, generate, then let the user run," not "silently migrate everything."
Recommended Project Layout
For implementation work, keep migration artifacts out of the application root unless the repo already has a convention:
migration/
extracted/ # raw source snapshots, usually gitignored
transformed/ # generated Sanity-shaped JSON/NDJSON, usually gitignored
reports/ # counts, quality issues, skipped records, validation output
scripts/
extract.ts
transform.ts
validate.ts
import.ndjsonMake generated data reproducible. Commit migration scripts and mapping docs; gitignore large exports, credentials, raw customer data, and generated imports unless the project explicitly wants fixtures.
Modeling Guidance
Model content by meaning, not by legacy storage or frontend layout.
Split generic page/template records into semantic types when the page represents a real entity:
person,location,event,product,caseStudy,article.Consolidate near-duplicates such as
Author,Staff,Presenter, andExpertwhen they represent the same real-world entity.Use references for reusable or independently managed content: authors, people, products, categories, tags, companies, reusable testimonials, shared CTAs.
Use embedded objects for content that only belongs inside one document: SEO metadata, page-specific hero content, one-off sections, migration metadata.
Preserve source IDs and legacy URLs in a
migrationormigrationMetadataobject unless the project already has a standard field shape.Add schema fields before migration code depends on them, then run schema extract and TypeGen if the project uses TypeScript.
Extraction Patterns
Prefer the most structured source available:
Repository access: read schemas, component definitions, and route/query code to prove completeness.
Admin or Local API: best for drafts, hidden fields, private entries, full locale data, and complete relationship expansion.
Public API: good for published content and fast prototypes, but document blind spots.
Export files: reliable snapshots for repeatable transforms; inspect parser output carefully.
Database dumps: complete but usually more work because relationships and media often require manual joins.
Static HTML or crawling: viable fallback when no structured export exists, but plan for lower fidelity and manual review.
Prefer an official bulk export when it exists. It is usually safer than hand-crafting API calls because it is more likely to include drafts, scheduled content, assets, field definitions, and metadata. If required access or credentials are missing, stop and ask for read-only credentials or an export file rather than guessing.
Always snapshot raw extraction results before transformation. This makes the slowest part of migration, transform iteration, offline and repeatable.
Transformation Patterns
Make transforms deterministic:
Derive
_idfrom source IDs, paths, slugs, locale IDs, or stable content hashes.Use stable
_keyvalues for generated arrays when rerun stability matters.Leave empty fields unset. Avoid
null, empty objects, raw parser artifacts, and"[object Object]".Keep raw source body fields temporarily only when they help verify rich text fidelity; hide or remove them after sign-off.
Create lookup maps for every referenced type before transforming documents that reference them.
Track quality issues per source record: missing required data, unresolved references, failed asset lookup, rich text conversion warnings, unsupported blocks, duplicate slugs.
Rich text is usually the long pole. Pick the converter by source format:
Markdown: use
@portabletext/markdown.Markdown that must be constrained to the Sanity schema: use
@portabletext/sanity-bridge.Clean HTML: use
@portabletext/block-toolswithJSDOMand schema-aware custom deserializers.CMS-specific block JSON: map structurally to Portable Text or custom objects.
Messy page-builder HTML: inspect real samples, define custom objects only for patterns that are consistent enough to preserve.
Import Patterns
Choose the write path by migration size and repeatability:
NDJSON +
sanity datasets import: best for large initial loads and export-file migrations. Supports_sanityAssetdirectives and--replace.sanity migration: good for reproducible scripted imports inside a Studio project, dry runs, and batched mutations.sanity execor custom scripts with@sanity/client: good for custom extraction/import loops, incremental syncs, or complex asset upload flows.Sanity MCP/content tools: good for small targeted operations, inspection, and patches. Avoid them for bulk content loads when a script or NDJSON import is more reliable.
Write order:
Assets or asset manifests.
Shared reference documents: authors, people, companies, categories, tags, products.
Primary content documents.
Translation metadata, redirect documents, and post-import relationship fixes.
For relationship-heavy migrations, use a Sanity-safe multi-pass import:
Upload assets and build source asset ID -> Sanity asset ID maps.
Promote reusable string lists, tags, categories, authors, products, or other shared values into reference documents.
Create primary documents with deterministic IDs and scalar fields.
Link references after every target document ID is known, either by emitting deterministic refs in NDJSON or by running a patch pass.
This pattern applies to Sanity because references are just document IDs. It is especially useful when the source stores relationships as nested objects, links, string arrays, or IDs that need lookup tables before they can become Sanity references.
NDJSON and Asset Directives
For bulk imports, generate one valid JSON document per line. Every document needs _type; _id is optional but should be set for rerunnable migrations.
Use _sanityAsset where an asset reference would normally go:
{"_id":"post-123","_type":"post","mainImage":{"_type":"image","_sanityAsset":"image@https://example.com/original.jpg"}}Rules:
Use
image@...for images andfile@...for files.Prefer the largest available original asset. Do not import multiple resized variants of the same source image.
For local files, use absolute
file:///...URIs or package the NDJSON and assets into a.tar,.tar.gz, or.tgz.During CLI import, Sanity temporarily imports references as weak and strengthens them after all documents are present; this is why
_updatedAtcan change for documents with references.Use
--replacefor idempotent reruns,--missingwhen only filling gaps, and--allow-failing-assetsonly when missing assets should be logged for later cleanup.Disable or pause webhooks that would be triggered by high-volume imports.
When using @sanity/client instead of CLI import:
Use low-concurrency queues and batched transactions.
Keep mutation payloads below API limits.
Prefer mutation visibility
deferredfor large imports when immediate queryability is unnecessary.Use
_weak: truein JSON references when creating references before targets exist. In schema definitions the property isweak; in client JSON the property is_weak.
Validation and Cutover
Validate before declaring the migration ready:
Compare source and Sanity counts by content type, locale, and status.
Remember schema validation rules run in Studio, not automatically on API/client mutations. Validate in transform code before import.
Spot-check representative documents from each type, including old, new, long, short, media-heavy, and locale variants.
Confirm Portable Text fields are arrays of blocks, not raw HTML strings.
Confirm all references have non-empty
_refvalues and the targets exist.Confirm images/files are Sanity assets or Media Library assets, with alt text and captions where available.
Run
npx sanity@latest documents validatewhen the destination schema is available.Crawl the legacy site and the new frontend to verify route coverage, redirects, canonical URLs, metadata, and broken links.
For high-value or high-volume pages, run visual regression or screenshot checks against representative legacy and migrated pages.
Keep a migration report with skipped content, known cleanup tasks, and fields that require editorial review.
Sanity Implementation Guardrails
Schema validation rules run in Studio and document validation commands, not automatically on API/client writes. Validate transformed documents before import.
Use
defineType,defineField, anddefineArrayMemberwhen creating schema files.Prefer image fields with
hotspot: trueand analtfield when editorial images need cropping or accessibility metadata.Use
defineQueryfor GROQ queries when the project uses TypeGen.Run schema extraction and TypeGen after schema/query changes when TypeScript types are part of the project.
Deploy or apply schema changes before using MCP/content tools against the target dataset.
Keep migration metadata small and useful: source system, source ID, source type, legacy URL, migrated timestamp, and quality flags.
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