Key takeaways

  • WordPress still runs a large share of the web. But plugin sprawl creates ongoing cost, security risk, and performance problems as sites grow.
  • A Next.js + Supabase migration typically runs in five phases: audit, stack setup, data migration, frontend rebuild, and a redirect-mapped launch.
  • Server components, built-in image optimization, and a Postgres backend replace most of what WordPress plugins used to do.
  • Careful 301 redirects and a complete URL map protect search rankings during the switch, not luck.
  • Our SleepDr.com migration moved Lighthouse performance scores from 35 to 94 after switching to Next.js, Payload CMS, and Supabase.

Updated 15 August 2026: sources added, experience claims checked against our project record, summary added.

Outgrown WordPress? A Migration Playbook for Next.js + Supabase

Signs You've Actually Outgrown WordPress

WordPress still runs a large share of the web. Current estimates put it at around 40% of all sites. It's approachable and has a huge plugin ecosystem. But there's a difference between a tool that gets you started and one that scales with you.

Not everyone needs to leave WordPress. If you run a personal blog or a brochure site for a local business, WordPress with a decent theme and a handful of plugins is probably still fine. But some clear signs show you've outgrown it:

Plugin Conflicts Are Breaking Things Monthly

You update WooCommerce, and your page builder breaks. You update your page builder, and your SEO plugin throws warnings. You update PHP to 8.2 because your host requires it, and three plugins stop working. This isn't a bug. It's the architecture. WordPress plugins all share the same global scope, the same hooks, and the same database. Every plugin can conflict with every other plugin.

Sites running 30, 40, even 60+ active plugins are common. At that point, you're not maintaining a website. You're maintaining a Jenga tower.

Performance Has Become a Full-Time Job

Your PageSpeed score sits in the 30s. You've installed a caching plugin, an image optimization plugin, a minification plugin, and a CDN plugin, all to fix problems caused by the other 25 plugins. The irony is thick.

WordPress builds pages dynamically on every request unless something caches them. Each plugin can also add its own CSS and JavaScript files. A typical WordPress page with popular plugins loads 15-30 separate render-blocking resources. HTTP Archive's Web Almanac tracks Core Web Vitals pass rates by CMS, and WordPress sites consistently score lower than sites built with modern JavaScript frameworks. See the current CMS chapter.

Security Vulnerabilities Keep You Up at Night

WPScan's vulnerability database tracks WordPress vulnerabilities. Most sit in plugins and themes, not core. If your site handles user data, payments, or other sensitive information, each plugin adds extra risk. Patchstack publishes an annual report that blames most WordPress vulnerabilities on plugins, not WordPress core.

You're trusting dozens of independent developers, many of whom maintain plugins as side projects, with your security.

Your Dev Team Hates Working On It

This one gets overlooked. Good developers don't want to work in WordPress anymore. The PHP-template-spaghetti-with-ACF-fields workflow feels painful next to modern component-based development. If you want to attract and keep engineering talent, your tech stack matters.

The WordPress Tax: What Plugin Hell Really Costs

Here's what the "WordPress tax" typically looks like for a mid-size site: an e-commerce store or a SaaS marketing site with a blog, user accounts, and custom functionality.

Cost Category Typical Annual Impact
Premium plugin licenses (15-20 plugins) Low to moderate recurring cost
Managed WordPress hosting (WP Engine, Kinsta) Moderate to high, scales with traffic
Security monitoring and cleanup (Sucuri, Wordfence) Low, but recurring
Performance optimization time (developer hours) Moderate and ongoing
Plugin conflict debugging (developer hours) Moderate to high, and unpredictable
Emergency fixes when updates break things Variable, with spikes after major releases
Total Annual WordPress Tax Often several thousand pounds before any new feature work

That's the cost of just keeping the lights on, before you build a single new feature.

Why Next.js + Supabase Is the Stack That Makes Sense

There are a dozen ways to go headless. You could use Gatsby, though it's largely in maintenance mode since Netlify acquired it (see Netlify's blog for details). You could use Remix, Astro, or SvelteKit. For the backend, you could pick Firebase, PlanetScale, or a custom API.

For teams migrating from WordPress in 2026, Next.js + Supabase hits a sweet spot that's hard to beat. Here's why.

Next.js: The Frontend That Does It All

