Web Design·8 min read·

Vibe Coding a Headless WordPress Migration: From Vite SPA to Next.js + Vercel + SiteGround

Preston Vawdrey

Preston Vawdrey

Full Stack Marketer

GLM 5.2 inside opencode planning a headless WordPress migration for Draftly.blog One opencode session, one audit, one migration plan. The vibe-coding setup did the work I'd usually pay a small team to figure out.

I sat down with GLM 5.2 in opencode this morning to look at one thing on Draftly.blog and ended the session with a security pass that cleaned up the worst of what it found, and a complete plan for moving Draftly from a Vite SPA plus a separate WordPress marketing site to a clean headless WordPress architecture on Vercel and SiteGround.

That's a lot of ground for one session, and it's worth writing up. Here's what got done, what the architecture looks like, and what the vibe-coding workflow actually unlocked along the way.

The Audit That Started It All

I asked GLM 5.2 to do a security review of the Draftly codebase. I expected a few notes. I got back a longer list of findings than I expected, with a handful at the top that demanded immediate fixes.

The top of the list was a handful of credentials that had no business being in the repo. Every one of them was rotated and the offending files were scrubbed before this post went up. Below that, the rest read like the predictable shape of a SaaS that grew fast, with the usual list of hardening tasks any seasoned reviewer would expect to find.

The audit didn't just list things. It ranked everything in remediation order, starting with the credentials work and stepping through the hardening tasks behind it. I worked the top of the list in the same session and queued the rest.

I rotated the affected credentials at the provider side, scrubbed what needed scrubbing, and confirmed the build was still clean before moving on.

If you've never had a model do a real security pass on a codebase you've been shipping, do it. The exercise on its own is worth the subscription.

From "Marketing Plus App" to a Clean Three-Domain Split

After the audit, I shifted the session to the architectural question I've been circling for a while.

Draftly today lives in two pieces. There's a WordPress marketing site on draftly.blog, hosted on SiteGround, where I run SEO content. And there's the actual Draftly app, a Vite + React SPA on a Bolt subdomain that publishes posts to WordPress, Ghost, Shopify, Webflow, and a few other CMS targets. The marketing site and the app share a brand but absolutely nothing else. Different domain. Different stack. Different mental model for visitors.

I want one home. I want draftly.blog to be the front door. I want the app to live at app.draftly.blog. And I want WordPress to stop trying to be the public-facing site and start being what it should be: a headless CMS that feeds a fast, statically rendered marketing frontend.

GLM 5.2 mapped out the target architecture cleanly. Three domains, three responsibilities:

  1. draftly.blog runs a new Next.js marketing site on Vercel. Pages are statically generated at build time and revalidated on a schedule. It fetches all content (homepage, features, pricing, blog posts, ACF fields) from WordPress via REST or WPGraphQL.
  2. wp.draftly.blog is WordPress on SiteGround. Editors live here. It serves the admin UI and the content API. It is not public-facing as a website. The frontend just queries it.
  3. app.draftly.blog is the existing Vite SPA on Vercel. Same code I already ship. Logged-out visitors get a minimal login screen instead of the in-app landing page, with a link back to draftly.blog for marketing.

That's the picture. The honest part: getting there is a real migration that takes some doing. WordPress's site URL has to move. DNS and hosting config need to point the right domains at the right places. The app's backend config and a couple of hardcoded fallback URLs need to follow. The in-app landing page goes away.

GLM 5.2 wrote that whole runbook in the same context window. Track A (deploy the app to Vercel) is in this repo. Track B (build the Next.js marketing site) is a new repo. Track C is DNS and external service config. I have a checklist now that I can hand to a freelancer or work through myself over an evening or two.

Why Headless WordPress (And Why Not Just Keep the SPA for Marketing)

Worth pausing on this one because it's the SEO decision underneath everything else.

The existing Draftly app is a client-rendered Vite SPA. That's fine for an app. It is not fine for marketing pages. Google's renderer can crawl JavaScript-rendered content, but the latency, the budget, and the quality of what gets indexed are all worse than serving HTML that's already there when the bot arrives. Every SEO audit I do for clients eventually lands on the same conclusion, and it would be embarrassing to know this and ship it wrong on my own product.

