Migration

How do you migrate from Contentful to Sanity?

Short answer

A Contentful to Sanity migration runs in three moves: convert the space, rewrite the schema as reviewed code, and import into a test dataset. Sanity's contentful-to-sanity tool exports the space and writes a schema file and a newline-delimited JSON (NDJSON) dataset (Sanity plugin page, October 2026). Version 5.0.0 drops Rich Text tables (converter source, October 2026), so count yours first.

Official converter
contentful-to-sanity 5.0.0, 13 March 2026Exports a space, writes a schema file and NDJSON; skips Rich Text tables, and imports one locale unless you run the separate schema and dataset commands with --intl multipleSources: Sanity (contentful-to-sanity Changelog, contentful-to-sanity source: contentfulEntryToSanityObject.ts, contentful-to-sanity source: command.ts)
Contentful records
10,000 per environment on LiteLite's Starter Space allows 2 environments, each with its own 10,000 records of entries plus assets; Sanity counts documents across every datasetSources: Contentful (Pricing, Environments, Spaces)
Sanity Growth
$15 per seat a month, 25,000 documentsUnlimited content types and locales, 2 datasets; documents count across every dataset, drafts included, with no overage allowedSources: Sanity (Plans that scale with you); Sanity Docs (Plans and payments)
Locale multiplier
100 documents in 3 languages become 400Document-level localization stores one document per language plus one translation.metadata document; field-level keeps oneSources: Sanity (@sanity/document-internationalization README); Sanity Docs (Localization)

Deploi’s call

Plan a Contentful to Sanity migration around what the converter won't do: Rich Text tables, JSON fields, archived entries, extra locales and every front-end query. Count documents the Sanity way, with drafts, every locale and every dataset, before you commit, because that number decides between Growth and Enterprise. Whether to leave Contentful at all is a separate decision; make it first.

PhaseMove from Contentful to Sanity in phases when

  • Your Contentful organization runs several spaces or front ends, so each one can move, launch and be checked while the rest stays on Contentful.
  • Your front end calls Contentful's Delivery or GraphQL APIs (application programming interfaces) throughout, because rewriting those queries in GROQ (Graph-Relational Object Queries) is the largest line item in Deploi's judgment, and a phase keeps each rewrite small enough to test.
  • Editors keep publishing during the move, so you re-export and re-import each phase's content just before it launches.

MigrateMove a Contentful space to Sanity in one cutover when

  • One space feeds one front end, so a single launch replaces every Contentful query.
  • A test import matches your Contentful counts per content type, and the documents, with drafts and every locale, fit Sanity Growth's 25,000, or 50,000 with the $299 a month add-on, as of October 2026.
  • Few Rich Text fields hold tables, because the converter drops tables and each one is rebuilt by hand.

What does Sanity give you to migrate from Contentful?

Sanity gives you two aids to migrate from Contentful, and neither runs the move end to end. The contentful-to-sanity command-line interface (CLI) turns a space export into a schema file and a newline-delimited JSON (NDJSON) dataset (Sanity plugin page, October 2026). Sanity's migration skill for AI (artificial intelligence) agents has a Contentful guide (Sanity on GitHub, October 2026), and Sanity calls the skill "agent-scaffolded, not fully automated" (Sanity Blog, What's new in August, October 2026).

