To migrate WordPress content to Sanity or Strapi, you reshape it into structured JSON that matches the headless CMS’s schema, rather than trying to import the raw WordPress XML, which neither accepts. The flow is: pull the content from the REST API or a crawl, convert it to the target’s content types, import it, and connect a front end. Keep the slugs and metadata so SEO carries over. WPBuildAI exports WordPress as clean, schema-ready data for Sanity, Strapi, or another headless CMS.
Why the XML does not import
The mismatch is between what WordPress exports and what a headless CMS expects. Sanity and Strapi store content as structured documents defined by your schema, and they expect imports shaped to that schema, typically as JSON with named fields. WordPress XML (WXR) is the opposite: a backup format full of serialized PHP fields, shortcodes, and WordPress-specific structure, meant for moving between WordPress installs where another WordPress decodes it. There is no built-in path that maps WXR’s structure onto Sanity’s or Strapi’s document model, so a direct import either fails or produces unusable content full of shortcode text. The 2024 Web Almanac shows how much shortcode and theme markup a typical WordPress post carries, all of which has to be stripped and reshaped before it maps into clean fields. So conversion comes first, always: the question is never “how do I import the XML” but “how do I reshape my content into the target’s schema.”
What Sanity and Strapi expect
Understanding the target shapes the whole migration, because headless CMSs are schema-first. In Sanity or Strapi, you define content types (also called schemas or document types) before you have content: a post type with fields like title, slug, body, excerpt, publishedAt, author, and categories; a page type; an author type; a category type. Each piece of content is then a document conforming to one of those types. This is different from WordPress, where the structure is more implicit and extended by plugins. So importing into a headless CMS means producing content that matches these defined types, one JSON object per item with fields named exactly as the schema expects. The headless model’s strength, clean, typed, structured content, is also what makes the import a reshape: you are translating WordPress’s loose, plugin-extended structure into the headless CMS’s strict, defined one, field by field.
Design the import around the schema
The reliable approach is to define the schema first, then shape the export to fit it:
- Define your content types in Sanity or Strapi, post, page, author, category, with the fields you need.
- Pull the WordPress content from the REST API or a crawl.
- Reshape it to the schema: one JSON object per item, fields named to match, slugs preserved, body cleaned of shortcodes and theme markup, media as references.
- Import the schema-matched JSON into the CMS.
- Connect a front end that renders the content.
The Generative Engine Optimization study is a reminder that clean, well-structured data is what every downstream system handles best, and a schema-matched export is exactly that. Mapping the export to the schema is the real work; once each field lines up, the import is a direct load. This is the same reshape-to-target discipline as exporting WordPress data for a structured backend and migrating a WordPress database to Supabase.
A worked example: a blog into Strapi
Picture moving a 300-post WordPress blog into Strapi. First you define Strapi content types: a post (title, slug, body as rich text or Markdown, excerpt, publishedAt, a relation to author and categories), an author, and a category. Then you pull the 300 posts from the WordPress REST API, page by page, and reshape each into a JSON object matching the post type: the title and slug carried straight across, the body converted from WordPress’s shortcode-laced HTML to clean rich text, the author and category linked as relations, and the featured image noted for re-hosting. You import the 300 schema-matched objects into Strapi, where each becomes a post document, and connect a front end that renders them by slug. Because the slugs were preserved, the URLs match the old blog; because the body was cleaned, the content is free of shortcode residue. The reshape to the schema was the work; the import itself was a straightforward load of well-formed data.
Pulling the content: REST API or crawl
There are two ways to get the content out of WordPress, and you pick by the situation. The REST API is the cleanest source when available: it returns posts and pages as structured JSON with named fields, which is already close to what you reshape into the target schema, and it lets you pull exactly the fields you need. Page through it to capture everything, and watch for timeouts on large or slow sites by requesting in batches. The alternative is a crawl of the rendered site, which captures the content as it appears, useful when the API is disabled or when the rendered output (with shortcodes already expanded) is cleaner than the stored body. Either way, the goal is the same: clean content you then reshape to the schema, not raw XML. The blog-to-JSON mechanics overlap with getting a WordPress blog into JSON for a front end, since both produce structured content from WordPress, differing mainly in the target shape.
Handling media references
Images need their own handling, or they break after the move. In WordPress, media lives in the media library and posts reference it by URL. When you migrate content to Sanity or Strapi, you have two choices for media: upload the images into the headless CMS’s own asset store (Sanity, for instance, manages assets natively), or host them externally and reference them. Either way, do not leave the imported content pointing at the old WordPress media URLs, because those break when the old site is retired. So part of the reshape is resolving each media reference: download the image, upload or host it for the new system, and update the reference in the content to the new location, carrying the alt text along for accessibility and image search. Treating media as part of the schema (a field or an asset reference) rather than as inline URLs is what keeps the migrated content’s images working in the long run, the same media-re-hosting discipline any clean migration needs.
The SEO half: front end and redirects
A crucial distinction: migrating content into a headless CMS moves the content, but a headless CMS has no front end of its own, so SEO depends on what you build in front of it. Two things protect rankings. First, the front end must be server-rendered, so crawlers and AI engines see the content in the HTML rather than an empty shell, the failure Google describes in its JavaScript SEO basics; a headless setup that renders content only in the browser hides it from search. Second, preserve the URLs, keep slugs so most URLs match, and 301 any that change, per Google’s site move guidance. The CMS holds the content; the front end and the redirects hold the rankings. That front-end side is the work in making a site headless. So a headless migration is really two projects: the content reshape-and-import (into Sanity or Strapi) and the SEO-safe front end (server-rendered, URLs preserved), and both are needed.
Common mistakes migrating to a headless CMS
The recurring errors come from skipping the reshape or the SEO front end. Trying to import the raw WordPress XML into Sanity or Strapi fails, because it is the wrong shape. Exporting the body with shortcodes and theme markup intact fills the new CMS with noise. Not defining the schema first means there is nothing for the import to map to. Leaving media referenced on the old WordPress domain breaks images later. Rendering the front end client-side hides the content from crawlers. And changing URLs without redirects strands rankings. Each is avoided by the schema-first approach: define the content types, reshape clean content to match, re-host media, then build a server-rendered front end with preserved URLs and redirects. The content move and the SEO front end are distinct, and a successful headless migration needs both done deliberately.
Key points to remember
Migrate WordPress content to Sanity or Strapi by reshaping it into structured JSON that matches the headless CMS’s schema, never by importing raw WordPress XML, which is a backup format the headless CMS cannot map. Define your content types first (post, page, author, category), then pull the content from the REST API or a crawl, clean the body of shortcodes, name the fields to the schema, preserve slugs, and re-host media as asset references rather than old WordPress URLs. Remember a headless CMS has no front end, so SEO depends on building a server-rendered front end that crawlers can read and on preserving URLs with 301s for any that change, the content move and the SEO front end are two distinct, necessary jobs. WPBuildAI exports schema-ready data and maps the URLs, so the move into a headless CMS keeps its content and rankings; send your site URL for a fixed quote.
Not affiliated with Sanity, Strapi, WordPress, or Lovable.