The fix is a frontend that pre-renders. Next.js statically generates the marketing pages at build time, pulls content from WordPress at that moment, and serves them as plain HTML. Incremental Static Regeneration handles the case where someone updates a post in WordPress and we don't want to wait for the next deploy. When Google crawls draftly.blog, it gets fully rendered HTML with proper metadata, JSON-LD, Open Graph tags, and a clean sitemap.

WordPress stays in the picture because the writer experience is good and I don't want to rebuild that. The headless setup keeps the WP admin, ACF Pro for structured content, and Yoast for SEO data, and just changes where the pages get served. Editors don't notice. Search engines notice a lot.

The other angle here is brand consolidation. Right now if someone shares the app on LinkedIn, they're sharing a Bolt subdomain. After the migration they share a clean subdomain on the root domain. The marketing site, the app, the docs, everything lives under the same root domain. That compounds, both for users and for search.

The Vibe-Coding Workflow That Made It Tractable

I'm not going to pretend GLM 5.2 alone produced the audit and the architecture plan. The setup matters. I write about this in my GLM 5.2 review, but it's worth saying it again in this context because the audit-plus-migration combo is exactly the use case the setup was built for.

I run GLM 5.2 inside opencode. Opencode is the harness. It's a Claude Code-shaped tool loop with the same kind of context handling, the same MCP-style tool calls, and the same general feel. The GLM coding subscription comes with a set of MCP servers that are actually good. File search, structured edits, the connectors I'd reach for in Claude.

The 1 million token context window is what made this session possible. The audit walked the entire codebase. Maybe 25K tokens. The architecture plan needed to hold the current WordPress setup, the current app code, the backend functions, and three target architectures simultaneously. Easily another 80K. Then we executed the first round of fixes, which meant reading and editing several files. Another 30K, conservatively. None of it would have fit in a 200K window without aggressive chunking, and chunking is how you lose the thread.

The real win is that the harness, the MCPs, and the context window together let you run a session that spans audit, plan, and execution without the model forgetting what the goal was. That's the part that feels like vibe coding, and it's the part you can't get from any single component.

What Got Done In This Session vs What's Still Ahead

I want to be honest about scope. This session produced:

  • A complete security audit with clear ranking and a remediation order.
  • The top-priority security fixes from the audit landed in code and at the provider side.
  • A full three-domain architecture plan for the migration, covering hosting, DNS, backend config, Vercel setup, and the code changes by file.
  • A clear separation of work into Track A (this repo), Track B (new marketing repo), and Track C (DNS and external services).

It did not produce a live migration. The domains still point where they did this morning. The Next.js marketing site is a plan, with no repo yet. The Vite app has not been deployed to Vercel yet. Those are the next sessions.

That's the honest framing of vibe-coded planning. The model can compress weeks of clarification into one afternoon, but it doesn't shortcut the deploys. I still have to wire up the Vercel project, move the DNS, and update the backend config. The migration runbook is a markdown file I'll work through.

The Lesson for Other Vibe Coders

If I had to pick one thing to take away from the session, it would be this: the harness, the model, and the context window do their best work as a unit. None of them on their own would have gotten this done in one sitting.

A bigger model with a smaller context would have rebuilt mental state every few exchanges. A bigger context with a worse model would have produced a confident-sounding audit that missed real issues. A great model and a great context but a clunky harness would have meant fighting the tool loop instead of doing the work. The vibe-coding workflow that's been worth running, for me, has been the version where all three are roughly equal and clearly designed for each other.

I'll take a lesson from the audit too. If you're building a SaaS, schedule a real adversarial review of your codebase the next time you're between features. Not a linter pass. An actual security audit. Let the model look for hardcoded keys, unauthenticated endpoints, and exposed environment variables. The chance that there's nothing wrong is basically zero, and the cost of finding out the easy way is one session.

If you want to talk through a migration like this on your own product, or you've vibe-coded something cool and want a second pair of eyes, ping me on LinkedIn. I'm running these sessions almost daily right now.

Let's Work Together

Whether you need a website, marketing strategy, or full-stack growth support, I'd love to hear about your project.