Next.js 15 shipped as stable in October 2024 and gives you server components by default. That means you get the speed of static sites with the flexibility of dynamic ones. You can statically generate blog posts at build time, server-render dynamic pages, and client-render interactive parts, all in the same app.

For teams coming from WordPress, the key benefits are:

  • Built-in image optimization -- replaces 2-3 WordPress plugins
  • Automatic code splitting -- each page only loads the JS it needs
  • Edge middleware -- handle redirects, auth, and A/B tests at the CDN level
  • Incremental Static Regeneration (ISR) -- rebuild individual pages without full deployments
  • App Router with React Server Components -- cuts client-side JavaScript sharply

Our SleepDr.com migration and the Not Another Sunday directory both run on this stack in production. The performance gap versus a plugin-heavy WordPress build is large and consistent.

Supabase: The Backend WordPress Wished It Had

Supabase is an open-source Firebase alternative built on PostgreSQL. It gives you:

  • A full Postgres database with a REST and GraphQL API auto-generated from your schema
  • Built-in authentication (email, OAuth, magic links, SSO)
  • Row-level security policies for fine-grained access control
  • Real-time subscriptions via WebSockets
  • Edge Functions for serverless backend logic
  • Storage for file uploads with CDN delivery

Supabase works well for WordPress migrations because WordPress uses MySQL, and your data model maps surprisingly well onto PostgreSQL. Custom post types become tables. Post meta becomes JSONB columns. User data maps almost one to one.

Supabase's pricing page lays out the free and Pro tiers, which cover most production sites without the recurring plugin and hosting fees that pile up on WordPress.

Outgrown WordPress? A Migration Playbook for Next.js + Supabase - architecture

The Migration Playbook: Phase by Phase

Here's a phased approach that keeps migrations predictable and low-risk. This isn't a weekend project. Budget 4-12 weeks depending on site complexity.

Phase 1: Audit and Architecture (Week 1)

Before you write a single line of code:

  1. Export a full plugin list with wp plugin list --status=active (WP-CLI)
  2. Map every plugin to its replacement in the new stack
  3. Export your full URL structure including all posts, pages, taxonomies, and custom post types
  4. Document all forms, integrations, and third-party connections
  5. Identify custom functionality that lives in your theme's functions.php

The plugin mapping step matters most. Here's what common replacements look like:

WordPress Plugin Headless Replacement
Yoast SEO Next.js built-in metadata API + generateMetadata()
WP Super Cache / W3 Total Cache Not needed (static by default)
Wordfence / Sucuri Supabase RLS + Vercel's built-in DDoS protection
Contact Form 7 / Gravity Forms React Hook Form + Supabase Edge Function
WooCommerce Saleor, Medusa.js, or Shopify Storefront API
ACF / Custom Fields Supabase tables with typed schemas
WP Migrate DB One-time Supabase migration script
Smush / ShortPixel Next.js Image component (built-in)
Elementor / WPBakery React components (you won't miss them)

Phase 2: Set Up the New Stack (Week 2)

## Create your Next.js project
npx create-next-app@latest my-site --typescript --tailwind --app --src-dir

## Install Supabase
npm install @supabase/supabase-js @supabase/ssr

## Set up environment variables
cp .env.example .env.local

Your .env.local:

NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key

Deploy to Vercel right away, before you've built anything meaningful. A live preview URL from day one changes how you work. Stakeholders see progress, and you catch deployment issues early.

Data Migration: Getting Your Content Out of WordPress

This is where most migration guides get vague. Let's be specific.

Step 1: Export WordPress Data

Don't use the built-in WordPress XML export. It's incomplete and poorly structured. Instead, use WP-CLI and direct database queries:

## Export posts as JSON
wp post list --post_type=post --format=json --fields=ID,post_title,post_content,post_excerpt,post_date,post_status,post_name > posts.json

## Export pages
wp post list --post_type=page --format=json --fields=ID,post_title,post_content,post_excerpt,post_date,post_status,post_name > pages.json

## Export custom post types
wp post list --post_type=your_cpt --format=json > cpt.json

## Export post meta (ACF fields, etc.)
wp eval 'global $wpdb; $results = $wpdb->get_results("SELECT post_id, meta_key, meta_value FROM wp_postmeta WHERE meta_key NOT LIKE \"_%\""); echo json_encode($results);' > postmeta.json

Step 2: Transform and Load into Supabase

Write a migration script. TypeScript works well for this:

import { createClient } from '@supabase/supabase-js'
import posts from './exports/posts.json'

const supabase = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!
)

