← All playbooks
CMS migration

WordPress → Sanity

Move WordPress posts, pages, authors, categories, tags, and media into Sanity with a CLI migration script over the REST API. Convert HTML or Gutenberg blocks to Portable Text. Rebuild the frontend. Page builders, SEO plugins, and ecommerce are out of Sanity's published course unless they appear on the API.

Typical timeline15–35 business days70–140 hours active work
Statusneeds review
Source profileWordPress export screen and REST API reviewed 2026-10-29
Destination profileSanity WordPress migration course reviewed 2026-10-29
Documentation date
Review window
Evidence3 sources
This route needs review.Use it as a planning baseline, then verify the dated sources and test with representative data before production.
Before you migrate

Should you make this move?

Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.

Current platform

WordPress

Reasons to stay
  • Unmatched ecosystem breadth and content ownership support almost any publishing model
  • Editors get a purpose-built publishing workflow instead of raw files
Reasons to leave
  • Plugin, hosting, security, and performance maintenance accumulate over time
  • Themes, extensions, and content models add maintenance and portability constraints
New platform

Sanity

What gets better
  • Content Lake, schema-as-code, and customizable Studio support sophisticated structured content
  • Editors get a purpose-built publishing workflow instead of raw files
What gets worse
  • GROQ, schema code, and custom editorial tooling demand engineering ownership
  • Themes, extensions, and content models add maintenance and portability constraints
Best of the move

Sanity: Content Lake, schema-as-code, and customizable Studio support sophisticated structured content. This removes a major source-side concern: Plugin, hosting, security, and performance maintenance accumulate over time.

Worst of the move

What you lose: Unmatched ecosystem breadth and content ownership support almost any publishing model. What you inherit: GROQ, schema code, and custom editorial tooling demand engineering ownership.

Jump to a section
01At a glance

Know the shape of the move.

Transfer outcome12 features audited
Transfer outcome distributionClean transfer: 0, Partial transfer: 4, Manual rebuild: 6, Not transferred: 2.
Clean0
Partial4
Manual6
Lost2
Mapping route9 of 9 fields have a destination path

This timeline assumes

  • Editorial planning scale: up to 2,500 posts and pages, 20 authors, and 10 GB of media that the REST API can list.
  • Sanity's course uses /wp-json/wp/v2 and the CLI Migration tool. It does not convert Elementor, Divi, Yoast, or WooCommerce unless those values are on the API.
  • You will build a new frontend. Sanity is not a WordPress theme host.
  • WordPress stays authoritative until the Sanity dataset and the new site pass URL, SEO, and editor checks.
  • We record evidence for every blocking verification check.
02What transfers

What survives the move.

“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.

ItemOutcomeImpactWhat happensMitigation
Posts, pages, users, categories, and tagspartialcriticalSanity's course imports those types through the REST API, creates references, and uploads assets. Custom post types need extra script work.Write schema types first. Dry-run the migration script. Reconcile counts by type.
HTML and Gutenberg blockspartialcriticalThe course converts Classic or Block editor HTML into Portable Text. Custom blocks need schema types you write.Pick rendered HTML or raw context=edit, then convert a sample of every block type.
Page builders and plugin HTMLlostcriticalSanity says the course does not handle Elementor, Divi, SEO, or ecommerce content except what the REST API exposes.Inventory builders. Rebuild those pages as structured documents or keep them out of scope.
Media librarypartialcriticalAttachments can upload into the Content Lake. Image styles, unused crops, and off-library files do not.Upload from attachment records, keep filenames, and check a sample of inline images in Portable Text.
Themes, plugins, and PHPlostcriticalNone of that runs in Sanity.Replace each runtime feature with Studio, a frontend, or an external service.
URLs and redirectsmanualcriticalSanity documents do not keep WordPress permalinks by themselves. Field reports keep finding plugin routes months later.Crawl the live site, map every public URL, and ship redirects with the frontend.
SEO plugin data and JSON-LDmanualhighYoast and similar plugins inject titles, canonicals, and schema that vanish when the theme dies.Read plugin fields from the API if they are exposed, then store SEO fields on the Sanity document and render them yourself.
Comments and revisionsmanualmediumThe course says you can learn to import comments. Revisions are not a promised Studio history.Decide whether comments move, stay on WordPress, or go to a new system.
Menus, widgets, and theme optionsmanualhighThose live in theme_mod and options, not in a portable post.Recreate navigation as Sanity documents or frontend config.
Users and application passwordspartialhighAuthors can import as documents. WordPress logins do not become Sanity logins. Raw block conversion needs an application password and context=edit.Invite Studio users separately. Store the application password only for the import.
REST API accessmanualcriticalSecurity plugins often block /wp-json/wp/v2. The course tells you to confirm the route first.curl the posts endpoint and fix the block before you write the script.
Frontend and hostingmanualcriticalSanity does not publish a public site by itself.Ship the new frontend and cut DNS only after previews and redirects pass.
03Field and feature mapping

