Migration

How do you migrate a Drupal site to Sanity, step by step?

Short answer

A Drupal to Sanity migration is a script your developers write and rerun. It exports each content type through JSON:API, an application programming interface (API) module in Drupal core (Drupal.org, October 2026). It maps fields to Sanity schema types, converts HTML to Portable Text and imports with Drupal's IDs, so reruns hit the same documents (Sanity Docs, October 2026).

Export route, Drupal 8.7 and later
Core JSON:API, no configuration neededResources follow Drupal entity types and bundles, so each content type exports as its own collection; Drupal 7 needs added modulesSources: Drupal.org (JSON:API module, Drupal 8.7.0 release notes); Sanity (The Drupal developer's introduction to Sanity.io)
Rich text converter
htmlToBlocks() in @portabletext/block-toolsHeadings, lists and paragraphs convert by default; images need custom rules, and built-in tables need Studio v6.6.0 and a declared table typeSources: Sanity Learn (Converting HTML to Portable Text); Sanity Docs (Configure the Portable Text Editor)
References on import
Weak first, strong once every document is inImport order doesn't matter, and IDs may use letters, numbers, hyphens and underscores, so node-123 keeps the Drupal node IDSource: Sanity Docs (Import data)
Roles on Growth
5 fixed roles, none scoped to a content typeCustom roles, the match for Drupal's per-content-type permissions, are Enterprise only, as of October 2026Sources: Sanity Docs (Roles and permissions); Sanity (Plans that scale with you)

Deploi’s call

Move a single Drupal site in one cutover with a re-runnable import, and move a multisite or a heavily moderated site one site or section at a time. Either way, remodel as you go: Paragraphs become arrays of object types, HTML becomes Portable Text, and revisions stay behind in an archived Drupal database.

MigrateMove a Drupal site to Sanity in one cutover when

  • One site, one front end and one editorial team, so one re-runnable import and one redirect map cover everything.
  • The new front end launches with every content type at once, because a half-moved site needs two systems serving one domain.
  • Content Moderation is light enough that drafts plus Scheduled drafts cover it, so Sanity's Growth plan fits as of October 2026.

PhaseMove a Drupal site to Sanity in phases when

  • A Drupal multisite runs several sites, so move one site at a time and keep Drupal serving the rest.
  • One section, such as a blog or a resource library, can launch first, because its URLs can be routed separately.
  • Editors depend on per-content-type permissions or multi-stage moderation, so prove the Sanity plan on one section first: Sanity's docs put custom roles on Enterprise only and Sanity Workflows in early access, as of October 2026.

How do you get content out of Drupal for a move to Sanity?

You get content out of Drupal 8.7 and later through core's JSON:API module, and out of Drupal 7 through added modules.

  • Drupal 8.7 and later. JSON:API, a core application programming interface (API) since 8.7.0 (Drupal 8.7.0 release notes, October 2026), is "a zero-configuration required, opinionated" way to read a site's content, Drupal.org says. Its resources follow Drupal's entity types and bundles, so each content type exports as its own collection (Drupal.org, JSON:API module, October 2026).
  • Drupal 7. Core has no JSON:API, so you install extra modules to get one (Sanity's Drupal guide, October 2026).

Which export tool fits your Drupal version compares the routes. Sanity Learn has no Drupal course (Sanity Learn, October 2026), though its WordPress course says many of its techniques transfer to HTML-based content management systems like Drupal (Sanity Learn, October 2026). For AI agents, Sanity's MCP (Model Context Protocol) server lists a guided Drupal path (Sanity blog, October 2026), and Sanity's migration skill has a Drupal guide covering "source-specific extraction routes" (Sanity agent toolkit, October 2026). Sanity calls that skill "agent-scaffolded, not fully automated" (Sanity blog, August releases, October 2026). This playbook assumes you've decided to leave Drupal; whether to upgrade to Drupal 11 or replatform is its own question. Our view of Drupal rests on its documentation and public sources.

How do Drupal content types and fields map to Sanity schema types?

Each Drupal content type becomes a Sanity document type, and each Drupal field becomes a field you define in code on that type, with a validation rule where Drupal had one. Sanity starts blank: "There are no content types setup with a clean installation" (Sanity's Drupal guide, October 2026). A list field's allowed values move to a string field's options.list of "predefined values" (Sanity Docs, String, October 2026).

How do Drupal entity references and taxonomy map to Sanity references?

Drupal entity references become Sanity references, and the import makes their order irrelevant: "During import, all references are automatically set to weak, then flipped to strong after all documents are in place" (Sanity Docs, Import data, October 2026). Sanity accepts any ID made of letters, numbers, hyphens and underscores, so an ID such as node-123 or term-45 keeps the Drupal entity ID and lets every rerun hit the same document. Drupal taxonomy terms can have hierarchies (Sanity's Drupal guide, October 2026), so each term becomes a document with a parent reference.

References keep Drupal IDs, and each vocabulary becomes a document type
Drupal structureSanity equivalentMigration note
Entity reference to a nodereference, or an array of referencesMulti-value fields become arrays
Reference to a taxonomy termreference to a term documentOne document type per vocabulary
Term hierarchyparent reference on the termRebuild the tree from parent IDs
Sources: Sanity Docs (Import data); Sanity (The Drupal developer's introduction to Sanity.io). As of 24 October 2026.

Two Drupal structures leave this mapping. Revisions stay behind in an archived Drupal database (Deploi's judgment), and Drupal translations get their own playbook because Drupal.org lists the translation support in core's JSON:API module, an application programming interface (API), as "Basic functionality" (Drupal.org, JSON:API module, October 2026).

What do Drupal HTML body fields, Paragraphs and Layout Builder become in Sanity?

Drupal HTML body fields become Portable Text, Paragraphs become arrays of object types, and Layout Builder overrides need a modeling decision before any script runs. Portable Text is Sanity's JSON-based format for rich text storage (Sanity's Drupal guide, October 2026); how Portable Text stores rich text explains it.

  • HTML body fields. htmlToBlocks() from @portabletext/block-tools converts headings, lists and paragraphs by default, and images need custom rules (Sanity Learn, Converting HTML to Portable Text, October 2026). HTML tables have a target: the Portable Text Editor has built-in table editing from Studio v6.6.0, off by default, once you declare a table schema type (Sanity Docs, Configure the Portable Text Editor, October 2026).
  • Paragraphs. The module lets editors pick predefined Paragraph types instead of one WYSIWYG (what you see is what you get) body field (Drupal.org, Paragraphs, October 2026). Each Paragraph type becomes an object type in an array, the way Sanity models a page builder (Sanity's Drupal guide, October 2026), and a nested Paragraphs field becomes an array inside that object (Deploi's judgment).
  • Layout Builder. It can "override the default display on individual items such as nodes" (Drupal.org, Layout Builder, October 2026), so each overridden node carries its own layout. Decide per override: a page builder array, or the default template.

Pages built in Acquia Site Studio follow their own path to Sanity.

What happens to Drupal media, files and images in a move to Sanity?

Drupal media, files and images become Sanity image and file assets, uploaded by the import itself through the _sanityAsset property. In each NDJSON (newline-delimited JSON) line, _sanityAsset sits where an asset reference would go, prefixed image@ or file@, with an absolute file path or a public URL (Sanity Docs, Import data, October 2026). The import fails when an asset is unavailable unless you pass --allow-failing-assets, so check every Drupal file URL before the run and log the ones that 404.

  • Alt text and captions. Copy them into fields on the Sanity image object, so they travel with every use (Deploi's judgment).
  • Shared files. A Drupal media entity used on many nodes is one file. Sanity gives an uploaded image an ID derived from the image itself, so uploading the same image twice never creates a duplicate. Uploading through the JavaScript client also lets you record the Drupal media ID in the asset's source key, so a rerun can skip files already uploaded (Sanity Learn, Uploading assets efficiently, October 2026).
  • Media Library. Sanity's organization-wide asset app is an add-on for certain Enterprise plans as of October 2026 (Sanity Docs, Media Library introduction; Sanity pricing). A Drupal move can use ordinary dataset assets instead, which need no add-on.

How do you keep Drupal URL aliases and redirects working after a move to Sanity?

Keep Drupal URLs by exporting each node's current URL alias into a Sanity slug, and each Redirect module entry into a redirect document your front end reads. Pathauto builds aliases from a pattern system "that uses tokens which the administrator can change" (Drupal.org, Pathauto, October 2026), so export the alias each node has today, not the pattern: nodes published under an older pattern can keep older aliases (Deploi's judgment).

The Redirect module lets editors "create manual redirects and maintain a canonical URL for all content" (Drupal.org, Redirect, October 2026). Each one becomes a redirect document with a source, a destination and a permanent flag, the model in Sanity's redirect guide (Sanity Docs, Managing redirects with Sanity, October 2026). That guide notes that in Drupal, redirects are "typically a plugin away", while a headless front end needs integration work to read them. Add Drupal's system paths, such as /node/123, as redirects too, because old links and feeds still use them (Deploi's judgment). Keeping search rankings through the move carries the full SEO (search engine optimization) checklist.

What replaces Drupal roles, Content Moderation, Views and modules like Webform and Search API?

Drupal's roles, moderation and modules, including Search API (a search application programming interface module), don't migrate. Each becomes a Sanity setting, a GROQ (Graph-Relational Object Queries) query in the front end, or a service. Drupal grants editor permissions "based on role and content type" (Sanity's Drupal guide, October 2026). Sanity's Growth plan has five fixed roles that span whole datasets (Contributor writes drafts "within all datasets"), so none limits an editor to one content type. Custom roles are Enterprise only as of October 2026 (Sanity Docs, Roles and permissions; Sanity pricing). Drupal's Content Moderation adds states beyond published and unpublished (Drupal.org, October 2026); Sanity Workflows is in early access (Sanity Docs; early access terms, October 2026).

Each Drupal feature has a Sanity replacement or a service
Drupal featureSanity replacementNote, October 2026
Roles by content typeCustom rolesEnterprise only
Light moderationDrafts plus Scheduled draftsGrowth; Studio v4.14.0 or later
Coordinated launchesContent ReleasesEnterprise add-on
Multi-stage reviewWorkflowsEarly access
ViewsGROQ queriesRewrite each listing
WebformA form service or @sanity/form-toolkitToolkit covers Mailchimp, HubSpot
Search APIGROQ text search or an Algolia syncSync runs as a Sanity Function
Sources: Sanity Docs (Roles and permissions, Scheduled drafts, Content Releases user guide, Cookbook: Editorial review, Forms with Sanity, Search text content with GROQ, How to implement front-end search with Sanity); Sanity (Plans that scale with you); Drupal.org (Concept: Uses of Views). As of 24 October 2026.

Site search on Sanity compares the search options.

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

A Drupal to Sanity migration runs in ten steps, and the import runs many times. It fails on any document ID that already exists, so reruns pass --replace with the same Drupal-derived IDs (Sanity Docs, October 2026).

Migrating a Drupal site to Sanity

  1. Inventory. Count nodes per type, Paragraph types, vocabularies, Views and modules.
  2. Cut list. Drop revisions, unused types, generated listings and modules with no front-end effect, because nothing reads them.
  3. Schema. Write one document type per content type and one object type per Paragraph type.
  4. Export. Pull JSON:API (application programming interface) collections, or Drupal 7 tables.
  5. Transform. Map fields, run htmlToBlocks() and derive each ID from its Drupal ID.
  6. Stage. Import into a staging dataset with webhooks off.
  7. Validate. Run sanity documents validate (Sanity Docs) and match counts.
  8. Front end. Rewrite Views as queries and load the redirects.
  9. Cutover. Freeze Drupal edits, rerun with --replace, then switch traffic.
  10. Read-only. Keep Drupal read-only until every old URL resolves.

Deploi’s point of view

Where we disagree
The common advice treats a Drupal migration as an export problem. Getting content out is the easy part, because core JSON:API exposes it without configuration. The work is in the decisions inside the content: nested Paragraphs, Layout Builder overrides stored per node, HTML tables, and aliases built from patterns that changed over the years. A script that copies those one to one moves Drupal's structure into Sanity and keeps the problems you left Drupal to fix. The second mistake is migrating revision history: editors rarely read it, and every revision you import is another document to count, store and validate.
What we’ve seen
Two problems a Drupal move raises are ones we've handled on deploi.ca's own Sanity content. Rich text: our Shopify FAQ hub stores tables as a custom Portable Text block type with a caption, sources and an as-of date, and 229 of its 256 live answers carried at least one table on 24 October 2026. Re-runnable imports: we ship content as packages with fixed document IDs, count checks and a rollback by ID list; the first wave of our Shopify data section was 406 documents and 159 images in October 2026, and our release scripts dry-run unless told to apply and export the dataset first. Links between our two datasets are stored as validated URL strings, because cross-dataset references need an Enterprise plan. All of it is deploi.ca's own Sanity work.
What we'd tell you to do first
Before any schema work, list every Paragraph type and count the nodes that carry a Layout Builder override. Those two numbers decide whether the move is a field mapping or a content redesign, and they belong in the scope before anyone prices the work.
What we refuse to promise
We won't promise that revisions, Webform submissions or Views pages come across on their own. Each one is a decision, and the usual answer is to archive it, export it to the service that replaces it, or rebuild it.

When this answer changes

  • Sanity publishes an official Drupal importer or a Drupal course on Sanity Learn
  • Sanity Workflows leaves early access
  • Sanity adds custom roles or content-type scoping to the Growth plan
  • @portabletext/block-tools changes htmlToBlocks() or adds a default table rule
  • Content Releases or Media Library changes plan
  • Drupal core JSON:API changes how it exposes Paragraphs, Layout Builder or translations

Sources

Every source opened and checked on 25 October 2026.

  1. Drupal.org, JSON:API moduledrupal.org · checked 24 October 2026
  2. Drupal.org, Paragraphsdrupal.org · checked 24 October 2026
  3. Drupal.org, Layout Builderdrupal.org · checked 24 October 2026
  4. Drupal.org, Pathautodrupal.org · checked 24 October 2026
  5. Drupal.org, Redirectdrupal.org · checked 24 October 2026
  6. Drupal.org, Content Moderation overviewdrupal.org · checked 24 October 2026
  7. Drupal.org, Concept: Uses of Viewsdrupal.org · checked 24 October 2026
  8. Drupal.org, Multisite Drupaldrupal.org · checked 24 October 2026
  9. Sanity Docs, Import datasanity.io · checked 24 October 2026
  10. Sanity Learn, Converting HTML to Portable Textsanity.io · checked 24 October 2026
  11. Sanity Docs, Configure the Portable Text Editorsanity.io · checked 24 October 2026
  12. Sanity Docs, Managing redirects with Sanitysanity.io · checked 24 October 2026
  13. Sanity Docs, Roles and permissionssanity.io · checked 24 October 2026
  14. Sanity, Plans that scale with yousanity.io · checked 24 October 2026
  15. Sanity Docs, Cookbook: Editorial reviewsanity.io · checked 24 October 2026
  16. Sanity Docs, Scheduled draftssanity.io · checked 24 October 2026
  17. Sanity Docs, Media Library introductionsanity.io · checked 24 October 2026
  18. Sanity Docs, Forms with Sanitysanity.io · checked 24 October 2026
  19. Sanity Docs, Search text content with GROQsanity.io · checked 24 October 2026
  20. Sanity Docs, Important considerations for schema and content migrationssanity.io · checked 24 October 2026
  21. Sanity, The Drupal developer's introduction to Sanity.iosanity.io · checked 24 October 2026
  22. Sanity, You'll need a CMS eventually. Let your agent set it up.sanity.io · checked 24 October 2026
  23. Sanity Learn, Migrating content from WordPress to Sanity: Introductionsanity.io · checked 24 October 2026
  24. Sanity Docs, Content Releases user guidesanity.io · checked 24 October 2026
  25. Sanity Docs, Stringsanity.io · checked 24 October 2026
  26. Sanity Docs, How to implement front-end search with Sanitysanity.io · checked 24 October 2026
  27. Sanity Learn, Replatforming from a legacy CMS to a Content Operation Systemsanity.io · checked 24 October 2026
  28. Sanity Docs, Cross-dataset referencessanity.io · checked 24 October 2026
  29. Sanity Docs, How early access workssanity.io · checked 24 October 2026
  30. Sanity Learn, Uploading assets efficientlysanity.io · checked 24 October 2026
  31. Sanity blog, August releasessanity.io · checked 24 October 2026
  32. Sanity on GitHub, sanity-migration skillgithub.com · checked 25 October 2026
  33. Drupal.org, Drupal 8.7.0 release notesdrupal.org · checked 25 October 2026