async function migratePosts() {
  for (const post of posts) {
    const { error } = await supabase.from('posts').insert({
      wp_id: post.ID,
      title: post.post_title,
      slug: post.post_name,
      content: convertWpContentToMdx(post.post_content),
      excerpt: post.post_excerpt,
      published_at: post.post_date,
      status: post.post_status === 'publish' ? 'published' : 'draft',
    })
    
    if (error) console.error(`Failed to migrate post ${post.ID}:`, error)
  }
}

function convertWpContentToMdx(html: string): string {
  // Use turndown or rehype to convert WordPress HTML to MDX
  // Handle shortcodes, embeds, and Gutenberg blocks
  // This is where 80% of migration complexity lives
}

The convertWpContentToMdx function will take the most time. WordPress content mixes HTML, shortcodes, Gutenberg block comments, and embedded oEmbed URLs. Libraries like turndown handle basic HTML-to-Markdown conversion, but you'll need custom rules for shortcodes and blocks.

Step 3: Migrate Media

import { createClient } from '@supabase/supabase-js'
import fetch from 'node-fetch'

async function migrateMedia(mediaItems: any[]) {
  for (const item of mediaItems) {
    const response = await fetch(item.source_url)
    const buffer = await response.buffer()
    
    const { error } = await supabase.storage
      .from('media')
      .upload(`uploads/${item.slug}.${item.mime_type.split('/')[1]}`, buffer, {
        contentType: item.mime_type,
      })
    
    if (error) console.error(`Failed to upload ${item.slug}:`, error)
  }
}

Building the New Frontend with Next.js

With your data in Supabase, building the frontend is the fun part. Here's a typical blog post page using Next.js App Router:

// src/app/blog/[slug]/page.tsx
import { createClient } from '@/lib/supabase/server'
import { notFound } from 'next/navigation'
import { MDXRemote } from 'next-mdx-remote/rsc'

export async function generateMetadata({ params }: { params: { slug: string } }) {
  const supabase = createClient()
  const { data: post } = await supabase
    .from('posts')
    .select('title, excerpt, og_image')
    .eq('slug', params.slug)
    .single()

  if (!post) return {}

  return {
    title: post.title,
    description: post.excerpt,
    openGraph: { images: [post.og_image] },
  }
}

export default async function BlogPost({ params }: { params: { slug: string } }) {
  const supabase = createClient()
  const { data: post } = await supabase
    .from('posts')
    .select('*')
    .eq('slug', params.slug)
    .eq('status', 'published')
    .single()

  if (!post) notFound()

  return (
    <article className="prose lg:prose-xl mx-auto">
      <h1>{post.title}</h1>
      <time dateTime={post.published_at}>
        {new Date(post.published_at).toLocaleDateString()}
      </time>
      <MDXRemote source={post.content} />
    </article>
  )
}

Notice there's no caching plugin, no performance plugin, and no SEO plugin. The metadata API handles SEO. Server components handle performance. The CDN handles caching. It's all built in.

Setting Up Supabase as Your Backend

Design your Supabase schema around your actual data needs, not WordPress's generic wp_posts / wp_postmeta structure. Here's a cleaner schema:

-- Posts table
create table posts (
  id uuid default gen_random_uuid() primary key,
  title text not null,
  slug text unique not null,
  content text,
  excerpt text,
  featured_image text,
  status text default 'draft' check (status in ('draft', 'published', 'archived')),
  author_id uuid references auth.users(id),
  published_at timestamptz,
  created_at timestamptz default now(),
  updated_at timestamptz default now(),
  metadata jsonb default '{}'
);

-- Categories
create table categories (
  id uuid default gen_random_uuid() primary key,
  name text not null,
  slug text unique not null,
  description text
);

-- Row Level Security
alter table posts enable row level security;

create policy "Published posts are viewable by everyone"
  on posts for select
  using (status = 'published');

create policy "Authors can manage their own posts"
  on posts for all
  using (auth.uid() = author_id);

The metadata jsonb column is your escape hatch. Custom fields that don't need their own column can live there. It's indexed, queryable, and flexible, much like ACF fields but without the plugin.

Handling Authentication and User Data

