Blog

Migrate from WordPress to EmDash CMS: a practical guide

The WordPress to EmDash route that actually works: what maps where, the steps from export to DNS cutover, which plugins replace what, where AI earns its keep, and when staying put is the right answer.

A vintage printing press feeding pages through cardboard moving boxes into a clean modern letterpress

WordPress earned its install base, and I ran it for years without complaint. What wore me down was the maintenance surface: plugin updates every week, PHP version bumps breaking something at the worst time, and a hosting bill for a site that mostly served static articles with a contact form. I moved my own site off WordPress once already. This guide is the route I would take doing it again, this time landing on EmDash, the CMS this site runs on.

The short version: EmDash is an Astro-based CMS where one deployment gives you the site, a real admin at /_emdash/admin, a REST API, passkey sign-in and a media library. It runs on Cloudflare Workers with D1 and R2, or on a plain VPS with Node and SQLite. The migration itself is a mapping exercise - decide what your content becomes, rebuild the theme once, script the bulk of the content over, then cut DNS. The honest caveat is that EmDash is young and WordPress is not going anywhere, so the first step is deciding whether to move at all.

Decide before you pack

EmDash is a good home for the sites WordPress is worst at running: blogs, documentation, marketing pages, content hubs. If your WordPress install is basically posts, pages, a menu and a contact form, you are the target audience. The editor is real - collections, a block editor, drafts, revisions, scheduled publishing - not a static site generator wearing a CMS hat.

If you are still weighing the platform choice, the EmDash review on bitdoze.com covers six months of running it in production, including who it is not for.

The concept map

Most of the migration fear comes from vocabulary, not difficulty. Nearly everything in a WordPress site has a direct counterpart in EmDash - it just lives somewhere more explicit:

WordPress to EmDash

Posts and pages
Collections and entries. One collection per content type, with your own fields
Custom post types and ACF
Typed collection fields in seed/seed.json - string, rich text, image, repeater, blocks
Theme (PHP templates)
Astro layouts and components plus CSS design tokens. Real code, no template hierarchy guessing
Widgets and shortcodes
Block types in a blocks field. Editors drop them into content, components render them
Contact Form 7 / WPForms
The Contact Forms plugin: forms, inbox, email log, honeypot and Turnstile built in
Yoast or Rank Math
A built-in SEO tab on every entry: title, description, canonical, robots, og image
Media library
EmDash media library backed by R2 (Workers) or the filesystem (Node)
Users and roles
CMS users with passkey sign-in and role-based permissions
Menus
Named menus edited in the admin, rendered by the layout
RSS feed
A route file like this site's rss.xml.ts, serving real XML

The migration, step by step

Nine steps, in order

  1. Audit the WordPress site

    List every plugin you actually use, every custom post type, every shortcode in real content, and every redirect you already maintain. Then export everything from Tools, Export in wp-admin and keep the old site running - the XML references images that still need to download. The audit list, not the XML, decides the shape of the new site.

  2. Stand up an EmDash site locally

    Run npm create emdash@latest and pick the template closest to your target - it ships the schema, routes and sample content you will edit rather than build from zero. No Cloudflare account needed for local work; the Node option runs on SQLite. The EmDash CMS tutorial on bitdoze.com walks the whole first-run path step by step.

  3. Model the content

    Turn your WordPress post types into collections in seed/seed.json. A post type with five ACF fields becomes a collection with five typed fields. Resist recreating every field - migration is the one time you get to delete the ones nobody fills in. Check the validation and block types while you are here; that is where allowed widget types get wired.

  4. Rebuild the theme once

    Port the look to Astro layouts, components and CSS tokens. This is the only step that is real work instead of plumbing, and you only pay it once - after this, the theme is a normal frontend codebase. The custom theme guide documents the exact file layout on this site, including doing the grunt work with AI agents.

  5. Move the content

    Small sites: paste it in through the admin and accept the two evenings it takes. Larger sites: script it. Parse the WordPress XML, map each item to a collection entry, upload images to the media library through the REST API, and write entries through the same content API the admin uses. Draft first, publish in one batch at the end.

  6. Replace the plugins you actually use

    Work down the audit list. Most WordPress plugin categories collapse into three EmDash answers: a block type, a built-in feature, or a plugin from the registry. The two plugins below cover the most common gap - contact forms and rich article blocks. Delete everything on the audit list that exists because a plugin needed another plugin.

  7. Pick the host

    Cloudflare Workers is the fast path: D1 and R2 are created at deploy, the free plan works if you skip sandboxed registry plugins, and the paid plan is $5 a month when you want them. A cheap VPS with Docker is the boring-reliable alternative, with Node standalone and SQLite. Both are documented in the tutorials linked below.

  8. Map redirects and cut DNS

    Export the old URL list (a crawler or the XML gives you most of it), write one 301 for each moved slug - WordPress /2024/05/my-post/ to EmDash /blog/my-post/ - and verify a sample by hand. Then point DNS at the new host and keep the old WordPress server running for two weeks. It costs almost nothing idle and covers the stragglers.

  9. Verify the boring way

    Submit the contact form yourself, click five old URLs, check the sitemap and RSS, then watch Search Console and analytics for a fortnight. Migration bugs do not announce themselves; they show up as a quiet 404 on a post that used to rank.