Sanity's two Contentful migration aids, and what each leaves to you
AidWhat it doesWhat it leaves to you
contentful-to-sanity CLIConverts a space export into a schema file and an NDJSON datasetRich Text tables, JSON fields, archived entries and extra locales
Migration skill, Contentful guideHelps an AI agent plan and write migration scriptsThe decisions, running the scripts and checking the result
Sources: Sanity (Contentful to Sanity plugin, contentful-to-sanity source: contentfulEntryToSanityObject.ts, contentful-to-sanity source: command.ts, contentful-to-sanity source: contentfulToDataset.ts); Sanity Blog (What's new in August); Sanity on GitHub (sanity-migration skill). As of 25 October 2026.

Whether to leave Contentful at all is a decision separate from the migration, with the case for staying, and so are the points where each platform's price jumps to Enterprise. Salesforce says it completed the purchase on 1 September 2026 (Salesforce, October 2026); what Salesforce ownership changes for Contentful customers has its own answer.

How do Contentful content types, entries and references map to Sanity?

Each Contentful content type becomes a Sanity document type in code, each entry a document, and each Reference field a Sanity reference. The contentful-to-sanity converter also decides that slug editors become slug objects, Markdown becomes Portable Text unless you pass --keep-markdown, and JSON object fields become JSON strings (converter source, October 2026). When you rewrite the generated schema, merge types that were split to fit Contentful's 50-field cap (Contentful data model, October 2026).

Contentful's model maps to Sanity almost one to one
ContentfulSanityWhat changes
Content typeDocument type in codeThe schema lives in your repository
EntryDocumentA draft is a second document
Rich TextPortable Text arrayTables need a table schema you declare
AssetImage or file assetCounts toward assets, not documents
LocaleLanguage field or arrayDocument-level or field-level per type
JSON objectString after conversionReshape into a typed object
Sources: Sanity Docs (Plans and payments, Localization); Sanity (contentful-to-sanity source: contentfulEntryToSanityObject.ts). As of 24 October 2026.

How does Contentful Rich Text become Portable Text?

Contentful Rich Text converts to Portable Text automatically for paragraphs, headings, lists, marks, links and embedded entries, but not for tables. Both are JSON trees: Contentful nests every node under one document root (Contentful rich text docs, October 2026), and Portable Text stores an array of blocks, each holding spans that carry marks (Sanity block content docs, October 2026). The contentful-to-sanity tool runs the @portabletext/contentful-rich-text-to-portable-text library, resolves embedded entries and entry links into Sanity references, turns horizontal rules into a break block, and drops table nodes (converter source, October 2026). Contentful's Rich Text field supports tables (Contentful data model), so search the export for table nodes before you convert, because a warning scrolls past in a long run.

The converter drops tables, so rebuild each one yourself. Sanity's Portable Text Editor has built-in table editing from Studio v6.6.0, which is off by default and binds to a table schema you declare (Sanity Docs, Configure the Portable Text Editor; Sanity Blog, What's new in August, October 2026); declare that schema and load each Contentful table into it. We built our own table block for deploi.ca, described in tables and citations in Portable Text. The Portable Text review covers the editor itself.

How should Contentful locales map to Sanity localization?

Map each Contentful content type to Sanity's document-level or field-level localization by one test: do editors publish locales on different days? Contentful publishes all of an entry's locales at once by default; publishing one locale alone needs locale-based publishing, sold only on specific plans (Contentful locale-based publishing, October 2026). Sanity's field-level localization keeps every language in one document but publishes all languages at once; document-level localization gives each language its own document that publishes independently (Sanity localization docs, October 2026). So per-locale publishing maps to document-level. Fallback locales don't migrate as a setting: in Sanity you write the fallback into each query with the coalesce() function of GROQ (Graph-Relational Object Queries), as the same docs show. The converter's --intl multiple mode writes the older __i18n_lang and __i18n_refs fields, which the current @sanity/document-internationalization plugin replaces with translation.metadata documents (converter source; plugin upgrade notes, October 2026). The Sanity localization review rates both approaches.

What happens to Contentful environments, and how many Sanity documents will you need?

Contentful environments become Sanity datasets only for development and risky schema changes; routine staging happens inside the production dataset, as drafts on Growth. Sanity reserves a separate dataset for development and for changes that rename, remove or restructure content (Sanity environments docs, October 2026). The counting rule flips too: Contentful gives every environment its own 10,000-record limit on Lite's Starter Space (Contentful environments FAQ; Contentful pricing, October 2026), while Sanity counts every stored document, drafts included, across all datasets, with no document overage on Growth (Sanity plans and payments, October 2026). So a full staging copy doubles your Sanity document count. Contentful's master alias maps to Sanity dataset aliases, which need Enterprise (Sanity environments docs).

A full Contentful Starter Space in 3 locales can outgrow Sanity Growth (Deploi arithmetic)
Sanity setupDocumentsFits Growth
Field-level, production only8,000Yes, under 25,000
Field-level, production plus a full staging copy16,000Yes
Document-level in 3 locales, production only32,000Only with the $299 a month quota add-on
Document-level in 3 locales, plus a staging copy64,000No, above 50,000
Deploi arithmetic for 8,000 entries and 2,000 assets, before drafts, which count as extra documents. Document-level adds one translation.metadata document per entry (plugin documentation); the add-on raises Growth to 50,000 (Sanity add-ons).Sources: Contentful (Pricing); Sanity Docs (Plans and payments, Extending the Growth plan with paid add-ons); Sanity (@sanity/document-internationalization README). As of 24 October 2026.

