Headless CMS · Migration
Contentful to Sanity Migration: When and How
By George Shvaya · Updated July 2026 · 9 min read
A Contentful to Sanity migration exports content types, entries, and assets through Contentful's APIs, remodels them as Sanity schemas defined in code, transforms rich text into Portable Text, imports the result via Sanity's import tooling, and verifies parity against a documented baseline — usually motivated by editorial customization needs rather than platform failure. That last clause matters more than any technical detail below, so this guide starts with whether the move is justified at all.
First: Should You Migrate at All?
There are legitimate reasons to move. The strongest is editorial customization: Sanity Studio is a React application you own and extend, so if your editors need custom input components, workflow-specific views, or an authoring interface embedded directly inside your own product, that is something you build rather than configure. Close behind is schemas-as-code — content model changes arrive as pull requests, get reviewed, and move through environments the way the rest of your codebase does. Cost model fit is a real reason too, particularly for teams that need many editor seats or whose usage sits awkwardly against per-space plan boundaries.
What is not a legitimate reason: vague dissatisfaction. Contentful is a mature, capable, well-supported platform with a strong API surface, genuinely good localization handling, and enterprise governance features that many teams depend on. If the current pain is a badly designed content model, migrating will carry that model to a new platform intact. Fix the model first; you may find the platform was never the problem. If you are still deciding between the two, our Sanity vs Contentful comparison covers the trade-offs in full, and the platform selection guide works through the decision without assuming an answer.
Why This Migration Is Different
Most migration advice you will read is written for moves off WordPress or another page-oriented CMS, where the central problem is that content does not have a structure yet — it is HTML with shortcodes in it, and the work is inventing a model that never existed. A Contentful to Sanity move is headless-to-headless, and the content is already structured. The work shifts from restructuring to translation.
- Content types become schemas. Both platforms model content as typed documents with defined fields, so the mapping is largely one-to-one — expressed in Contentful's UI on one side and in TypeScript on the other.
- References become references. Relationships between entries survive the move as relationships between documents; the work is preserving identity so nothing dangles after import.
- Rich Text becomes Portable Text. Both are structured JSON rather than HTML strings, so this is a node-by-node transform, not a parse-and-hope conversion.
- Locales become localized fields. Contentful's locale system and Sanity's localization patterns solve the same problem differently, so this needs a deliberate design decision rather than a mechanical mapping.
The practical consequence is the biggest scope reducer available to you: if your frontend is already a headless application — a Next.js site consuming the Contentful Delivery API — the components, routing, and design system usually survive untouched. Only the data layer is rewritten. Establish whether that is true of your site before scoping anything else, because the difference between "rewrite the data layer" and "rebuild the frontend" is the difference between two very different projects.
The Five Phases
Inventory & Baseline
Start with a space export and a written description of the content model: every content type, every field and its validations, every reference relationship, the locale configuration, and the asset library. Alongside that, a crawl of the live site records URLs, titles, metadata, and structured data. The model documentation defines what has to be translated; the crawl defines what has to still be true afterward.
Schema Translation Design
Contentful content types become Sanity schemas defined in code. Most of the mapping is mechanical — fields to fields, references to references, arrays to arrays — but the decisions that matter are the ones a converter cannot make: which embedded entry types become Portable Text blocks, how locales become localized fields or separate documents, and which legacy content types have quietly gone unused and should not be carried across.
Scripted Transformation
Entries and assets are pulled through the Contentful export and management APIs, transformed into Sanity documents, written as ndJSON, and loaded with Sanity's import tooling. Contentful entry IDs are preserved or deterministically mapped so references resolve, and assets are re-uploaded and their URLs rewritten. The script is the deliverable: it must be re-runnable so the migration can be rehearsed repeatedly and re-run against fresh content at cutover.
Frontend Data Layer & Parity Check
The frontend's data access is rewritten — Delivery API calls become GROQ queries, Rich Text renderers become Portable Text renderers, and image handling moves to Sanity's asset pipeline. Then the staging site is compared against the baseline page by page: content parity, metadata parity, structured-data parity. Nothing cuts over until that comparison is clean.
Instrumented Cutover
Final content sync, environment variables and webhooks repointed, deployment cutover, and integrations re-verified end to end — with analytics and conversion events confirmed live before launch is called done. The old Contentful space stays readable for a defined period rather than being deleted on launch day.
Those phases are the migration-specific expression of the documented-baseline, side-by-side-comparison, instrumented-launch process we publish in our methodology. The discipline does not change with the destination platform; only the translation layer in the middle does.
What Actually Goes Wrong in These Migrations
- Rich Text edge cases. This is the hard part, and it is worth saying plainly: headings and lists convert trivially, but embedded entries and embedded assets inside Rich Text are where these projects actually get stuck. Each embed type needs a Portable Text block type, a schema, and a frontend renderer — and the inline variants (an entry referenced mid-sentence rather than as its own block) are the ones teams forget until a page renders with a hole in it.
- Locale handling. Contentful localizes at the field level within a single entry. Sanity offers several patterns — localized field objects, separate documents per language, or a document-level translation reference — and the choice affects querying, editing, and routing. Deciding this during the transform script instead of during design is how teams end up migrating twice.
- Asset URL rewrites. Assets keep working right up until the old space is decommissioned, which makes it very easy to ship with images still served from Contentful's CDN. Every asset reference — including the ones buried inside Rich Text nodes and inside plain-text fields as hardcoded URLs — has to be rewritten and verified, not assumed.
- Unmapped webhooks and integrations. Contentful spaces accumulate webhooks, scheduled publishes, preview configurations, and app integrations that nobody documented. They are invisible in a content export and only surface when something silently stops firing after cutover. List them during inventory and map each one to its Sanity equivalent or an explicit decision to retire it.
- Un-repeatable imports. A hand-corrected, one-shot import means the rehearsal was the performance, and any content published during the project is stranded. Scripts must run cleanly from scratch against fresh content, every time.
What You Gain on the Other Side
A content model that lives in your repository, reviewed like code and versioned with it; an editing environment you can shape to your editors instead of configuring around; and GROQ queries that shape responses at the API rather than in application code. The structural benefit that matters most for search is that typed documents with real references map cleanly onto schema.org markup — the entity-level clarity that classic search and AI answer engines both reward, and the foundation of the entity SEO work we specialize in. That said, Contentful models content as typed documents too; the gain here is in ownership and extensibility, not in structure you did not already have.
If you want the build side rather than the decision side, our Sanity development page covers schema design, Studio customization, and frontend integration. Webvello is an independent development partner and is not affiliated with Sanity Inc. or Contentful.
Frequently Asked Questions
How long does a Contentful to Sanity migration take?
It depends on how many content types exist, how heavily Rich Text fields use embedded entries and assets, how many locales are in play, and whether the frontend is being rebuilt at the same time. Because the content is already structured, these projects are usually shorter than a migration off a page-builder CMS — but the honest answer is that we scope the timeline in writing after the content model is documented. Anyone quoting a fixed timeline before reading your content types is guessing.
Does the frontend need to be rebuilt too?
Often not. If the site is already a headless frontend — a Next.js app consuming the Contentful Delivery API, for example — the components, routing, and styling typically survive. What changes is the data layer: API calls become GROQ queries, response shapes change, and the Rich Text renderer is swapped for a Portable Text renderer. That is a meaningful scope reducer, and it is worth confirming early because it changes the size of the project substantially.
What happens to Contentful Rich Text fields?
Rich Text is a structured JSON document, and Portable Text is too, so the transform is node-by-node rather than a lossy HTML parse — headings, lists, marks, and hyperlinks map cleanly. The hard part is embedded entries and embedded assets inside Rich Text: each embed type needs a corresponding Portable Text block type, a Sanity schema, and a renderer on the frontend. That mapping is a design decision, not something a generic converter can make for you.
Is there SEO risk in moving from Contentful to Sanity?
This is the lowest-risk migration type there is, because the CMS is not what serves your URLs. If the frontend and its routing stay in place, indexed URLs do not change at all, and the risk is limited to content or metadata that fails to transform correctly. We still verify parity against a documented baseline before cutover — titles, meta descriptions, structured data, and rendered content compared page by page — because silent field-level loss is the realistic failure mode here, not URL loss.
Weighing a Move Off Contentful?
The first deliverable is a documented content model and translation plan — you keep it whether or not you migrate with us, and it is just as useful if the answer turns out to be "stay."
Start With the Content Model