Encited logo
Pricing
How to Migrate a WordPress Site to Lovable Without Losing SEO

How to Migrate a WordPress Site to Lovable Without Losing SEO

Sep 16, 2026September 16, 2026ยทby Aki from Encited

A step by step WordPress to Lovable migration: pick the architecture, keep every URL, move the content, and fix the rendering gap before you switch DNS.

A WordPress site that has been live for a few years is two things at once. It is a pile of pages you could rebuild in a weekend, and it is a set of URLs that Google, Bing and the AI crawlers already trust. The rebuild is the easy half. The URLs are where migrations go wrong.

This guide is the second half. It assumes you know WordPress and have never shipped a React app, and explains the React side in passing where it matters. If you are starting a Lovable site from nothing, the complete Lovable SEO guide is the better place to begin.

Work in this order: pick the architecture, take an inventory, move the content, protect the URLs, fix the rendering, then switch DNS.

1. Three ways to move WordPress to Lovable

"Migrate" covers three different projects. Decide which one you are doing before you prompt Lovable for anything, because the answer changes what you export and what you rebuild.

Approach What it is What you keep What you lose Pick it when
Full rebuild Content moves into the Lovable project, WordPress is switched off One stack, one bill, one deploy, full control of markup and routes The WordPress editor, plugins, scheduled publishing, revision history The site is mostly static pages and a modest blog, and nobody needs a CMS
Headless WordPress stays as the content store, Lovable renders the frontend from the REST or WPGraphQL API The editor your writers know, categories, drafts, scheduling, the plugin layer for content One stack becomes two, with hosting, auth and cache invalidation on both sides You have a real editorial team, a large archive, or writers who will not move
Hybrid New pages ship on Lovable, the WordPress blog stays under a subdirectory or subdomain Every indexed blog URL, untouched, plus a fast new front end A split design system, two publishing workflows, and a reverse proxy if the blog stays in a subdirectory The blog is the traffic and you want to leave it alone while you rebuild the rest

Headless is the option most articles recommend, because it sounds like the best of both, and it carries the highest ongoing cost. You keep paying for WordPress hosting, you keep patching plugins, and you add an API contract between two systems that can break on either side. Reach for it when the editorial workflow is the reason for the whole project, not to avoid a content export.

Hybrid suits sites where the blog is the traffic: your /blog/ URLs never change, so there is nothing to redirect and nothing to re-index. If you are still choosing between the platforms rather than planning the move, Lovable vs WordPress covers that decision.

2. Before you start: inventory the WordPress site

Everything here is read-only. Do it while WordPress is still running, because half of it is impossible to reconstruct afterwards.

Export every URL. Start with the sitemap. WordPress 5.5 and later publishes one at /wp-sitemap.xml; Yoast and Rank Math publish theirs at /sitemap_index.xml.

bash
CopyDownload
curl -s https://oldsite.com/wp-sitemap.xml
curl -s https://oldsite.com/sitemap_index.xml

The sitemap lists what WordPress thinks exists. A crawl tells you what is reachable and what already redirects. Run both and compare, because orphaned pages with backlinks only show up in the crawl.

A crawl gives you the status code, title, description and canonical for every page, which is the same list you check again after launch. The free SEO audit crawler exports it without a desktop install.

Export Search Console. Pull the last 12 months of pages and queries and save the CSV. Two lists matter later: the top 20 pages by clicks and the top 20 queries. Those are what you check at day 14 and day 30.

List every plugin and what it does. Go through the plugin screen one row at a time and write down the job each one performs: contact forms, SEO meta, redirects, caching, WooCommerce, memberships, galleries, analytics. Anything that outputs HTML or handles a form is work you are about to inherit.

Export the redirects you already have. The Redirection plugin exports to CSV or JSON from its own screen, and Yoast Premium and Rank Math have their own redirect managers. Those rules must survive the move, because the links pointing at the old URLs still exist.

Screenshot every template. One full-page screenshot per template: home, page, single post, archive, category, author, search results, 404. That is your build spec, and the only record of the old design once the theme is gone.

