For a long time, my website lived on WordPress with Elementor. That setup did something important for us: it made the site editable for my cofounder without needing me for every small change. That part genuinely mattered, and I do not want to pretend otherwise. When you are building something together, having a visual editor is useful. It lowers the barrier to making content changes, and it keeps the website from becoming "the developer’s problem" every single time something needs to be updated.
Still, over time I noticed I was starting to avoid working on the site. Not because I had run out of ideas, but because improving it had become annoying in a very specific way. Every time I wanted to tweak styling, clean up layout, improve structure, or make broader SEO changes, I felt like I had to fight through layers of builder settings, theme behavior, and plugin logic. Nothing was impossible, but very little felt clean.
That was the real problem for me. Elementor solved the editing problem, but it made the development side feel heavier and more opaque than I wanted. WordPress added to that feeling. Need a feature? Probably a plugin. Need another feature? Probably another plugin. Need to update everything? Hopefully nothing breaks. Even when things were working, the site often felt more fragile than it should have.
At the same time, I had a pretty clear idea of how I wanted to work on the site going forward. I wanted the frontend to live in code again in a way that made sense to me. I wanted to improve design and SEO more systematically. I wanted to use AI tools and coding agents to help with real improvements, not just random copy suggestions. I wanted the site to feel like software again.
That is what pushed me toward EmDash.
What attracted me was not that it was new. I was not looking for novelty. I was looking for a setup that gave me a CMS without making the frontend feel like a black box. Once I decided to migrate, I realized pretty quickly that I did not want to treat it like a literal one-to-one port. I was not interested in dragging all of the old implementation details into a new system. What I wanted was to extract the important parts of the current site and rebuild it properly.
That turned out to be the right mental model for the whole project.
The idea that made the migration manageable
Instead of thinking, "How do I recreate my WordPress site in EmDash?", I started thinking, "How do I turn my current website into a specification?"
That specification had a few parts:
- the visual style
- the exact routes I wanted to preserve
- the page types behind those routes
- the content itself
- the SEO rules I did not want to accidentally break
Once I had those written down, the migration stopped feeling like a giant vague project and started feeling like a series of smaller, solvable tasks.
What I actually did
The process was more structured than glamorous. I did not open EmDash and start blindly building pages. I first tried to understand what my existing site actually was.
1. Extract the design language from the current site
I started with a screenshot of my homepage. I sent that to ChatGPT and asked it to extract the site’s design system in a way that would be useful for implementation.
I did not ask for generic design feedback. I asked for something practical.
For reference, this is what we started out with:

For example, this was the kind of prompt I used:
I am rebuilding this website in a new stack.
Based only on this screenshot, extract the visual system in a way that an AI coding agent can use.
Please describe:
- primary, secondary, accent, background, surface, border, and muted colors
- typography style and likely heading hierarchy
- spacing rhythm
- button styles
- card styles
- border radius and shadow language
- what feels intentional in the design
- what should stay the same in a rebuild
Output as a markdown design-system document.
The output became a file in my project that looked something like this:
# Style system
## Colors
- Primary: dark blue
- Accent: green
- Background: warm off-white
- Surface: white
- Borders: light gray
- Text: near-black
## Typography
- Modern sans-serif
- Large bold headings
- Clean readable body text
- Friendly but product-focused tone
## UI style
- Rounded buttons
- Soft shadows
- Generous spacing
- Clean cards with subtle separation
This sounds simple, but it helped a lot. It gave me a reference point that was more useful than "make it feel like the old site."
2. Extract every route from the sitemap
This was the SEO-sensitive part, and probably one of the most important steps in the whole migration.
I took my sitemap or sitemaps from the WordPress site and sent them to ChatGPT with a prompt to extract all public routes and group them by type.
Something like this:
Here are my sitemap URLs.
Extract every indexable route and preserve the exact pathnames.
Group them by page type:
- homepage
- marketing page
- article
- article index
- category page
- author page
- legal page
- utility page
Also tell me:
- which routes should definitely stay identical for SEO
- which routes share a common template
- which pages are likely generated from the same content model
Output as a markdown route inventory.
That gave me a real inventory instead of a vague idea of what existed.
A simplified version looked like this:
# Route inventory
## Core pages
- /
- /about
- /pricing
- /contact
## Blog
- /blog
- /blog/my-first-post
- /blog/another-post
## Categories
- /category/shopify
- /category/seo
## Legal
- /privacy-policy
- /terms-of-service
This became one of my most important migration documents. It meant I had a clear list of what needed to survive the rebuild exactly as-is.
3. Reduce the site to page types
Once I had the routes, I grouped them into templates. This step made the project feel much smaller.
A site with 40 pages sounds like a lot. A site with 5 page types sounds very doable.
My page types looked roughly like this:
# Page types
## Homepage
Hero, supporting sections, trust, CTA, footer
## Standard page
Intro, flexible content blocks, CTA
## Article page
Title, metadata, article body, related content
## Article index
List/grid of articles with pagination or categories
## Simple content page
Minimal layout for legal or basic informational content
This helped a lot when working with both Stitch and coding agents, because I was no longer asking for "all my pages." I was asking for a homepage template, an article template, a standard page template, and so on.
Using Stitch without letting it take over
After I had a style document and route/page-type docs, I used them to create prompts for Google Stitch.
The main thing that helped here was not using Stitch like a magical website generator. I treated it more like a design assistant for the major page types.
So instead of asking it to rebuild the entire site, I would ask it for a strong homepage direction, or a clean article page layout, based on the style system I had already extracted.
For example, for the homepage I would use a prompt like this:
Create a homepage for a modern software website.
Use the attached style-system document as the source of truth.
The tone should feel friendly, premium, and clear, not flashy.
Structure:
- hero
- proof or trust section
- problem/solution section
- feature grid
- how it works
- FAQ
- final CTA
- footer
This will later be implemented in Astro/EmDash, so keep it realistic and reusable.
For the article page, something more like this:
Create a long-form article page for a technical/product-focused site.
Requirements:
- strong readability
- generous whitespace
- good room for code examples
- metadata near the top
- optional table of contents
- related content section
- visually clean, not overdesigned
This will later be implemented in Astro/EmDash.
That gave me a nice starting point without making me feel locked into the generated result.
Setting up EmDash locally
Once the planning was solid enough, I set up the new project locally.
This was honestly one of the nicest parts of the migration. After spending enough time in WordPress and Elementor, being back in a normal project with files, structure, and code felt great.
At a high level, the setup looked like this:
npm create emdash@latest
Or if you are working from the repo directly:
git clone https://github.com/emdash-cms/emdash.git
cd emdash
pnpm install
pnpm build
After that, I chose a starting point, ran the site locally, and began shaping the frontend properly.
The important thing here is that I did not start by rebuilding random pages. I started with the shared system.
What I built first
This is one of the parts where being a bit methodical saves a lot of time later.
I started with the things that everything else depends on:
- layout shell
- header
- footer
- max-width containers
- typography rules
- button styles
- card styles
- spacing rules
- shared content sections
That gave me a real visual foundation.
Only after that did I start building actual page templates:
- homepage
- standard page
- article page
- article index
Then I mapped real routes onto those templates.
This order matters more than it sounds. If you start by generating pages one by one, especially with AI involved, you can end up with a weird pile of near-duplicate components. Starting with the shared design system keeps things much more sane.
Connecting Stitch MCP to Codex or Claude
This was one of the parts I was most curious about going in, because it is where the workflow starts to become much more agent-friendly than a typical WordPress setup.
In my case, I connected the Stitch MCP flow to Codex. The goal was not to let AI "take over the website." The goal was to give the coding agent enough structured context that it could actually be helpful.
That context included:
- the design-system file
- the route inventory
- the page-types document
- the project files themselves
- the Stitch output for the relevant page types
- the EmDash project conventions
The result was that I could ask the agent for very concrete work, like:
Read:
- docs/migration/style-system.md
- docs/migration/routes.md
- docs/migration/page-types.md
Then:
- implement the homepage using reusable components
- preserve the exact homepage route
- keep spacing and typography aligned with the style document
- use the Stitch design only as inspiration, not as literal final code
- document anything unclear
That worked much better than the kind of AI workflow I could have used in WordPress, because the system was much easier to reason about. The agent was operating on actual project files, not trying to untangle page-builder markup and plugin settings.
Put the migration docs inside the repo
This was such a small thing, but it made the whole process better.
I created a structure like this:
docs/
migration/
style-system.md
routes.md
page-types.md
content-notes.md
That meant the project itself contained the context for the migration.
It also meant I was not constantly re-explaining everything to the agent. I could point it to files and say, "work from these." That makes a bigger difference than I expected.
Content migration: let the platform do the boring part
For content itself, I tried to be practical.
I did not want to manually recreate every article or page using AI. That sounded slow, error-prone, and generally miserable. The much better approach was to let EmDash handle the WordPress content migration path where possible, then use AI for the work around it.
That meant AI was helping with:
- structure
- prompts
- route mapping
- template recreation
- cleanup
- implementation
- content QA
Instead of helping with:
- manually copying article number 37 into a new file at 1 AM
That split felt right to me.
The actual guide part: how I would recommend doing this
If you want the short version of the process, this is the order I would recommend.
Migration checklist
- export or document the current site
- capture the visual style from screenshots
- extract all routes from the sitemap
- group routes into page types
- generate page-type directions in Stitch
- set up EmDash locally
- add migration docs into the repo
- build shared layout and components first
- build templates second
- migrate content
- map every old route to a real page
- QA the result before launch
Minimal file structure that worked well for me
my-site/
docs/
migration/
style-system.md
routes.md
page-types.md
content-notes.md
src/
components/
layouts/
pages/
Example of a practical migration prompt for an agent
Read these files first:
- docs/migration/style-system.md
- docs/migration/routes.md
- docs/migration/page-types.md
Goal:
Rebuild the current WordPress site in EmDash while preserving existing public routes.
Rules:
- preserve route paths exactly unless documented otherwise
- build reusable components instead of one-off page sections
- keep the visual style aligned with style-system.md
- recreate templates first, then wire up routes
- document any route or content mismatch
Example of a good sanity-check list before launch
- [ ] homepage rebuilt
- [ ] standard page template rebuilt
- [ ] article template rebuilt
- [ ] blog index rebuilt
- [ ] important routes preserved exactly
- [ ] metadata checked
- [ ] internal links checked
- [ ] redirects added if needed
- [ ] content imported
- [ ] mobile layout checked
- [ ] forms and CTAs tested
What changed for me after the rebuild
The biggest difference is that the website now feels like something I can actually work on properly again.
That might sound obvious, but it is hard to overstate how much better that feels. If I want to improve the design system, I can do that in code. If I want to improve internal linking, metadata handling, or article layout, I can do that in code too. If I want AI to help me identify weak pages, improve templates, or suggest structural changes, I can point it at a system that actually makes sense.
That was really the whole point of the migration.
I was not trying to make the site dramatically different for the sake of it. I was trying to make it easier to own, easier to improve, and less annoying to work on.
A few things I would do the same way again
A few parts of this process were much more helpful than I expected:
- extracting the style system before touching code
- extracting all routes before touching code
- thinking in page types instead of individual pages
- keeping migration docs inside the repo
- building the shared system before the templates
- using AI for structure and implementation help, not for blind content recreation
Those were the parts that made the migration feel controlled instead of chaotic.
A few things I would avoid
There are also a few traps I would try to avoid if I had to do it again.
I would not start by trying to recreate every page exactly, one by one. That is a good way to turn the project into a slog.
I would not let design tools generate production structure without checking whether the result is actually reusable.
I would not casually change routes during the migration just because a different URL pattern looks nicer.
And I definitely would not keep all the important migration context in my head or in random chat tabs. Writing it down inside the project is worth the small effort.
Final thoughts
I do not think WordPress and Elementor are bad tools. They solved real problems for us at a certain stage. My cofounder could edit the site visually, and that was useful. But eventually I wanted something that gave me more control, less black-box behavior, and a better foundation for improving the website over time.
That is what made this migration worth it for me.
The interesting part was not just moving to a new CMS. The interesting part was changing the shape of the work. Instead of treating the old website as something I had to keep patching, I treated it as something I could analyze, document, and rebuild properly.
That made the whole process much less intimidating than I expected.
And maybe that is the nicest surprise in projects like this. Once you stop thinking of a migration as one giant leap and start treating it like a series of very boring, very clear steps, it becomes a lot more doable.
If you want to check out what we're building (the old site) go to https://addora.app
I'll be rolling out the new app this afternoon, and I'm super excited about it!
Top comments (0)