If your WordPress site has user accounts, Supabase Auth handles the migration cleanly. You can't migrate password hashes directly, since WordPress uses phpass and Supabase uses bcrypt. But you can:

  1. Import user emails and profiles into Supabase
  2. Trigger a "reset your password" flow for all users on first login
  3. Or use magic link authentication so passwords aren't needed at all

Supabase supports email/password, Google, GitHub, Apple, and dozens of other OAuth providers out of the box. No plugin needed.

SEO Preservation: Don't Lose What You've Built

This part is non-negotiable. A botched migration can wipe out years of SEO value overnight. Here's the checklist:

  1. Map every old URL to its new URL. WordPress uses /2024/01/post-title/ by default. Your new site might use /blog/post-title. Every single old URL needs a 301 redirect.

  2. Implement redirects in Next.js:

// next.config.js
module.exports = {
  async redirects() {
    return [
      // Date-based WordPress URLs to clean slugs
      {
        source: '/:year(\\d{4})/:month(\\d{2})/:slug',
        destination: '/blog/:slug',
        permanent: true,
      },
      // Category pages
      {
        source: '/category/:slug',
        destination: '/blog/category/:slug',
        permanent: true,
      },
    ]
  },
}
  1. Preserve all meta titles, descriptions, and structured data. Export them from Yoast before migration.
  2. Submit the new sitemap to Google Search Console right after launch.
  3. Keep the old site running on a subdomain (old.yoursite.com) for 30 days as a fallback.

Performance Benchmarks: Before and After

These are illustrative ranges, not an average pulled from many client engagements. Our SleepDr.com migration, from WordPress to Next.js 15, Payload CMS, and Supabase, took its Lighthouse performance score from 35 to 94. Other Core Web Vitals tend to move the same way when a plugin-heavy WordPress site gets replaced with a statically generated or server-rendered React app on a global CDN:

Metric WordPress (Typical) Next.js + Supabase (Typical) Direction of Change
Lighthouse Performance Score Often in the 30s-40s Often in the 90s Large improvement
Largest Contentful Paint (LCP) Frequently above the 2.5s "good" threshold Usually well under it Significant reduction
Interaction to Next Paint (INP) Often above recommended thresholds Usually within Core Web Vitals guidance Significant improvement
Cumulative Layout Shift (CLS) Often above the 0.1 "good" threshold Usually near zero Significant reduction
Total Page Weight Often several megabytes, driven by plugin scripts Substantially lighter Large reduction
HTTP Requests Often 40 or more per page Typically under 10 Large reduction

Remove 30-plus plugins, each with its own CSS and JavaScript, and swap dynamic PHP rendering for static or server-rendered React on a CDN. That combination drives this shift. In production Next.js builds, this pattern shows up again and again.

Curious what these results could look like for your project? Our pricing page breaks down what headless migration projects typically cost.

Cost Comparison: WordPress vs Headless Stack

WordPress (Annual) Next.js + Supabase (Annual)
Hosting Moderate to high, scales with your managed WordPress tier Vercel's free and Pro tiers cover most sites
Database/Backend Included in hosting, but limited Supabase's free and Pro tiers
Plugin Licenses Several premium plugins renewing annually None
Security Tools Ongoing monitoring and cleanup costs Built in (Vercel platform security + Supabase RLS)
CDN Often a separate paid add-on Included
Maintenance Dev Hours Frequently the largest line item Substantially lower once migration is complete
Total Often the largest recurring line item in a marketing budget Typically a fraction of WordPress's operating cost

The headless stack is usually far cheaper to run year over year. The migration itself has an upfront cost that depends heavily on complexity. See our headless CMS development services for typical project scopes. It often pays for itself within the first year or two through lower operating costs alone, before you even count the revenue impact of better performance and SEO.

FAQ

Do I need to learn React/Next.js to manage my content after migration?

No. Most teams pair Next.js with a headless CMS like Sanity, Contentful, or even WordPress itself used purely as a headless CMS via its REST API. Content editors never touch code. They get a clean editing interface while the frontend pulls content via API. If you want to keep the WordPress editor your team already knows, you can. Just remove the WordPress frontend and use it as a content backend.

How long does a typical WordPress to Next.js migration take?

For a content-focused site with a blog and standard pages, expect 4-6 weeks. For a site with e-commerce, user accounts, custom post types, and complex functionality, expect 8-14 weeks. The biggest variable is content complexity: sites with heavily shortcode-dependent content or deeply customized Gutenberg blocks take longer to migrate cleanly.