The plugin question, answered

The two gaps people hit first leaving WordPress are forms and rich article blocks. Both are covered by free plugins I maintain - they run on this site, so what you see in the demos is what ships.

Contact forms without Contact Form 7

The Contact Forms plugin builds forms in the admin with real fields, stores every submission in an inbox with an email log, sends mail through whichever provider the site already uses, and carries honeypot, fill-time and per-IP rate limiting plus optional Cloudflare Turnstile.

The live form on this site is the working demo, and the Turnstile write-up shows the full anti-spam pipeline with curl tests you can repeat. It replaced a Contact Form 7 install and a separate anti-spam plugin in one move.

Article blocks without page builders or shortcodes

The Content Widgets plugin adds nineteen block types to the editor - notices, accordions, tabs, numbered steps, code blocks, product boxes, quotes, a table of contents, cards and more - all server-rendered with no client JavaScript unless the widget needs it.

Every block is live on the widget gallery demo, and the install walkthrough covers registry install and wiring the renderers. This is what replaced the shortcode soup my old articles carried.

The plugins catalog page on emdashhq.com listing the registry entries
The plugins catalog on this site links each registry listing - the two free plugins cover the gaps most WordPress leavers hit first.

Where AI actually saves time

This is the part every migration post oversells, so the honest version first: AI will not do the migration for you. What it does well is the tedious mechanical middle that used to take days.

When I moved my own site off WordPress I used Codex CLI exactly this way - batch image conversion, frontmatter fixes, reference rewrites - and that WordPress to Astro migration write-up documents what held up and what needed babysitting.

Worth automating

  • WordPress HTML and shortcode cleanup: feed the agent a parser, then have it convert post HTML into clean blocks or Portable Text - verify a random sample by eye
  • Block components and seed schema: give an agent your old templates and it drafts the collection fields and renderers; the custom-theme tutorial did this end to end
  • Redirect maps: generate the old-slug to new-slug table from the XML, then review every row before it ships
  • Meta titles and descriptions: draft them per post from the content, edit the important pages by hand

Not worth automating

  • Body copy rewrites: it will quietly change your voice and occasionally your facts
  • Redirect maps unreviewed: a hallucinated 301 leaks ranking to the wrong page or to nothing
  • Content model design: that is a decision, not a task - you have to live with it
  • Anything involving your analytics history or SEO priorities: it does not know which posts pay the bills

Pre-launch checklist

Before you cut DNS

  • Every old URL either resolves or 301s somewhere sensible
  • The contact form stores a real submission and the email lands
  • SEO title and description filled on the pages that get search traffic
  • Sitemap and RSS reachable at their new URLs
  • Analytics firing - Plausible via the Site Scripts settings on EmDash, or self-hosted Plausible on your VPS
  • A real 404 page, not a blank error
  • Old WordPress server still running, billing untouched
The live contact form on emdashhq.com including the Turnstile widget
The boring verification, made visible: the live form on this site. If a submission stores and the email lands, the plumbing works - this is the last thing to test before DNS.

Links for the road

Tutorials referenced in this guide

A small site - a few dozen posts, a theme, one form - is a weekend once you have done the mapping. A five-hundred-post archive with three custom post types is a real project, but it is a finite one: the list above is all of it. WordPress will still be there if it ever wins again. The difference is you stop paying a weekly plugin tax for a site that was mostly pages all along.