Where each thing goes.

SourceDestinationMethodNotes
postSanity document type you definetransformKeep the WordPress id as a source field for reruns.
pagepage documenttransformMap parent pages if you still need hierarchy.
category / tagcategory and tag documents plus referencestransformCreate terms before posts so references resolve.
authorauthor document or Sanity user referencetransformStudio login is separate.
featured image / attachmentSanity image assettransformUpload once and reuse the asset id.
post_content HTMLPortable TexttransformConvert Classic or Gutenberg; custom blocks need schema.
permalinkslug plus a redirectmanualDo not assume the frontend path matches.
Yoast or SEO plugin fieldsSEO fields on the documentmanualOnly if the API exposes them.
custom post typeextra document typemanualThe published course does not import these for you.
04Before you begin

Make the move recoverable.

Backup procedure

Create the source-of-truth backup

Keep a WordPress export and a URL crawl before the script writes to Sanity.

  1. Run Tools → Export for posts, pages, and media as a legal archive.
  2. Crawl public URLs, titles, and status codes.
  3. Confirm /wp-json/wp/v2/posts responds.
  4. Inventory post types, plugins, builders, and SEO fields.
  5. Create the Sanity project, dataset, and schema types.

Proof to capture: A dated manifest lists export files, crawl counts, API access, and schema types.

Transformation · npx sanity@latest migration create "Import WP"

CLI migration script

Follow Sanity's recommended bulk path with dry runs.

  1. Skip document types in the prompt and replace the template.
  2. Query the REST API and return createOrReplace mutations so reruns are safe.
  3. Dry-run, then run with --no-dry-run only after counts look right.

Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.

Transformation · Sanity block conversion plus custom schema types

Portable Text conversion

Turn HTML or raw blocks into documents editors can use.

  1. Convert a sample of Classic, Gutenberg, embeds, and galleries.
  2. Store unknown blocks as signed exceptions, not silent drops.
  3. Preview each type in Studio.

Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.

05Handle with care

The things most likely to hurt.

These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.

Inventory

The REST API is blocked

criticalpossible likelihood

/wp-json/wp/v2/posts does not return posts.

Consequence
The script imports nothing or a login page.
Mitigation
Fix the security plugin or use an application password before you write mutations.

Stop if: The posts endpoint is not readable.

Pilot

Page-builder HTML becomes unusable Portable Text

criticalpossible likelihood

A sample page is a soup of shortcodes or builder markup.

Consequence
Editors cannot edit the page in Studio.
Mitigation
Rebuild those templates. Do not call the import done.

Stop if: A critical template has no structured destination.

Cutover

Redirects miss plugin routes

highpossible likelihood

Search Console later shows URLs the crawl never saw.

Consequence
SEO drops on URLs nobody mapped.
Mitigation
Crawl, then watch Search Console after launch and add redirects.

Stop if: A mapped money URL 404s after DNS cutover.

06Precise timeline

Do the work in this order.

Estimate forEditorial estimate for up to 2,500 posts and pages plus a new frontend
Total elapsed15–35 business days
Active work70–140 hours
BufferAdd time for page builders, multilingual plugins, or a large unused media library.
01
Days 1–5Inventory and schema3–5 days
02
Days 4–7Export and crawl1–3 days
03
Days 7–20Script and frontend8–14 days
04
Days 20–28Full import and DNS2–6 days
05
Days 26–35Observe and close6–10 days
  1. Days 1–5 · inventory

    Inventory and schema

    10–18 hours active3–5 days elapsedEditor review waiting
    • Crawl URLs and list post types.
    • Confirm the REST API.
    • Write Sanity schema for the types you will import.

    Depends on: WordPress and Sanity admin access

    Stop / go checkpoint

    Export?

    Go when: API, schema, and URL map have owners.

    Stop when: A critical builder page has no plan.

  2. Days 4–7 · backup

    Export and crawl

    4–8 hours active1–3 days elapsedExport download waiting
    • Download the WordPress export.
    • Store the crawl.
    • Create the dataset.

    Depends on: Approved inventory

    Stop / go checkpoint

    Script?

    Go when: Export and crawl counts match the site.

    Stop when: The export is empty or the API is blocked.

  3. Days 7–20 · pilot

    Script and frontend

    30–70 hours active8–14 days elapsedEditor preview waiting
    • Dry-run the import.
    • Convert Portable Text samples.
    • Build routes, previews, and SEO fields.

    Depends on: Verified backup

    Stop / go checkpoint

    Scale?

    Go when: Pilot documents, assets, and templates pass editor review.

    Stop when: A critical block or URL is wrong.

  4. Days 20–28 · cutover

    Full import and DNS

    10–25 hours active2–6 days elapsedDNS and cache waiting
    • Run the production import.
    • Ship redirects.
    • Switch DNS after smoke checks.

    Depends on: Passed pilot

    Stop / go checkpoint

    Open production?

    Go when: Counts, redirects, and the homepage match the crawl.

    Stop when: A money URL 404s.

  5. Days 26–35 · observe

    Observe and close

    8–14 hours active6–10 days elapsedSearch Console waiting
    • Watch 404s and editor workflow.
    • Keep WordPress read-only.

    Depends on: Verified cutover

    Stop / go checkpoint

    Close rollback?

    Go when: No trigger fires through one publishing cycle.

    Stop when: Indexed URLs fail or editors cannot publish.

