Migration
How do you migrate from WordPress to Sanity?
Short answer
A WordPress to Sanity migration is a script you write, not a plugin you install. Sanity's course imports posts, pages and images and converts HTML to Portable Text, Sanity's JSON format (Sanity Learn; its block-tools lesson, October 2026). Page builder, SEO (search engine optimization) and ecommerce plugin data stay yours, so they set the effort (Sanity Learn, October 2026).
- Official path
- Sanity Learn course: a scripted importPulls posts, pages, terms, users and images from the WordPress API; Sanity argues against a one-size-fits-all importerSource: Sanity Learn (Migrating content from WordPress to Sanity: Introduction)
- Growth document quota
- 25,000 included, 50,000 with the add-onThe add-on is $299 a month. Image and file assets don't count; a draft with unpublished changes counts twice; Growth blocks new documents at the capSources: Sanity (Plans that scale with you); Sanity Docs (Technical limits, Plans and payments)
- Rich text converter
- htmlToBlocks in @portabletext/block-toolscontent.rendered for Classic editor HTML; content.raw from an authenticated edit request to keep Gutenberg blocksSources: Sanity Learn (Converting HTML to Portable Text, Converting WordPress blocks to Portable Text)
- Roles on Growth
- 5 built-in roles, none matches AuthorAdministrator, Editor, Viewer, Developer and Contributor; a publish-your-own-posts role needs Enterprise custom rolesSources: Sanity Docs (Roles and permissions); Sanity (Plans that scale with you)
Deploi’s call
Move from WordPress to Sanity one section at a time behind the same domain, rerunning the import script against live content until each section launches. Move the whole site at once only when it is mostly Classic editor content that one launch can replace. Whether to leave WordPress at all is a separate decision; make it first.
PhaseMove from WordPress to Sanity one section at a time when
- You've already decided to leave WordPress for a structural reason, such as content that feeds several channels, and want the lowest-risk way to move.
- The site has several sections, such as a blog, product pages and docs, so each can launch behind the same domain while the rest stays on WordPress.
- Gutenberg blocks or page builder layouts need remodeling, because a section-by-section move keeps each rebuild small enough to test.
- Editors keep publishing during the move, so the import script reruns against live WordPress content with fixed document IDs until each section launches.
- Your document count fits the Sanity plan you'll pay for, because Growth includes 25,000 documents with no overage, or 50,000 with the $299 a month add-on, as of October 2026.
MigrateMove the whole WordPress site to Sanity at once when
- Most content is Classic editor posts and pages, so htmlToBlocks converts it with few custom rules.
- One new front end replaces the whole theme in a single launch, with a redirect ready for every changed URL.
How hard is a WordPress to Sanity move?
A WordPress to Sanity move is as hard as your content and team make it: Classic editor posts convert cleanly and page builder layouts need rebuilding. Sanity's WordPress course argues against "one-size-fits-nobody" importers, so it builds the script with you (Sanity Learn, Introduction, October 2026). Sanity's MCP (Model Context Protocol) server lists a guided WordPress migration path (Sanity's blog, October 2026). Sanity's migration skill for AI (artificial intelligence) agents has a WordPress guide (Sanity on GitHub, October 2026), "agent-scaffolded, not fully automated" in Sanity's words (Sanity, What's new in August, October 2026).
A PHP developer team "may need to up-skill with JavaScript to configure the Studio", though a PHP front end can use Sanity's PHP client, and content creators "absolutely" need training (Sanity Learn, Planning a re-platforming project, October 2026).
| Content | Difficulty | Why |
|---|---|---|
| Classic editor posts and pages | Low | htmlToBlocks converts them by default |
| Gutenberg block editor layouts | Medium | Needs content.raw and a type per kept block |
| Elementor, Divi or Beaver Builder pages | High | Messy HTML; the course warns of a bad migration |
| WooCommerce or membership plugins | High | Checkout and accounts move elsewhere |
Whether to leave WordPress is a separate, earlier decision.
Will your WordPress site fit in a Sanity plan?
A WordPress site fits Sanity's Growth plan if its posts, pages, terms, authors and redirects total fewer than 25,000 documents, or fewer than 50,000 with a $299-a-month quota add-on (Sanity pricing, October 2026). The 50,000 figure is Growth's hard ceiling (Technical limits, October 2026). Image and file assets don't count toward the quota. A draft with unpublished changes counts as a second document, and the Free and Growth plans block new documents at the limit instead of billing overages (Plans and payments, October 2026).
Count before you choose a plan. The WordPress API (application programming interface) returns an x-wp-total header on each content route, so one request per type gives that type's public total (Sanity Learn, Find your WordPress API, October 2026). The course's example output shows 21,492 posts, which alone fills 86% of Growth's included documents (Deploi arithmetic from the figures above). Add terms, authors, redirect documents and drafts with unpublished changes, and a site that size is likely to need the add-on or an Enterprise quote. What Sanity's Free and Growth plans include compares the two.
How do WordPress posts, pages, custom post types and ACF fields map to Sanity?
WordPress content maps to Sanity document types one to one at first, then gets restructured. Sanity's WordPress course creates post, page, category, tag and author types that mirror WordPress API (application programming interface) responses (Sanity Learn, Preparing a Studio and schema types, October 2026). Advanced Custom Fields (ACF) data reaches that API only for field groups set to Show in REST (Representational State Transfer) API, and ACF's Message, Accordion and Tab fields aren't supported there (ACF documentation, October 2026).
| WordPress object | Sanity equivalent | Notes |
|---|---|---|
| Post | post document | Keep slug, date, modified, status and sticky as fields |
| Page | page document | Template pages become person or location documents |
| Custom post type | Its own document type | Name it for what it is |
| Category and tag | category and tag documents | Posts hold arrays of references |
| User shown as author | author document | Guest authors need no Sanity login |
| ACF field group | Fields or objects on the document type | Turn on Show in REST API first |
| Featured image | image field | Reuse the WordPress media ID |
How do Gutenberg blocks and classic HTML become Portable Text?
Classic editor HTML becomes Portable Text, Sanity's JSON rich text format, through htmlToBlocks from @portabletext/block-tools, and Gutenberg, WordPress's block editor, converts from its raw block markup into one Portable Text object type per block you keep. htmlToBlocks turns headings, lists and paragraphs into Portable Text blocks without configuration (Sanity Learn, Converting HTML to Portable Text, October 2026). Images inside the HTML need a custom rule: the course stores each image URL as a placeholder block, then searches WordPress media by filename, which isn't guaranteed to find it.
The public content.rendered field flattens blocks into final HTML, so a two-column layout arrives as one column of text. An authenticated request with context=edit returns content.raw, which keeps the block markup. The @wordpress/block-serialization-default-parser package splits it into blocks, and each kept block, such as columns, gets its object type (Sanity Learn, Converting WordPress blocks to Portable Text, October 2026). Log every unhandled block name, because each one is a schema decision.
Page builders are the hard case. Sanity's course warns that sites built with Elementor, Divi or Beaver Builder "will have a bad time with the migration", because their HTML is messy (Sanity Learn, Converting HTML to Portable Text, October 2026). How Portable Text stores rich text explains the target format.
How do you keep WordPress URLs and SEO metadata in a move to Sanity?
Keep WordPress URLs with a redirect for every path that changes, and keep search engine optimization (SEO) metadata by reading each SEO plugin's API (application programming interface) output into a Sanity seo object. Keep every WordPress slug, crawl the live site, and store a redirect document for each path that changes (Sanity Learn, re-platforming course, October 2026). Vercel caps configuration redirects at 2,048 (Vercel docs, October 2026). The Sanity import also resets _updatedAt on every document that references an asset or another document (Sanity Docs, Import data, October 2026), so don't use it as a post's sitemap date. Keeping search traffic through a move to Sanity covers both.
Yoast SEO adds yoast_head and yoast_head_json to WordPress API responses, and the JSON version gives each tag as a key and value (Yoast developer docs, October 2026). Rank Math, once headless support is on, returns only finished head HTML from its getHead endpoint, so you parse the tags out yourself (Rank Math knowledge base, October 2026). Map both into an seo object whose fields override the page's title and description and fall back to them when empty (Sanity Learn, SEO schema types and metadata, October 2026). The SEO risks of going headless lists what else breaks.
Which WordPress plugins need a replacement in Sanity?
Most WordPress plugins that write content or serve visitors need a replacement or a rebuild. Sanity's WordPress course imports what the WordPress API (application programming interface) exposes and leaves page builder, search engine optimization (SEO) and ecommerce plugin data to you (Sanity Learn, Introduction, October 2026). Site search moves to GROQ (Graph-Relational Object Queries) match and score() (Search text content with GROQ, October 2026).
| WordPress plugin | Sanity replacement | What moves |
|---|---|---|
| Forms: Contact Form 7, Gravity Forms | A form service or an API route | Field definitions and exported submissions |
| SEO: Yoast SEO, Rank Math | An seo object and redirect documents | Titles, descriptions, canonical tags |
| Multilingual: WordPress Multilingual (WPML), Polylang | The document internationalization plugin | Each translation, linked |
| WooCommerce | A commerce platform, such as Shopify with Sanity Connect | Editorial content |
| Memberships | An identity or membership service | Accounts stay outside Sanity |
Polylang links each translated post in a translations field, which matches Sanity's document-level localization (Sanity Docs, Localization, October 2026). The API returns that field only in paid Polylang plans (Polylang documentation; Polylang pricing, October 2026).
What happens to WordPress users, roles and editorial workflow?
WordPress users split in two: people shown as bylines become author documents, and people who edit become Sanity project members with roles. Sanity doesn't copy member data into documents, so guest authors need no login (Sanity Learn, Preparing a Studio and schema types, October 2026). WordPress ships six roles (WordPress, Roles and Capabilities, October 2026). Sanity's Growth plan offers five built-in roles, and custom roles are Enterprise only (Sanity Docs, Roles and permissions, October 2026; Sanity pricing).
| WordPress role | Sanity role on Growth | Gap |
|---|---|---|
| Administrator | Administrator | None |
| Editor | Editor | None |
| Author | Editor or Contributor | No built-in role limits publishing to your own posts |
| Contributor | Contributor | Writes any draft, not only their own; an Editor publishes |
| Subscriber | None | Site visitors aren't Sanity users |
A publish-your-own-posts rule is possible on Enterprise, because custom roles can filter documents by the signed-in user's ID. Migrated bylines are author documents, not users, so the filter only works if each document also stores its editor's user ID (Deploi's judgment from Sanity Docs, Roles and permissions). What each Sanity role can do covers the full matrix.
What is the step-by-step plan for a WordPress to Sanity migration?
A WordPress to Sanity migration runs in seven steps, and the import reruns until launch. WordPress serves live content through its API (application programming interface), so reruns with fixed document IDs minimize any content freeze (Sanity Learn, Migrating without downtime, October 2026). Move low-risk content first (Sanity Learn, re-platforming course, October 2026).
Migrating WordPress to Sanity
- Inventory. Count each type with
x-wp-total, crawl live URLs, list content plugins. Owner: developer. - Model. Type content by what it is, not how it looks; review with editors. Owner: developer and content lead.
- Script the import. Use the
sanity migrationscommand-line interface (CLI), which dry-runs until you pass--no-dry-run(Sanity Docs, Migrations CLI). Owner: developer. - Convert rich text. Run
htmlToBlocks, type each kept Gutenberg block, log the rest. Owner: developer. - Upload assets. Send images with alt text, captions and source IDs in small batches (Sanity Learn). Owner: developer.
- Rebuild metadata and redirects. Fill
seoobjects from Yoast or Rank Math; redirect changed paths. Owner: search lead. - Validate and cut over. Run
sanity documents validate(Sanity Docs), confirm each old URL resolves or returns a 301, then switch. Owner: developer.
Deploi’s point of view
- Where we disagree
- The common pitch sells the move as a plugin-free future. Plugins don't vanish: forms, search, memberships and checkout become separate services you choose, pay for and connect, and Sanity Studio has plugins of its own. The real gain is content stored as data that any channel can query, and that only pays off if you remodel during the move instead of copying pages one to one.
- What we’ve seen
- We load our own content into Sanity with the discipline a WordPress migration needs: fixed document IDs, unpublished drafts first, weak references so import order doesn't matter, and an export to restore from. The first wave of deploi.ca's Shopify data section went in during October 2026 as one package of 406 documents and 159 images, with count checks and a rollback by document ID list.
- Martin's take
- Martin Dejnicki has worked with content management systems for more than 20 years and built and managed hundreds of WordPress sites between 2005 and 2017. What he doesn't miss is chasing plugin compatibility across dozens of sites and hundreds of plugins at the same time. He moved on from WordPress in 2017.
- What we'd tell you to do first
- Before any schema work, get two numbers: an x-wp-total count for every content type, and a crawl of every live URL. The first picks the Sanity plan; the second sets the redirect method and the size of the job. Get both before you price anything.
When this answer changes
- Sanity changes the Growth document quota, the $299 quota add-on or the 50,000 ceiling
- Sanity publishes an official WordPress importer, retires the WordPress course or changes its migration skill's WordPress guide
- WordPress changes how Gutenberg stores blocks in content.raw, or @portabletext/block-tools changes htmlToBlocks
- Yoast SEO or Rank Math change their headless API output
- Sanity adds an own-documents role to the Growth plan
- Vercel changes its 2,048 configuration redirect limit
Sources
Every source opened and checked on 25 October 2026.
- Sanity Learn, Migrating content from WordPress to Sanity: Introduction
- Sanity Learn, Find your WordPress API
- Sanity Learn, Preparing a Studio and schema types
- Sanity Learn, Uploading assets performantly
- Sanity Learn, Converting HTML to Portable Text
- Sanity Learn, Converting WordPress blocks to Portable Text
- Sanity Learn, Re-modelling your existing content
- Sanity Learn, Migrating without downtime
- Sanity, Plans that scale with you
- Sanity Docs, Technical limits
- Sanity Docs, Plans and payments
- Sanity Docs, Localization
- Sanity Docs, Roles and permissions
- Sanity Learn, SEO schema types and metadata
- Sanity Learn, Set up forms, redirects, and sitemap
- Sanity Docs, Search text content with GROQ
- Sanity Docs, Sanity Connect for Shopify
- Sanity Docs, Migrations CLI command reference
- Sanity Docs, Migrating your schema and content
- WordPress Developer Resources, REST API Handbook: Posts
- WordPress Documentation, Roles and Capabilities
- Yoast, Yoast SEO: REST API
- Rank Math, How to Enable Headless CMS Support in Rank Math?
- ACF, WP REST API Integration
- Polylang, REST API
- Polylang, Pricing
- Vercel, Configuration Redirects
- Sanity on GitHub, sanity-migration skill
- Sanity, What's new in August
- Sanity Docs, Import data
- Sanity, You’ll need a CMS eventually. Let your agent set it up.
- Sanity Learn, Planning a re-platforming project