DEV Community

Cover image for Migrate your WordPress blog to Sanity in an afternoon (open-source tool)
Nilesh Gadekar
Nilesh Gadekar

Posted on

Migrate your WordPress blog to Sanity in an afternoon (open-source tool)

If you're running your blog on WordPress but building your frontend with modern stacks like Next.js, Remix, or Astro, Sanity can be a much better home for your content. Structured content, Portable Text, powerful querying, real-time collaboration… all the good stuff.

The problem: actually moving your WordPress content into Sanity is usually the painful bit.

Most guides show you how to write your own migration script from scratch. We did that work, battle-tested it on real blogs, and then open-sourced the tool so you don’t have to reinvent the wheel:

👉 Repo: https://github.com/salttechno/wp-to-sanity-migration

This post walks you through what it does and how to use it.


What this tool does for you

The WordPress to Sanity Blog Migration Tool is a Node.js-based CLI that talks to the WordPress REST API and pushes everything into Sanity in a structured way.

Out of the box, it:

  • ✅ Migrates posts, categories, tags, and media (images)
  • ✅ Converts WordPress HTML content to Portable Text for richer editing and rendering
  • ✅ Preserves relationships (categories, tags, featured images)
  • ✅ Uploads images into Sanity’s asset pipeline
  • ✅ Is idempotent – safe to run multiple times (no duplicate docs)
  • ✅ Ships with a ready-to-use Sanity Studio configured for the migrated content

It’s focused on blog migration first, but you can extend it to pages, custom post types, authors, etc.


Prerequisites

You’ll need:

  • Node.js 18+
  • A WordPress site with the REST API enabled (default for WP 4.7+)
  • A Sanity project (free tier is enough)
  • A Sanity API token with write access

If you can open:

  • https://your-site.com/wp-json/wp/v2/posts

in your browser, you’re good on the WordPress side.


Quick start: migrate your blog in a few steps

1. Clone the repo and install dependencies

git clone https://github.com/salttechno/wp-to-sanity-migration.git
cd wp-to-sanity-migration
npm install
Enter fullscreen mode Exit fullscreen mode

2. Configure your .env

Copy the example env file:

cp .env.example .env
Enter fullscreen mode Exit fullscreen mode

Then edit .env:

# WordPress Configuration
WP_API_URL=https://your-wordpress-site.com/wp-json/wp/v2

# Sanity Configuration
SANITY_PROJECT_ID=your-project-id
SANITY_DATASET=production
SANITY_TOKEN=your-write-token
SANITY_API_VERSION=2024-11-25
Enter fullscreen mode Exit fullscreen mode

Tip: you’ll find your Project ID and can generate a token in sanity.io/manage under your project’s settings.

3. Test connections

Before importing anything, verify both sides are reachable:

npm run test
Enter fullscreen mode Exit fullscreen mode

This checks that:

  • WordPress REST API is reachable
  • Sanity credentials and token are valid

4. Run the migration

Now the fun part:

npm run migrate
Enter fullscreen mode Exit fullscreen mode

Behind the scenes, the script:

  1. Fetches categories
  2. Fetches tags
  3. Fetches media (images) and uploads them to Sanity
  4. Fetches posts and creates/updates corresponding Sanity documents, wiring up all relationships

The migration is idempotent, so you can safely re-run it while tweaking schemas or content mapping.


Browse your content in Sanity Studio

The repo also includes a preconfigured Sanity Studio in the the studio/ folder.

From the project root:

cd studio
npm install
cp .env.example .env   # add your Sanity project details
npm run dev
Enter fullscreen mode Exit fullscreen mode

Open:

http://localhost:3333
Enter fullscreen mode Exit fullscreen mode

You’ll see:

  • A Post schema with Portable Text fields
  • Categories and Tags linked via references
  • Featured images wired up as Sanity image assets

You can deploy the Studio as well:

cd studio
npm run deploy
Enter fullscreen mode Exit fullscreen mode

Sanity will give you a hosted Studio URL like your-blog.sanity.studio.


How the migration works under the hood

If you’re curious (or planning to extend it), here’s the rough architecture:

  • wordpress-client.js

    Talks to the WordPress REST API, fetches posts, categories, tags, and media.

  • sanity-client.js

    Uses @sanity/client to create or replace documents in Sanity.

  • html-to-blocks.js

    Converts HTML from WordPress into Portable Text blocks, so your editors aren’t stuck editing raw HTML.

  • transformers.js

    Maps WordPress entities into Sanity document shapes: posts → post, terms → category / tag, media → image assets.

  • migrate.js

    Orchestrates the whole process in the right order and ensures it’s safe to re-run.