What happens to Contentful assets, URLs and dates in the move?

Contentful assets move as files, and URLs stay put unless you change them, because your front end owned the URLs, not Contentful. The converter points each asset at its Contentful URL through _sanityAsset (converter link source, October 2026), and Sanity's importer uploads every file it references, failing the run on a missing file unless you pass --allow-failing-assets (Sanity import docs, October 2026). So keep the Contentful space live until the import finishes, and archive an export made with the --download-assets flag of Contentful's command-line interface (CLI) (Contentful CLI docs, October 2026). Slug editors become Sanity slugs (converter source), so routes can stay identical.

Two traps hide in dates and history:

What replaces Contentful's roles, workflows, apps and webhooks?

None of Contentful's roles, workflows, apps or webhook secrets carry over to Sanity, so you rebuild each one. The export of Contentful's command-line interface (CLI) leaves workflows, apps and webhook credentials behind (Contentful CLI docs, October 2026); what a Contentful export leaves behind lists the rest. Here is what replaces each in Sanity, as of October 2026:

  • Roles: Growth includes 5 roles and free viewer seats (Sanity pricing); custom roles need Enterprise (Sanity roles docs). See roles and permissions.
  • Workflows: Sanity's Workflows product is in early access (Sanity Workflows docs); Comments and Tasks come with Growth (Sanity pricing).
  • Scheduling: Growth has Scheduled drafts for one document at a time; Content Releases needs Enterprise (Sanity environments docs).
  • Apps and UI (user interface) extensions: rebuild them as Sanity Studio plugins or custom inputs in your own React code.
  • Webhooks: recreate them with a GROQ (Graph-Relational Object Queries) filter and a new secret (Sanity webhooks docs).
  • Front-end queries: rewrite Contentful Delivery and GraphQL calls in GROQ. In Deploi's judgment this is the largest line item.

What is the step-by-step plan for a Contentful to Sanity migration?

A Contentful to Sanity migration runs as eight steps, and in Deploi's judgment the developer who owns the front end should own all of them. The plan uses Sanity's contentful-to-sanity tool, version 5.0.0 (changelog).

Moving a Contentful space to Sanity

  1. Audit the space. Count entries, assets and archived entries per environment, list locales, and find Rich Text tables and JSON fields.
  2. Choose localization per type. Document-level where locales publish on different days, field-level where they ship together.
  3. Export with drafts and assets. Include drafts, archived entries and assets (Contentful export docs).
  4. Rewrite the generated schema. Rewrite it as reviewed code, and declare a table schema if Rich Text used tables.
  5. Fix what the converter skipped. Rebuild tables and JSON fields, decide what archived entries become, reshape locale fields.
  6. Validate, then import to a test dataset. Run sanity documents validate on the import file (Sanity migration docs), import, and compare counts per type.
  7. Rewrite queries and test routes. Swap Contentful calls for GROQ (Graph-Relational Object Queries) queries, crawl old URLs, add redirects.
  8. Freeze and cut over. Freeze editing, re-export, import with --replace, keep the imported ID list for rollback, then switch the front end.

Step 8 is how we ship large content packages with release scripts; choosing --replace or --missing covers its re-import.

Deploi’s point of view

Where we disagree
Two claims mislead buyers. The first is that Contentful localization is all-or-nothing per content type. Contentful's own docs describe locales enabled per field, and spaces with locale-based publishing publish one locale at a time; a team that relies on that and picks field-level localization in Sanity loses per-locale publishing. The second is the one-command migration. The official converter drops Rich Text tables, stores JSON object fields as strings, and imports archived entries as published documents.
What we’ve seen
We move content into Sanity as packages, never by hand. The first wave of deploi.ca's Shopify data section went in as one package of 406 documents and 159 images, signed off in October 2026, with check queries and a rollback by document ID list. Our decision pages go in as NDJSON with drafts-prefixed IDs, so nothing publishes before review, and with weak references, so import order doesn't matter. Our Portable Text carries a custom table block, and 229 of the 256 live answers in our Shopify FAQ hub use it, counted 24 October 2026. Those answers reach Sanity through our own Markdown to Portable Text importer; a converter that dropped tables would have stripped them from 229 of those 256 answers.
What we'd tell you to do first
Export the space with drafts and archived entries, then run three counts before anyone writes a schema: Rich Text fields that contain tables, JSON object fields, and archived entries. Each one is work the converter won't do for you. Then multiply entries by locales and by datasets to see which Sanity plan you are actually buying.
What we refuse to promise
A duration or an hours figure from a call. The drivers are the number of content types, the Rich Text features in use, the locale model, and how many front-end queries call Contentful directly; we scope from those counts.