3. Move the WordPress content into Lovable

There are two export routes out of WordPress. The admin export at Tools then Export produces a WXR file: XML holding posts, pages, custom post types, comments and post meta. It is complete and awkward to parse. The REST API, on by default since WordPress 4.7, gives you the same content as JSON.

bash
CopyDownload
curl -s "https://oldsite.com/wp-json/wp/v2/posts?per_page=100&page=1&_embed" > posts-1.json

per_page caps at 100, so anything larger needs paging. The response carries an X-WP-TotalPages header that tells you how many pages to ask for. This script walks both posts and pages and writes one JSON file per type:

js
CopyDownload
// export-wp.mjs, run with: node export-wp.mjs
import fs from "node:fs";
const SITE = "https://oldsite.com";
const TYPES = ["posts", "pages"];
async function fetchAll(type) {
const out = [];
let page = 1;
let totalPages = 1;
while (page <= totalPages) {
const res = await fetch(
`${SITE}/wp-json/wp/v2/${type}?per_page=100&page=${page}&_embed`,
);
if (!res.ok) throw new Error(`${type} page ${page}: ${res.status}`);
totalPages = Number(res.headers.get("x-wp-totalpages") ?? 1);
out.push(...(await res.json()));
page += 1;
}
return out;
}
for (const type of TYPES) {
const items = await fetchAll(type);
const rows = items.map((item) => ({
slug: item.slug,
url: item.link,
title: item.title.rendered,
date: item.date,
html: item.content.rendered,
excerpt: item.excerpt.rendered,
image: item._embedded?.["wp:featuredmedia"]?.[0]?.source_url ?? null,
}));
fs.writeFileSync(`${type}.json`, JSON.stringify(rows, null, 2));
console.log(`${type}: ${rows.length}`);
}

Keep the url field. It is the old live URL of every page, and it becomes the left-hand column of the redirect map in the next section.

The html field is WordPress output, with block wrapper classes, inline styles and shortcodes your old plugins used to expand. Shortcodes render as raw text once WordPress is gone, so search the export for square brackets and handle those posts by hand. For Markdown instead of HTML, the turndown package converts it in a few lines.

Where the content lands. For a handful of pages, paste them into the prompt and let Lovable build a route per page. That does not scale past twenty or so, and not at all for a blog. For a blog, pick one of these:

  • A database table plus one route. Load the JSON into a Supabase table, then build a single /blog/:slug route that reads it. Writers edit rows. This is the usual answer and it has a rendering consequence, covered in section 5.
  • Files in the repo. One MDX file per post, turned into routes at build time. No database and no runtime fetch, but editing means opening a code editor, so it suits a technical team.
  • Headless. Leave the posts in WordPress and fetch them at request time. Nothing to migrate, and the WordPress bill continues.

Images. Everything under /wp-content/uploads/ has to move. Download the media library, then drop the files into public/ in the project or push them to object storage. Keep the file names. If the path changes, every old image URL breaks and image search traffic goes with it.

What happens to your CSS and your responsive layout

Nothing comes across, and that surprises people who expected a converter.

A WordPress theme's look is its own stylesheet plus whatever the page builder generated, often thousands of lines tied to that theme's markup. Lovable builds with Tailwind, which sets styles as utility classes on the elements themselves. The two do not translate, and pasting theme CSS into a Lovable project fights the framework rather than reusing it.

What does carry over is the design, if you hand it over as a design rather than as code. Screenshot every template at desktop and mobile width, note the exact values that matter, and give Lovable those: the hex codes, the font families and sizes, the container width, the spacing rhythm. A prompt with real numbers gets much closer than one asking it to match a screenshot.

Responsive behaviour is worth checking separately rather than assuming. WordPress themes ship breakpoints their designer chose; Tailwind has its own defaults. Walk each rebuilt template at phone, tablet and desktop width before you decide it is finished. The places it usually breaks are tables, anything that was in a sidebar, and images that were sized by the theme.