This makes it easy to:

  • Add new content types (e.g. case studies)
  • Plug in custom fields
  • Change schema names or structures on the Sanity side

What gets migrated (and what doesn’t)

Out of the box, the tool handles:

Posts

  • Title, slug, published date
  • Body content → Portable Text
  • Excerpt → Portable Text
  • Featured image
  • Categories & tags

Categories & Tags

  • All your post categories and tags, with relationships preserved

Media (images)

  • Images referenced from posts are uploaded into Sanity as proper image assets

By default, it does not migrate:

  • Pages
  • Authors
  • Comments
  • Custom post types
  • WordPress-specific metadata

The point is to give you a clean, production-ready baseline for blog content, not an opaque black box that tries to handle every plugin and edge case.


Extending it: pages, authors, custom post types

You can absolutely extend this for more advanced setups.

Some ideas:

Pages

  • Add a page schema to studio/schemas/
  • Add a new fetch function in wordpress-client.js for pages
  • Create a page transformer in transformers.js
  • Wire it into migrate.js

Authors

  • Fetch authors from the WordPress API
  • Add an author schema (already included as an optional type)
  • Link authors in the post transformer

Custom post types

  • Create additional WordPress client functions (e.g. getCaseStudies())
  • Add a corresponding schema in Studio
  • Create a transformer and call it from the migration script

There’s even an example branch in the repo that shows how to handle a custom post type.


Common gotchas (and how to fix them)

Some issues you might hit:

1. “Cannot connect to WordPress API”

Check:

  • Is WP_API_URL correct?
  • Can you open <WP_API_URL>/posts in your browser?
  • Is your WP site behind auth or a firewall?

2. “Sanity connection failed”

Verify:

  • SANITY_PROJECT_ID matches your actual project
  • Token has read + write permissions
  • Token hasn’t been revoked or expired

3. “Invalid image, could not read metadata”

This usually means there are corrupted or weird images in your WordPress media library. The script logs these but doesn’t fail the entire migration—fix them later if needed.

4. “JavaScript heap out of memory”

For very large sites, bump Node’s memory limit:

NODE_OPTIONS="--max-old-space-size=4096" npm run migrate
Enter fullscreen mode Exit fullscreen mode

Plugging into your frontend

Once your content lives in Sanity, you can query it with GROQ and render with your framework of choice.

Example GROQ query:

*[_type == "post"] | order(publishedAt desc) {
  title,
  slug,
  publishedAt,
  excerpt,
  body,
  featuredImage,
  categories[]->,
  tags[]->
}
Enter fullscreen mode Exit fullscreen mode

Example React/Next.js component with Portable Text:

import { PortableText } from "@portabletext/react";

export function BlogPost({ post }) {
  return (
    <article>
      <h1>{post.title}</h1>
      <PortableText value={post.body} />
    </article>
  );
}
Enter fullscreen mode Exit fullscreen mode

From here, all the usual headless/modern-stack benefits kick in:

  • Static generation or ISR
  • Custom designs per post type
  • Multi-channel publishing
  • Better performance and SEO than a typical WP theme stack

Why we open-sourced it

We originally built this tool to migrate a real production blog to Sanity, without spending days writing one-off scripts and dealing with partial imports.

Instead of leaving it as an internal script, we decided to clean it up, document it properly, and release it under the MIT license.

If you:

  • Are stuck on WordPress but want Sanity for content
  • Don’t want to maintain fragile one-off migration scripts
  • Prefer a starting point you can hack on

…this repo should save you a lot of time.

👉 Check it out, try it, and star it if it helps:

https://github.com/salttechno/wp-to-sanity-migration


Maintainers & support

This project is maintained by Salt Technologies, a software outsourcing company in India that builds modern web apps, headless CMS projects, and data/AI-powered platforms for clients across the globe.

🌐 Software Outsourcing Company in India – Salt Technologies

If you run into issues:

  • Open a GitHub issue on the repo
  • Share ideas for improvements (custom post types, progress UI, auth support, etc.)
  • Or open a PR if you’ve already solved a problem others might hit

Happy migrating 🚀

Top comments (0)