07The point of change

Cut over with a way back.

Go live

Cutover

Make Sanity plus the new frontend the only public site after a reconciled import.

Recommended window: A weekday with DNS, SEO, and editor owners available.

  1. Freeze WordPress publishing.
  2. Run the production migration script and reconcile counts.
  3. Publish the frontend with the redirect map.
  4. Switch DNS after smoke checks on money URLs.
  5. Keep WordPress recoverable.

Proof to capture: The new host serves the site. Counts match. Mapped URLs resolve. Editors can publish in Studio.

Return to safety

Rollback

Return the public site to WordPress without losing reviewed Sanity-era edits.

Deadline: Before you delete the WordPress host or the export.

  1. Stop publishing on the new frontend.
  2. Point DNS back at WordPress.
  3. Export Sanity documents created after cutover.
  4. Apply only reviewed content back to WordPress if needed.
  5. Run the same URL checks on WordPress.

Proof to capture: WordPress serves the public site again and the new frontend is not receiving production traffic.

Rollback immediately when
  • Money URL 404
  • Import count mismatch
  • Unusable builder content
  • Editor cannot publish
  • API was blocked mid-import
08Verification report

Prove the migration worked.

Every blocking check must pass. Capture the evidence before cleanup begins.

0%
Interactive report preview0 / 8 checks passed
PassIDCheckMethodExpected resultEvidence
V-01BlockingREST accessGET /wp-json/wp/v2/posts and confirm a post list.JSON posts, not a block page.curl headers.
V-02BlockingType countsCompare WordPress counts to Sanity documents by type.Every scoped type matches or has a signed exception.Count sheet.
V-03BlockingPortable Text samplesOpen Classic, Gutenberg, embed, and gallery samples in Studio.Editors can edit each approved type.Studio screenshots.
V-04BlockingImage durabilityOpen featured and inline images as ordinary Studio users.Assets resolve without WordPress URLs.Asset manifest.
V-05BlockingRedirect mapRequest every money URL from the crawl.200 or a single 301 to the signed destination.Crawl diff.
V-06BlockingTitles and canonicalsCompare rendered title, description, and canonical on key URLs.Approved values, not empty plugin leftovers.SEO sheet.
V-07BlockingEditor pathCreate, preview, and publish one document.The frontend updates without WordPress.Publish log.
V-08BlockingWordPress exportConfirm the WXR export and host still boot.You can point DNS back.Rollback note.
09Post-migration cleanup

Remove the scaffolding safely.

Safe after: One publishing cycle, a Search Console pass, and at least seven stable days.

  1. Archive the WXR export, crawl, and migration logs.
  2. Revoke the WordPress application password.
  3. Keep WordPress read-only through the retention window.
  4. Remove leftover WordPress embeds only after redirects are stable.
  5. Document Studio roles and the frontend deploy.
  6. Schedule the next schema and redirect review.
10Experiences from the field

When the plan met reality.

First-hand accounts are preferred. Vendor case studies are labeled, and every note below is an editorial paraphrase—follow the link for the full context.

First-person accountOneOffObject

From WordPress to Sanity and Astro: Rebuilding My Personal Site2026

OneOffObject left a long-running WordPress site in May 2026 for Sanity, Astro, and Cloudflare Pages. A Node script turned the WXR export into NDJSON: 167 posts, 166 main images, 499 body images, and 625 image URLs, with no unresolved images at the end. Affiliate embeds and tables stayed as htmlEmbed blocks. Article slugs stayed at /{slug}/. Email moved to Sakura first, then DNS switched on May 13. WordPress went offline on May 17.

What they recommend
  • Convert what editors need as Portable Text, and keep awkward HTML as a bounded embed instead of forcing every table into blocks.
  • Move mail DNS before the website cutover if the old host still owns MX records for the same domain.
Worth noticing
  • The work was a rebuild, not a theme swap. Static pages later left Sanity and became Astro files.
  • Keeping post slugs and splitting email from web DNS mattered more than a perfect Portable Text conversion.
Sources and evidence

Verify against the primary material.

Platform behavior changes. Check these sources and the review dates above before executing a production migration.

  1. Sanity Learn: migrating content from WordPressAccessed 2026-10-29
  2. WordPress: Tools Export screenAccessed 2026-10-29
  3. WordPress: REST API handbookAccessed 2026-10-29