When this answer changes

  • Contentful changes Lite's locale, content type, environment or record quotas
  • Sanity changes the Growth document quota, dataset count or seat price
  • contentful-to-sanity adds Rich Text table support or changes its locale output to translation.metadata documents
  • Sanity Workflows leaves early access or changes plan
  • Sanity moves Content Releases or dataset aliases into Growth
  • Sanity changes or retires its migration skill's Contentful guide

Sources

Every source opened and checked on 25 October 2026.

  1. Sanity, Plans that scale with yousanity.io · checked 24 October 2026
  2. Contentful, Pricingcontentful.com · checked 24 October 2026
  3. Sanity Docs, Plans and paymentssanity.io · checked 24 October 2026
  4. Sanity Docs, Localizationsanity.io · checked 24 October 2026
  5. Sanity Docs, Development, staging, and production environmentssanity.io · checked 24 October 2026
  6. Sanity Docs, Import datasanity.io · checked 24 October 2026
  7. Sanity, Contentful to Sanity pluginsanity.io · checked 24 October 2026
  8. Sanity, contentful-to-sanity source: contentfulEntryToSanityObject.tsgithub.com · checked 24 October 2026
  9. Sanity, contentful-to-sanity source: command.tsgithub.com · checked 24 October 2026
  10. Sanity, @sanity/document-internationalization READMEgithub.com · checked 24 October 2026
  11. Contentful, Environmentscontentful.com · checked 24 October 2026
  12. Contentful, Import and export content with the Contentful CLIcontentful.com · checked 24 October 2026
  13. Contentful, Data modelcontentful.com · checked 24 October 2026
  14. Contentful, What is rich text?contentful.com · checked 24 October 2026
  15. Sanity Docs, Block contentsanity.io · checked 24 October 2026
  16. Contentful, Localization with Contentfulcontentful.com · checked 24 October 2026
  17. Sanity, You're stuck at 49 of 50 content types. Again.sanity.io · checked 24 October 2026
  18. Sanity, document-internationalization: Upgrade from v1github.com · checked 24 October 2026
  19. Sanity Docs, Extending the Growth plan with paid add-onssanity.io · checked 24 October 2026
  20. Sanity Docs, How early access works (Workflows)sanity.io · checked 24 October 2026
  21. Sanity, contentful-to-sanity source: contentfulToDataset.tsgithub.com · checked 24 October 2026
  22. Contentful, Spacescontentful.com · checked 24 October 2026
  23. Salesforce, Salesforce Signs Definitive Agreement to Acquire Contentfulsalesforce.com · checked 24 October 2026
  24. Sanity Docs, GROQ-powered webhookssanity.io · checked 24 October 2026
  25. Sanity, contentful-to-sanity Changeloggithub.com · checked 24 October 2026
  26. Sanity Docs, Migrating your schema and contentsanity.io · checked 24 October 2026
  27. Sanity Docs, Rolessanity.io · checked 24 October 2026
  28. Contentful, Locale-based publishingcontentful.com · checked 24 October 2026
  29. Sanity, contentful-to-sanity source: contentfulLinkToSanityReference.tsgithub.com · checked 24 October 2026
  30. Sanity Docs, Configure the Portable Text Editorsanity.io · checked 24 October 2026
  31. Sanity Blog, What's new in Augustsanity.io · checked 24 October 2026
  32. Sanity, contentful-to-sanity source: createIntlFields.tsgithub.com · checked 24 October 2026
  33. Sanity on GitHub, sanity-migration skillraw.githubusercontent.com · checked 25 October 2026