WordPress to Sanity: A Composite Migration Case Study

Before you get invested: this is a composite, not a real company with a real logo. Nobody sat me down and told me this story. It's the same story I've watched play out across a dozen content-team replatforming projects, stitched into one so I don't have to keep saying "at one client we worked with." The numbers are typical ranges, the kind you'd nod along to, not one company's audited figures. Treat it as a pattern, not a receipt.

Open a WordPress article that's been live for eight years and scroll to where the writer dropped in a photo gallery. What you'll find isn't a gallery. It's a cryptic little string in square brackets, something like , a note that only WordPress and one specific plugin ever knew how to read aloud. Now multiply that by a whole archive, throw in the inline styles, the third-party embeds, the tables somebody built by hand in 2016, and you start to see the shape of the thing.

Here's why that's worth stopping over. Everyone approaches WordPress to Sanity with the same comforting sentence in their head: export the posts, import the posts, done by Friday. I have watched that sentence walk teams straight off a cliff, because the two systems don't even store an article the same way. WordPress keeps the body as one blob of markup; Sanity wants structured JSON. That gap isn't a step in the project. It is the project. So let's take a composite media brand through the move and look at exactly where the floor gives way.

Context

Picture a content-driven media brand on WordPress — a large archive of articles, a busy editorial team, and a growing need to publish the same content to a website, a mobile app, and a newsletter. WordPress had served the website well, but the team wanted structured content they could deliver anywhere, a faster front end, and an editing experience built around their real workflows. Sanity, paired with a Next.js front end, was the destination.

WordPress to Sanity looks straightforward — export the posts, import them — and that assumption is exactly where it goes wrong. WordPress stores article bodies as a single blob of HTML (classic editor) or block markup (Gutenberg); Sanity stores content as Portable Text, a structured JSON representation. The gap between those two is the whole project.

What broke

The failures concentrated in the content itself and the plugin ecosystem around it.

HTML to Portable Text

Years of WordPress authoring had produced bodies full of shortcodes, plugin-injected markup, inline styles, and embedded media. Converting that to clean Portable Text is a transformation, not an import, and it is lossy by default: custom shortcodes, galleries, tables, and third-party embeds do not have obvious equivalents. The team that ran a naive HTML-to-Portable-Text conversion shipped articles with missing embeds and mangled formatting until they built a mapping for their specific patterns and reviewed the edge cases by hand.

Plugins have no home

The SEO plugin’s per-post titles and meta descriptions, the custom-fields plugin’s structured data, and the forms plugin all lived inside WordPress. Sanity is a content backend with no plugin layer, so each of those became something the team had to model or rebuild: SEO fields as first-class schema, forms as a separate service, structured data as explicit content types. Losing the SEO metadata in the move would have quietly erased carefully tuned titles and descriptions across the archive.

Media and links

The media library moved to Sanity’s asset pipeline, which meant every image and file URL changed. In-body links and image references that pointed at /wp-content/uploads/… had to be rewritten during the transformation, or they would break at cutover.

URLs, redirects, and the editor

WordPress permalinks mapped to new Next.js routes, so a full 301 map was required to hold rankings. And editors who lived in the WordPress admin had to learn Sanity Studio — a different, more structured way of working. Without a tailored Studio and preview setup, the editorial team’s productivity would have dropped at exactly the wrong moment.

What worked

Success came from treating the body conversion as the core engineering task. The team wrote a transformation that mapped their actual shortcodes and block patterns to Portable Text and custom types, then reviewed a representative sample rather than trusting the whole archive blindly. They modeled SEO fields and reusable components explicitly in the schema. They customized Sanity Studio to match editorial workflows and wired up live preview against the Next.js front end early. And they migrated incrementally — highest-traffic content first, validated, then the long tail.

Lessons

The body is the project. HTML-to-Portable-Text is a lossy transformation; build a mapping for your real shortcodes and embeds and review the edge cases.

Model what plugins used to do. SEO metadata, structured fields, and forms have no plugin layer in Sanity — make them first-class schema or separate services.

Rewrite media links. Asset URLs change; rewrite every in-body image and file reference during the transformation.

Preserve SEO metadata explicitly. Carry per-post titles and descriptions across, and build a complete 301 map from permalinks to new routes.

Design the Studio for your editors. A tailored Studio plus live preview protects editorial velocity through the transition.

Before you make this move

The checklist that would have de-risked this migration:

Audit the article bodies for every shortcode, block, embed, and table pattern in use, and map each to a target.

Inventory plugin-provided data — SEO fields, custom fields, forms — and decide how each is modeled or rebuilt.

Export existing SEO titles and descriptions and plan to migrate them, not regenerate them.

Build the permalink-to-route redirect map before launch.

Prototype Sanity Studio and preview with real editors before committing the whole team.

So what do I actually think? Treat the body conversion as the real engineering work and almost everything else falls into place; treat it as an afterthought and it will find you at cutover, one mangled embed at a time. If I were running this, I'd spend the first week doing nothing but cataloguing every shortcode, block, and embed pattern in the archive and deciding what each one becomes, before I wrote a single line of import code. I'd carry the SEO titles and descriptions across by hand rather than trust a regenerate, and I'd build the redirect map early enough to be bored by it. Yes, that's slower up front, and yes, it feels like you're stalling. You're not. You're reading the square brackets before they read you. That's the whole difference, and it's the surface a pre-migration scan on the WordPress-to-Sanity path is built to drag into the light, so Replatform Radar shows you every one of them on your own site before the rebuild, not during it.