How do you migrate content from one headless CMS to another?
You migrate content between headless CMSs in six stages: design the new content model, map old fields to new ones, export everything with scripts, transform rich text and references, import in dependency order, and validate before switching the front end. The method is the same whether you're moving from Contentful to Sanity, Sanity to Payload or Strapi to anything else.
Headless migrations are easier than website migrations in one way: your front end and URLs don't have to change. They're harder in another: every CMS stores rich text, references, locales and assets differently, and that's where content gets lost. This guide is the general method. The pair-specific guides linked below cover the quirks of each move, starting with Contentful to Sanity and Contentful to Payload.
Stage | Output | Who owns it |
|---|---|---|
Content model | New schema, reviewed by editors | Developer and content lead |
Field mapping | A sheet mapping every old field to a new one | Developer |
Export | Raw JSON or NDJSON of entries and assets | Script |
Transform | Converted rich text, references and locales | Script, reviewed by a developer |
Import | Content in the new CMS, in dependency order | Script |
Validate and cut over | Counts, diffs, editor sign-off, front end switched | Developer and editors |
Why do teams switch headless CMS?
Teams switch headless CMS for four reasons: pricing jumps, editing experience, the wish to self-host, and developer experience. Pricing is the most common trigger, and it usually arrives as a tier boundary rather than a gradual increase.
Our research found that Contentful's tier jumps, such as the 25-content-type ceiling on a smaller space tier noted in Pagepro's migration write-up, push teams toward Sanity or Payload. Locales and seats create similar step changes in other platforms. Editing experience is the second trigger: editors who want a customizable studio or visual editing. The third is control: teams that want content in their own database, which is why self-hosted options like Payload and Strapi come up. For a side-by-side view, read Contentful vs Sanity vs Payload vs Strapi.
- Pricing tier boundaries: content types, locales, seats or API usage crossing a plan limit.
- Editing experience: editors need a better studio, previews or a visual editor.
- Ownership and hosting: content should live in your database, on your infrastructure.
- Developer experience: a typed, code-defined schema that lives with the front end.
What is a content-model-first CMS migration?
A content-model-first migration designs the new schema before moving any content, instead of recreating the old schema field for field. It's the single decision that most improves the result, because every CMS accumulates content types nobody uses, duplicated fields and workarounds for the old platform's limits.
Start by listing every content type with its entry count and last edit date. Merge types that differ only by a flag, delete types with no entries, and replace workaround patterns with the new CMS's native features, such as Payload's blocks field or Sanity's Portable Text custom blocks. Then review the draft schema with the people who edit content every week. Our guide to content modeling for CMS migrations covers this in depth, and content modeling 101 explains the basics for a SaaS site.
Migrate the content you need into the model you want, not the model you had.
How do you map content types between Contentful, Sanity, Payload and Strapi?
You map content types by matching each platform's equivalent concept, then writing down every field-level difference. The concepts line up closely; the details, especially rich text and localization, don't.
Concept | Contentful | Sanity | Payload | Strapi |
|---|---|---|---|---|
Content type | Content type | Document type | Collection | Collection type |
Singleton | Single entry by convention | Singleton document | Global | Single type |
Rich text | Rich text JSON document | Portable Text | Lexical JSON | Blocks or Markdown |
Flexible sections | References to section entries | Array of objects | Blocks field | Dynamic zone |
References | Link fields | Reference fields | Relationship fields | Relations |
Localization | Per-field locales | Field or document level, by plugin | Per-field, localized flag | Per-entry locales via i18n |
Assets | Assets with CDN URLs | Image and file assets | Upload collections | Media library |
Build the field mapping as a spreadsheet with one row per old field: old type, old field, new type, new field, transform needed and notes. It becomes the spec for your scripts and the checklist for validation. Check each platform's current docs for field types before you finalize it, since CMSs add field types between versions.
How do you export, transform and import headless CMS content?
You export with the old CMS's tooling or API, transform with a script you own, and import with the new CMS's API or import tool. Keep the three steps separate, so you can re-run the transform without hitting either API again.
Export everything
Use the platform's export tool or API to pull entries, assets, locales and drafts into JSON files. Contentful has a CLI space export; Sanity exports datasets as NDJSON.
Freeze or log changes
Agree a content freeze for the cutover window, or log every edit made after export so you can replay it.
Transform fields
Map plain fields directly. Convert rich text to the new format, resolve references to new IDs and reshape locales.
Download and re-upload assets
Download every image and file, upload them to the new CMS, and record a map of old asset IDs to new ones.
Import in dependency order
Import assets first, then entries that others reference (authors, categories), then the entries that point at them.
Make the script repeatable
Use stable IDs so a second run updates rather than duplicates. You'll run it several times before cutover.
For the general mechanics of exporting, cleaning and importing between any platforms, including non-headless ones, see CMS content migration. Coming from Webflow, start with exporting Webflow CMS content.
How do you migrate rich text and references between CMSs?
You migrate rich text by converting the old format into the new one node by node, and references by importing referenced entries first and rewriting IDs from a lookup table. These two steps take most of the engineering time in any headless CMS migration.
Each CMS stores rich text as structured data with its own vocabulary: Contentful's rich text is a JSON tree of nodes, Sanity uses Portable Text blocks with marks and annotations, and Payload's default editor stores Lexical JSON. Headings, paragraphs, lists, links and bold text map cleanly. Embedded entries, inline references and custom components need a decision for each one.
How to handle embedded entries inside rich text
Embedded entries, such as a code sample, a call-to-action box or a product card dropped into an article, are the usual casualties.
First, list every embedded entry type in use by scanning the exported rich text for embed nodes. Second, define a matching custom block in the new CMS: a Portable Text object type in Sanity, a Lexical block in Payload, or a component in Strapi. Third, have the transform script convert each embed node into that block, carrying its reference through the ID lookup table.
Then render a sample of articles with each embed type and compare them against the old site. If an embed type appears in only a handful of articles, it can be cheaper to rewrite those by hand.
References work best with a two-pass import. In the first pass, create every entry with its plain fields and record old ID to new ID. In the second pass, patch in the references using the lookup. This avoids failures when two entries point at each other. For WordPress sources, where content is HTML, WordPress to Sanity or Payload covers HTML conversion.
How do you validate a headless CMS migration?
You validate a headless CMS migration with automated checks first and human review second: entry counts per type and locale, reference integrity, slug parity and rendered-page comparisons. Clicking through a few pages finds the obvious problems and misses the ones that matter.
- Counts: entries per type and per locale match the export, minus anything you chose to drop.
- References: no reference points at a missing entry, and no entry lost its references.
- Slugs: every published slug exists in the new CMS, so URLs on the front end don't change.
- Assets: every image in fields and in rich text resolves to the new CMS's storage.
- Rendered diffs: render a sample of pages from old and new content and compare them visually.
- Editor sign-off: editors check their own content types and the editing workflow, including previews.
Previews deserve their own test, because they're often what editors care about most. If the new setup includes visual editing, check it before cutover with our guide to live preview in headless CMS setups.
Which headless CMS migration are you planning?
Pick your pair below. Each guide covers that move's specific triggers, tooling and data conversion, on top of the general method in this hub.
Moving from | Moving to | Read |
|---|---|---|
Contentful | Sanity | |
Contentful | Payload | |
Sanity | Payload | |
Strapi | Payload | |
Prismic or Storyblok | Sanity | |
WordPress | Sanity or Payload | |
Webflow CMS | A headless CMS | |
Framer CMS | A headless CMS, keeping Framer | |
Not sure yet | Comparing options |
If you're new to headless architecture altogether, read headless CMS explained for non-developers first. Commerce teams moving product content should see migrating a Shopify theme store to headless.
How long does a headless CMS migration take, and what does it cost?
A headless CMS migration's length and cost scale with content types, rich text complexity and locales, so a small, clean dataset is a short project and a large multilingual one is a long one. Our research found agencies quoting $1.5k to $25k for website migrations by size, and £13k to £30k over 6 to 12 weeks for a WordPress to headless Next.js move, which includes a front-end rebuild.
- Typical website migration quotes by size (our research)
- $1.5k–$25k
- Quoted for WordPress to headless Next.js (our research)
- 6–12 weeks
- Designify retainer: ongoing design and development team
- $6,000/mo
A CMS-to-CMS move with no front-end change sits at the lower end, because the work is the content model, scripts and validation. The drivers are content types, rich text complexity, embedded entries, locales and asset volume. A front-end rebuild at the same time is a much larger project, so decide whether you're changing one layer or two before you ask for quotes. For commerce stacks, see headless Shopify cost.
What mistakes break headless CMS migrations?
The mistakes that break headless CMS migrations are copying the old model, ignoring rich text embeds, hot-linking assets, skipping drafts and locales, and cutting over without editor sign-off. Each one is cheap to prevent and expensive to fix after launch.
- Copying the old schema. You keep every workaround the old platform forced on you.
- Ignoring embedded entries. Articles render with gaps where components used to be.
- Hot-linking assets. Images break the day the old plan is cancelled.
- Forgetting drafts and scheduled content. Editors lose unpublished work.
- Flattening locales. Translations collapse into the default language or go missing.
- No content freeze. Edits made during the migration window disappear at cutover.
Keep the old CMS running read-only until the front end has run on the new one for a few weeks and editors have confirmed nothing is missing. It's the cheapest insurance in the project.