Two things that look like styling but are not. Any layout your theme built from a shortcode or a page-builder block is markup, not CSS, so it has to be rebuilt as a component. And font licences do not travel: check that the licence for a self-hosted font covers the new setup before you copy the files over.

4. Keep your WordPress SEO: URLs, redirects and metadata

This is the part that decides whether traffic survives. Do it in this order.

1. Match the URL structure, or map every change. The cheapest migration is the one where every URL stays the same. WordPress permalinks like /2019/03/12/post-name/ are ugly, and changing them costs more than keeping them. If you change them anyway, build the map now: old URL and new URL for every row of the crawl from section 2, with no blanks.

2. Redirect everything that moved. Every changed URL needs a 301, which tells search engines the move is permanent and passes the ranking signals to the new address. Lovable has no screen for arbitrary path redirects, and it does not read a _redirects file either, so there is nothing to configure inside the project. The rule has to live in the layer in front of the app, which leaves two places: your DNS provider, or a managed proxy.

Whichever you pick, the list is the same three columns.

ts
CopyDownload
source destination status
/2019/03/12/wordpress-hosting-tips /blog/wordpress-hosting-tips 301
/about-us /about 301
/category/news/* /blog 301

Cloudflare Bulk Redirects takes that as a CSV upload. Watch the trailing slash: WordPress serves /about/ and a Vite build usually serves /about. Pick one and add a rule that normalises the other, or you get two live versions of every page.

Do not build that list by opening pages one at a time. Run a technical audit against the new site and let it hand you the dead URLs. Point the crawler at your old URL list from section 2, and everything that now returns a 404 is a row in your redirect map, already deduplicated and already grouped by the pattern it came from. An afternoon of clicking becomes one crawl.

Encited runs that audit and holds the redirect rules in the same place, which means the list never leaves the tool. Both are exposed over MCP, so a coding agent can crawl the site, read back the pages that 404, and write the 301s for them in one pass. You review what it proposes instead of assembling it.

That matters more than it sounds, because of what you gave up in the move. On WordPress you had a plugin screen for redirects, a plugin for the sitemap, a setting for the canonical domain and a template for the 404 page. On Lovable you have none of them, since you no longer control the layer they live in. That layer still exists. It sits in front of the app now, and something has to own it: 301 rules for arbitrary paths, a real 404 status instead of a 200 carrying "page not found", apex against www as the canonical host, a sitemap that regenerates itself, and complete HTML for the crawlers that do not run JavaScript.

3. Rebuild titles and descriptions. Yoast and Rank Math store per-page overrides in post meta and generate everything else from site-wide templates at render time. Neither travels with your content. What you see in the search results today is assembled by a plugin that will not exist after the move, so capture it first:

bash
CopyDownload
curl -s https://oldsite.com/some-post/ | grep -iE '<title>|name="description"'

Run that over the URL list and you have every live title and description. Some plugins also expose the rendered head over the REST API, which saves the crawl.

4. Rebuild the structured data. Schema is JSON-LD that describes the page to search engines, and your SEO plugin generated it invisibly. Article on posts, Organization or LocalBusiness on the site, Product on anything you sell, FAQPage where you had an FAQ block. It becomes a script tag per route.

5. Regenerate the sitemap. A hand-written sitemap goes stale within a week, and Lovable will invent URLs if you ask it to write one. Generate it from the router at build time instead, step by step in how to generate a sitemap on Lovable. Point robots.txt at the new file.

6. Tell Search Console. Submit the new sitemap and leave the old property connected, so you can watch old URLs drop out as new ones appear. Use the Change of Address tool only if the domain changes; it does nothing for a same-domain rebuild.

5. Rendering: why WordPress to Lovable can cost you traffic

WordPress renders on the server. PHP assembles the full HTML for every request, so anything that visits, including crawlers that run no JavaScript at all, gets the complete text of the page in the first response. You have never had to think about this, because it has always been true.

A Lovable project on the Vite stack works differently. The build produces one HTML file with an empty container, and React fills it in the browser after the JavaScript loads. Lovable now ships two stacks: newer projects can land on TanStack Start, which renders on the server the way WordPress does, while older projects and many new ones are Vite. Check which one you have first. Open View Source, not the inspector, and look at the body. Content in the HTML means server rendering. An empty div with id="root" means the page is assembled in the browser. A routeTree.gen.ts file in the project is the other tell for TanStack.

Lovable closed part of this gap. Its own SEO and AEO page says new apps ship with TanStack Start server rendering, and older apps get pre-rendered snapshots automatically, on every tier, with nothing to switch on. Neither claim survived our testing intact. We checked more than 50 live Lovable sites, asking ChatGPT, Claude and Gemini to read the pages after the update and running the same URLs through a crawler simulator. We could not verify the automatic pre-rendering is being applied at all: the responses carried no page content, Lovable documents no way to check, and you cannot inspect your own snapshot. Server rendering does run on TanStack, and the initial HTML arrives, but the data does not. Anything from Lovable Cloud, Supabase or a remote API is fetched in the browser after that first response, so it never reaches a crawler. For a migrated WordPress site whose posts now come out of a database, that is precisely the content you moved for.

Put that next to section 3 and the problem is clear. The blog you just moved into a database table is the case the built-in snapshots do not cover. On WordPress those posts were server-rendered HTML for every visitor and every bot. On Vite they are one route that fetches rows in the browser.

Moving a server-rendered WordPress site onto a Vite Lovable project is an SEO downgrade unless you pre-render or land on TanStack. The content is the same and the URLs are the same. What changed is the one thing every crawler depends on. SSR versus pre-rendering for Lovable explains both options and how to check which applies to your project.

Test it rather than assume it. Pick a blog post on the staging build and ask for it as a bot would:

bash
CopyDownload
curl -s -A "Googlebot" https://staging.newsite.com/blog/some-post \
| grep -i "a sentence from the middle of that post"

Run the same command against the WordPress URL. WordPress returns the line. If the new site does not, that page is not readable as HTML today, whatever it looks like in your browser. The crawler simulator shows the same for AI crawlers and social previewers without a terminal. Do this before DNS, not after.

6. What does not come over: WooCommerce, forms and comments

Plugins are the part people underestimate. Each row below was a checkbox in WordPress and becomes a build task.

WordPress feature What it did What replaces it
WooCommerce Catalogue, cart, checkout, orders, tax Stripe in the Lovable project for a small catalogue, or an embedded Shopify buy button for a real store
Contact forms Rendered the form, validated it, emailed you A database insert through an edge function, or a hosted form service
Comments Threaded discussion under posts A hosted comment widget, or drop them and keep the good ones as page content
Site search Searched the post database A query against your own table, or a hosted search index
User accounts and memberships Registration, login, gated content Supabase auth plus a check on the protected routes
Category and tag archives A listing page per term, generated automatically Routes you build, with their own titles, descriptions and internal links
RSS feed /feed/ for readers and syndication A generated file, if anything still subscribes; check the logs first

Archives catch people out. WordPress created a page for every category and tag without being asked, and some of those pages rank. Check Search Console before you decide they do not matter.

7. Deployment, custom domain and DNS

Connect the custom domain inside Lovable and follow its DNS instructions exactly. Decide up front whether the canonical host is the apex oldsite.com or www.oldsite.com, then redirect the other to it. Serving both is a duplicate content problem you will spend a month unpicking.

At least a day before the cutover, lower the TTL on the records you are about to change to 300 seconds. TTL is how long resolvers cache a record, so a default of 24 hours means a bad switch stays broken for a day.

Keep WordPress running after you flip. One more month of hosting is the cheapest insurance in the project. Leave it up until Search Console shows the new pages indexed, then archive the database and the uploads folder before you cancel anything.

Where a headless setup lives

A full rebuild is simple to host: Lovable serves the site, you point DNS at it, done.

A headless setup has two things to host, and people underestimate the second one. The Lovable frontend stays on Lovable or moves to a static host. WordPress stays up as the content store, but it no longer needs to serve pages to the public. Move it to a subdomain such as cms.oldsite.com, keep it off the search index with a noindex header, and let the frontend read it over the REST API or WPGraphQL.

Two things to get right there. Requests from the browser to a different hostname need CORS headers on the WordPress side, or the browser blocks them. And a public WordPress REST endpoint is a rate-limit and abuse target, so cache the responses rather than calling WordPress on every page view.

The hidden cost is that you are now paying for WordPress hosting forever, on top of Lovable. If the only reason to keep WordPress is that the editors know the admin screen, price that convenience honestly against moving the content once.

8. The first two weeks after launch

Check these, in this order, on day 1, day 7 and day 14.

  • Coverage in Search Console. New URLs moving into Indexed. "Discovered, currently not indexed" on a lot of pages points back at section 5.
  • Crawl errors and the 404 report. Every 404 is a redirect you missed. Add the rule, do not wait.
  • Your top 20 queries and top 20 pages from the export in section 2. Positions wobble for a week or two after a migration. A steady decline over three weeks is usually redirects or rendering.
  • Link previews. Paste three URLs into Slack, WhatsApp and LinkedIn. Each should show its own title, description and image, not the homepage card.
  • Favicon and Open Graph images. Both are easy to leave behind with the old theme.
  • Rendering, again. Run the curl check from section 5 against a live blog post, a service page and the homepage.

Serve complete HTML on every route after the move

If the project stayed on Vite, Encited pre-renders every route, including the dynamic ones, and serves finished HTML to search engines, AI crawlers and social previewers, with a log of what each bot received.

Frequently asked questions

Can Lovable import a WordPress site directly?

No. There is no importer, plugin or converter that turns a WordPress site into a Lovable project. You export the content yourself, through the REST API or the WXR file, and rebuild the templates as routes. Lovable can generate the page structure from a prompt, but the content and the URL mapping are your job.

Does Lovable work with WooCommerce?

Not as WooCommerce, which is PHP running inside WordPress and cannot move. For a small catalogue, build checkout with Stripe in the Lovable project. For a real store with inventory, tax and fulfilment, keep the store on its own platform and use Lovable for the pages in front of it. That is the hybrid approach in section 1.

Should I go headless or rebuild?

Rebuild if the site is mostly static pages and a modest blog, and nobody needs a CMS. Go headless if writers will not leave the WordPress editor, or the archive is large enough that migrating it is its own project. Headless keeps your editorial workflow, and also keeps the hosting bill, the plugin updates and a second system to maintain.

Will I lose rankings?

Positions move for a week or two after any migration, then settle. Lasting losses come from three causes: URLs that changed without a 301, titles and descriptions that were not rebuilt, and content that no longer reaches crawlers as HTML. Handle all three and a same-domain migration is usually flat within a month.

How do I keep my WordPress blog?

Leave it running and point only the rebuilt pages at Lovable. A subdomain like blog.oldsite.com is the simple version, and every blog URL changes, so every one needs a 301. Keeping the blog under /blog/ on the same domain needs a reverse proxy in front of both systems: nothing changes for readers or Google, and it is more infrastructure to run.

How do I handle redirects in Lovable?

Not inside Lovable. It has no screen for arbitrary path redirects and does not read a _redirects file, so the rules live in the layer in front of the app: your DNS provider, or a managed proxy. Build the full old-to-new list before launch and load it in one go.

What happens to my Yoast SEO settings?

They stay behind. Yoast keeps per-page overrides in post meta and builds the rest from site-wide templates at render time, and the new project has no plugin reading either. Capture the live titles and descriptions with the curl command in section 4 before you switch WordPress off, then set them per route. The same goes for the schema Yoast generated.

Get discovered anywhere search happens

Readable, citable, outranking pages.

Avatar
How can we help?
Get instant answers to your questions or leave a message for an engineer will reach out
Ask AI about Encited
See our docs
Contact support
Leave a message
We'll get back to you soon
Avatar
Ask AI about Encited
Product questions, troubleshooting and
Thinking
Preview
Drop an image to attach
Powered by ReplyMaven