Will I lose my Google rankings during migration?

Not if you handle redirects properly. Well-implemented 301 redirects preserve most of your link equity, though rarely all of it. Sites typically see a small dip in the first one to two weeks after migration while Google recrawls, followed by better rankings from improved Core Web Vitals scores. The key is mapping every single URL and waiting to launch until your redirect map is complete.

Is Supabase production-ready for high-traffic sites?

Yes. Supabase runs on AWS infrastructure, and its database is just PostgreSQL, one of the most battle-tested databases in production use. Supabase describes its own scale in terms of millions of databases and a very high volume of daily API requests across its platform. For extra scale, Supabase's Pro and Team plans add dedicated resources and priority support.

Can I migrate WooCommerce to this stack?

You can, but e-commerce adds real complexity. Most teams migrating from WooCommerce go to either Shopify, using the Storefront API with a Next.js frontend, or an open-source option like Medusa.js or Saleor. Supabase can handle product catalogs and order management, but you'd need to build checkout, payment processing, inventory management, and tax calculation yourself. For most businesses, a dedicated e-commerce backend connected to Next.js makes more sense.

What about WordPress multisite -- can this stack replace it?

Yes. Next.js supports multi-tenant setups through middleware and dynamic routing, and Supabase's Row Level Security makes it easy to split data by tenant. Together they can replace a WordPress multisite network with a single application that routes each tenant to its own content and settings, which simplifies operations a lot.

Do I still need a CMS, or can I just use Supabase directly?

Supabase gives you a table editor that works fine for developers, but content editors usually want something more polished. Common approaches: use a dedicated headless CMS like Sanity or Storyblok for content while Supabase handles application data, build a simple admin UI with Next.js and Supabase Auth, or keep WordPress as a headless CMS backend. The first option is most popular for content-heavy sites. We cover the tradeoffs in our Astro development and headless CMS pages.

What if the migration goes wrong -- can I roll back to WordPress?

Yes, and you should plan for this ahead of time. Keep your WordPress site running on a subdomain throughout the migration, and use DNS-level switching (changing your A record or CNAME) so you can roll back within minutes. Keep the old WordPress instance running for at least 30 days after launch, and only shut it down once redirects, rankings, and functionality are all confirmed. Want help planning a migration with proper rollback steps? Reach out to our team.

Frequently Asked Questions

How long does a typical WordPress to Next.js migration take?

Most migrations finish within 4-8 weeks depending on site complexity and content volume. Simple blogs move faster than e-commerce sites with custom functionality. The timeline covers content export, database restructuring, frontend development, SEO preservation, and thorough testing before launch.

Will I lose my current search engine rankings during migration?

Careful migration planning preserves your SEO value through URL mapping and 301 redirects. Next.js can even improve rankings over time through faster load times and better Core Web Vitals. Keeping all metadata, structured data, and server-side rendering in place helps maintain crawlability throughout the switch.

Can I keep my existing WordPress content and media files?

All your posts, pages, images, and media transfer completely to the new system. Content gets exported from WordPress, restructured for Supabase's database, and media gets moved to modern hosting. Your content stays intact while gaining better performance and management tools.

What happens to my WordPress plugins after switching platforms?

Most plugin functionality gets rebuilt as native features or handled through modern APIs. Contact forms, SEO tools, and analytics become lightweight code instead of bloated plugins. This removes compatibility issues and cuts security risk while keeping the features your business needs.

Is Supabase reliable enough to replace my WordPress database?

Supabase provides managed PostgreSQL databases with automatic backups and scaling, running on established cloud infrastructure. It handles higher traffic than typical WordPress setups while offering real-time features and strong default security. Many production applications already rely on Supabase for critical data storage.

How much does migration cost compared to WordPress maintenance?

Initial migration cost varies widely based on complexity, from a straightforward blog rebuild to a full e-commerce platform. You do cut ongoing costs for premium plugins, managed WordPress hosting, and third-party security services. Many businesses recover the investment within a year or two through lower maintenance costs and faster site performance.

Key takeaway: Modern stacks cut costs. Next.js and Supabase replace most of what WordPress plugins used to do.

Related guide: 7 Signs You've Outgrown WordPress -- the full breakdown of your options.