TL;DR

Die Migration von WordPress zu Next.js im Jahr 2026 bringt messbare Leistungsgewinne: Die durchschnittliche TTFB sinkt von 1.200 ms auf 85 ms, das Seitengewicht schrumpft von 3,2 MB auf 620 KB, und die Lighthouse-Scores springen von 42 auf 94. Der Prozess umfasst den Export von Inhalten über die WP REST API zu Supabase oder Payload CMS, die Migration von Medien zu Object Storage, das Mapping aller URLs für 301-Weiterleitungen, die Beibehaltung von Yoast/RankMath SEO-Daten in der Next.js Metadata API und das Ersetzen von Plugins wie Gravity Forms durch Server Actions. Für WooCommerce-Seiten ersetzt Stripe den gesamten Commerce-Stack. Rechnen Sie mit 4-8 Wochen für typische Seiten mit 100-500 Seiten, wobei die größte Zeitinvestition beim Testen von Weiterleitungen und dem Rebuild von Templates liegt.

Dies ist keine flüchtige Meinung. Ich entwickle seit über 12 Jahren mit WordPress. Ich habe Agentur-Websites, Membership-Plattformen, WooCommerce-Stores mit sechsstelligen monatlichen Umsätzen und mehr Custom Post Types als ich zählen kann, herausgebracht. Ich habe auch Produktionsseiten zu Next.js + Supabase migriert. Hier sind alle technischen Details -- was sauber abbildet, was nicht, und was Sie planen müssen.

Ich werde nicht so tun, als ob WordPress schlecht ist. Das ist es nicht. Es betreibt 43 % des Webs aus gutem Grund. Aber für bestimmte Projekte -- Seiten, bei denen Performance eine Geschäftsmetrik ist, bei denen die Sicherheitsoberfläche wichtig ist, bei denen Sie Ihre Deployment-Pipeline kontrollieren möchten -- ist Next.js das bessere Werkzeug. Die Migration aber? Sie ist ein Minenfeld, wenn Sie nicht planen.

Dieser Leitfaden behandelt den genauen Prozess, den ich verwende, mit echtem Code, echten Fallstricken und ehrlichen Bewertungen dessen, was Sie gewinnen und was Sie verlieren werden.

Inhaltsverzeichnis

WordPress to Next.js Migration: A Complete Technical Guide

Content Migration: WP REST API zu Supabase oder Payload CMS

Jede WordPress-Migration beginnt hier. Sie haben Posts, Pages, Custom Post Types, ACF-Felder, Taxonomien -- Jahre von Inhalten, die irgendwo sicher landen müssen.

Sie haben zwei solide Optionen, wo diese Inhalte hingehen:

  • Supabase -- wenn Sie eine Datenbank wünschen, die Sie vollständig kontrollieren, mit Row-Level Security und einer REST/GraphQL API out-of-the-box
  • Payload CMS -- wenn Ihr Kunde eine Bearbeitungserfahrung benötigt, die sich nach WordPress anfühlt

Für unsere Headless CMS Development Projekte bewerten wir dies pro Klient. Payload gewinnt, wenn Redakteure Selbstbedienung brauchen. Supabase gewinnt, wenn Entwickler die primären Content Manager sind oder wenn Sie die Daten für mehr als nur eine Website benötigen.

Welche Content-Struktur sollte ich beim Migrating von WordPress zu Next.js beibehalten?

Behalten Sie Post-Metadaten, Taxonomien, Custom Fields und URL-Slugs während der Migration bei. Ihre WordPress-Posts enthalten Jahre strukturierter Daten: Kategorien, Tags, ACF-Felder, Featured Images und Veröffentlichungsdaten. Exportieren Sie alles davon über die WP REST API mit dem _embed-Parameter, um Media-URLs in einer einzelnen Anfrage zu bekommen. Speichern Sie sowohl HTML- als auch Markdown-Versionen von Inhalten -- HTML als Fallback, Markdown für MDX-Rendering. Bilden Sie Custom Post Types auf entsprechende Datenbanktabellen oder CMS-Sammlungen in Ihrem neuen System ab.

Das Export-Skript

Hier ist das Node.js-Skript, das ich verwende, um Inhalte aus der WP REST API zu ziehen, aufzuräumen und in Supabase einzufügen. Dies behandelt Posts, aber Sie würden das Muster für Pages und CPTs duplizieren (ändern Sie einfach den Endpunkt).

import { createClient } from '@supabase/supabase-js';
import TurndownService from 'turndown';

const supabase = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_SERVICE_KEY
);

const turndown = new TurndownService({
  headingStyle: 'atx',
  codeBlockStyle: 'fenced',
});

const WP_API = 'https://yoursite.com/wp-json/wp/v2';

async function fetchAllPosts() {
  let page = 1;
  let allPosts = [];
  let hasMore = true;

  while (hasMore) {
    const res = await fetch(
      `${WP_API}/posts?per_page=100&page=${page}&_embed`
    );

    if (!res.ok) break;

    const posts = await res.json();
    allPosts = allPosts.concat(posts);

    const totalPages = parseInt(res.headers.get('X-WP-TotalPages'));
    hasMore = page < totalPages;
    page++;
  }

  return allPosts;
}

async function migrateContent() {
  const posts = await fetchAllPosts();
  console.log(`Fetched ${posts.length} posts from WordPress`);

  const transformed = posts.map((post) => ({
    wp_id: post.id,
    title: post.title.rendered,
    slug: post.slug,
    content_html: post.content.rendered,
    content_markdown: turndown.turndown(post.content.rendered),
    excerpt: post.excerpt.rendered.replace(/<[^>]*>/g, '').trim(),
    published_at: post.date,
    status: post.status,
    featured_image:
      post._embedded?.['wp:featuredmedia']?.[0]?.source_url || null,
    categories:
      post._embedded?.['wp:term']?.[0]?.map((t) => t.name) || [],
    tags:
      post._embedded?.['wp:term']?.[1]?.map((t) => t.name) || [],
  }));

  const { data, error } = await supabase
    .from('posts')
    .upsert(transformed, { onConflict: 'wp_id' });

  if (error) {
    console.error('Migration failed:', error);
  } else {
    console.log(`Migrated ${transformed.length} posts to Supabase`);
  }
}

migrateContent();

Ein paar Dinge, die ich auf die harte Tour gelernt habe:

  • Verwenden Sie immer _embed bei Ihren WP REST API-Aufrufen. Ohne sie erhalten Sie Media-IDs statt URLs, was N+1-Anfragen bedeutet, um Featured Images aufzulösen.
  • Turndown konvertiert HTML zu Markdown -- das ist kritisch, wenn Sie später mit MDX rendern möchten. Behalten Sie das Original-HTML auch als Fallback.
  • Shortcodes überleben nicht. WordPress rendert einige Shortcodes über die REST API, aber viele (besonders von Plugins wie WPBakery oder Elementor) kommen als roher Bracket-Text durch. Sie benötigen eine Shortcode-zu-Component-Mapping-Strategie. Ich behalte eine Tabellenkalkulation.
  • ACF / Custom Fields: Wenn Sie ACF verwenden, benötigen Sie das Plugin "ACF to REST API" aktiviert, dann erscheinen die Custom Fields in der acf-Eigenschaft jedes Post-Objekts.

Supabase Tabellenschema

CREATE TABLE posts (
  id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
  wp_id INTEGER UNIQUE,
  title TEXT NOT NULL,
  slug TEXT UNIQUE NOT NULL,
  content_html TEXT,
  content_markdown TEXT,
  excerpt TEXT,
  published_at TIMESTAMPTZ,
  status TEXT DEFAULT 'publish',
  featured_image TEXT,
  categories TEXT[],
  tags TEXT[],
  seo_title TEXT,
  seo_description TEXT,
  og_image TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

Beachten Sie, dass ich seo_title-, seo_description- und og_image-Spalten einbezogen habe. Sie benötigen diese im SEO-Migrations-Abschnitt unten.

Media Migration: Von wp-content zu Supabase Storage

Dies ist der Teil, den die meisten Leitfäden übergehen, und es ist der Teil, der am längsten dauert. Eine 12 Jahre alte WordPress-Site kann leicht 10.000+ Dateien in wp-content/uploads/ haben.

Der Ansatz:

  1. Laden Sie das gesamte wp-content/uploads/-Verzeichnis herunter
  2. Laden Sie es zu Supabase Storage hoch (oder Cloudflare R2 oder S3)
  3. Schreiben Sie jede Media-URL in Ihrem Inhalt um

Wie migriere ich WordPress-Media-Dateien zu modernem Object Storage?

Laden Sie Ihr gesamtes wp-content/uploads/-Verzeichnis herunter, laden Sie es zu Supabase Storage oder Cloudflare R2 hoch, dann schreiben Sie alle Media-URLs in Ihrem Inhalt um. Verwenden Sie ein Skript, um jede Bild-URL aus Ihrem WordPress-Inhalt zu holen, laden Sie es zu Object Storage hoch und behalten dabei die Verzeichnisstruktur bei (2024/03/image.jpg), führen Sie dann einen zweiten Pass durch, um alte URLs durch neue Storage-URLs zu ersetzen. Richten Sie Wildcard-Weiterleitungen für externe Sites ein, die direkt auf Ihre alten Bild-URLs verlinken.

Download- und Upload-Skript

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

const supabase = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_SERVICE_KEY
);

const BUCKET = 'media';

async function migrateMedia(posts) {
  const urlRegex =
    /https?:\/\/yoursite\.com\/wp-content\/uploads\/[^\s"')]+/g;

  for (const post of posts) {
    const urls = post.content_html.match(urlRegex) || [];

    for (const url of urls) {
      try {
        const res = await fetch(url);
        const buffer = Buffer.from(await res.arrayBuffer());

        // Preserve directory structure: 2024/03/image.jpg
        const storagePath = url.replace(
          /https?:\/\/yoursite\.com\/wp-content\/uploads\//,
          ''
        );

        const { error } = await supabase.storage
          .from(BUCKET)
          .upload(storagePath, buffer, {
            contentType: res.headers.get('content-type'),
            upsert: true,
          });

        if (error) console.error(`Failed: ${storagePath}`, error);
        else console.log(`Uploaded: ${storagePath}`);
      } catch (e) {
        console.error(`Skipped: ${url}`, e.message);
      }
    }
  }
}

async function rewriteUrls() {
  const { data: posts } = await supabase.from('posts').select('*');
  const supabaseBase = `${process.env.SUPABASE_URL}/storage/v1/object/public/${BUCKET}`;

  for (const post of posts) {
    const updated = post.content_html.replace(
      /https?:\/\/yoursite\.com\/wp-content\/uploads\//g,
      `${supabaseBase}/`
    );

    await supabase
      .from('posts')
      .update({
        content_html: updated,
        content_markdown: turndown.turndown(updated),
      })
      .eq('id', post.id);
  }
}

Der Image-Performance-Gewinn

Hier zahlt sich die Migration wirklich aus. WordPress serviert Original-Uploads -- oft 3000×2000px PNGs, die jemand von seiner DSLR hochgeladen hat. Auch mit einem Plugin wie ShortPixel servieren Sie Bilder immer noch über PHP.

Die Next.js <Image>-Komponente mit next/image führt automatische Format-Verhandlung (WebP/AVIF), responsive Größenanpassung und Lazy Loading durch. Die Zahlen aus unserer letzten Migration:

Metrik WordPress Next.js + Image Component
Durchschnittliches Seitenbildgewicht 2,1 MB 380 KB
Bildanfragen 12 pro Seite 6 pro Seite (Lazy Loading)
Format JPEG/PNG WebP (AVIF wo unterstützt)
Kumulative Layout-Verschiebung 0,18 0,02

Das ist kein Tippfehler. Das durchschnittliche Bild-Payload sank von 2,1 MB auf 380 KB. Und das war ohne neu hochgeladene optimierte Dateien -- nur durch next/image.

import Image from 'next/image';

export function PostImage({ src, alt }: { src: string; alt: string }) {
  return (
    <Image
      src={src}
      alt={alt}
      width={800}
      height={450}
      sizes="(max-width: 768px) 100vw, 800px"
      quality={80}
      placeholder="blur"
      blurDataURL="data:image/jpeg;base64,..." // generate at build time
    />
  );
}

URL-Struktur: Mapping jeder alten URL

Hier sterben Migrationen. Eine verpasste Weiterleitung bedeutet einen 404 für eine Seite, die Google jahrelang indexiert hat. Ich behandle URL-Mapping mit derselben Ernsthaftigkeit wie eine Datenbankmigration -- testen Sie sie, verifizieren Sie sie, und verifizieren Sie sie dann erneut.

Wie ist die korrekte Weise, um URL-Weiterleitungen während der WordPress-Migration zu handhaben?

Exportieren Sie jede veröffentlichte URL aus WordPress, kreuzen Sie sie mit Google Search Console indexierten URLs ab, und implementieren Sie dann 301-Weiterleitungen für alle. Fragen Sie jede veröffentlichte URL aus Ihrer wp_posts-Tabelle ab, exportieren Sie GSC's indexierte URLs, und erstellen Sie eine Redirect-Map. Verwenden Sie next.config.js Weiterleitungen für unter 50 URLs, eine JSON-Datei für 50-1.024 URLs oder Middleware für Sites, die das Vercel-Limit von 1.024 Weiterleitungen überschreiten. Schließen Sie Wildcard-Weiterleitungen für Category Pages, Pagination und wp-content/uploads-Pfade ein.

Der Mapping-Prozess

Zuerst exportieren Sie jede URL aus WordPress. Ich hole direkt aus der Datenbank:

SELECT
  CONCAT('/', post_name, '/') AS old_url,
  post_type,
  post_status
FROM wp_posts
WHERE post_status = 'publish'
  AND post_type IN ('post', 'page', 'product')
ORDER BY post_type, post_name;

Dann kreuzen Sie mit Google Search Console's indexierten URLs ab. GSC zeigt oft URLs an, die nicht mehr in Ihrer Datenbank existieren -- alte Category Pages, Pagination-URLs, Attachment Pages. Sie benötigen Weiterleitungen für alle.

next.config.js Weiterleitungen

Für Sites mit unter 50 Weiterleitungen, inline sie:

// next.config.js
module.exports = {
  async redirects() {
    return [
      {
        source: '/2019/03/old-post-slug/',
        destination: '/blog/old-post-slug',
        permanent: true,
      },
      {
        source: '/category/:slug',
        destination: '/blog/category/:slug',
        permanent: true,
      },
      {
        source: '/product/:slug',
        destination: '/shop/:slug',
        permanent: true,
      },
    ];
  },
};

Für 200+ Weiterleitungen: Verwenden Sie eine JSON-Datei

Sobald Sie über ein paar hundert hinausgehen, ist die Verwaltung von Inline-Weiterleitungen furchtbar. Ich verwende eine JSON-Datei:

// redirects.json
[
  {
    "source": "/2018/01/my-old-post/",
    "destination": "/blog/my-old-post",
    "permanent": true
  },
  {
    "source": "/about-us/",
    "destination": "/about",
    "permanent": true
  },
  {
    "source": "/wp-content/uploads/:path*",
    "destination": "https://yourbucket.supabase.co/storage/v1/object/public/media/:path*",
    "permanent": true
  }
]
// next.config.js
const redirectsList = require('./redirects.json');

module.exports = {
  async redirects() {
    return redirectsList;
  },
};

Die Wildcard-Weiterleitung für wp-content/uploads ist kritisch. Es wird externe Sites geben, die direkt auf Ihre Bilder verlinken. Verlieren Sie diese Backlinks nicht.

Wichtig: Vercel hat ein Limit von 1.024 Weiterleitungen in next.config.js. Für Sites mit mehr als diesem, verwenden Sie Middleware:

// middleware.ts
import { NextResponse } from 'next/server';
import redirects from './redirects.json';

const redirectMap = new Map(
  redirects.map((r) => [r.source, r])
);

export function middleware(request) {
  const redirect = redirectMap.get(request.nextUrl.pathname);
  if (redirect) {
    return NextResponse.redirect(
      new URL(redirect.destination, request.url),
      redirect.permanent ? 308 : 307
    );
  }
}

WordPress to Next.js Migration: A Complete Technical Guide - architecture

SEO Migration: Yoast und RankMath Daten zu Next.js Metadata

Wenn Sie Yoast oder RankMath verwendet haben, haben Sie Jahre von Custom Meta Titles, Descriptions und Open Graph Daten in der wp_postmeta-Tabelle gespeichert. Verlieren Sie sie nicht.

Wie behalte ich Yoast SEO-Daten beim Migrating zu Next.js bei?

Exportieren Sie Yoast Meta Titles, Descriptions und Open Graph Images aus wp_postmeta, speichern Sie sie in Ihrer neuen Datenbank, dann rendern Sie sie mit der Next.js Metadata API. Fragen Sie wp_postmeta für _yoast_wpseo_title, _yoast_wpseo_metadesc und _yoast_wpseo_opengraph-image Felder ab. Importieren Sie diese Daten in dedizierte SEO-Spalten in Ihrer Posts-Tabelle. Verwenden Sie generateMetadata in Next.js App Router, um diese Daten als richtige Meta-Tags und Open Graph-Markup zu rendern.

Export von SEO-Daten

SELECT
  p.post_name AS slug,
  MAX(CASE WHEN pm.meta_key = '_yoast_wpseo_title' THEN pm.meta_value END) AS seo_title,
  MAX(CASE WHEN pm.meta_key = '_yoast_wpseo_metadesc' THEN pm.meta_value END) AS seo_description,
  MAX(CASE WHEN pm.meta_key = '_yoast_wpseo_opengraph-image' THEN pm.meta_value END) AS og_image
FROM wp_posts p
JOIN wp_postmeta pm ON p.ID = pm.post_id
WHERE p.post_status = 'publish'
GROUP BY p.ID, p.post_name;

Für RankMath, tauschen Sie die Meta-Keys: rank_math_title, rank_math_description, rank_math_facebook_image.

Importieren Sie diese Daten in die Supabase posts-Tabelle in den SEO-Spalten, die wir oben definiert haben.

Next.js Metadata API

Mit dem App Router ist Metadata ein erstklassiger Bürger:

// app/blog/[slug]/page.tsx
import { supabase } from '@/lib/supabase';
import { Metadata } from 'next';

export async function generateMetadata(
  { params }: { params: { slug: string } }
): Promise<Metadata> {
  const { data: post } = await supabase
    .from('posts')
    .select('title, seo_title, seo_description, og_image')
    .eq('slug', params.slug)
    .single();

  return {
    title: post.seo_title || post.title,
    description: post.seo_description,
    openGraph: {
      title: post.seo_title || post.title,
      description: post.seo_description,
      images: post.og_image ? [{ url: post.og_image }] : [],
    },
  };
}

Schema Markup als JSON-LD Server Components

WordPress-Plugins generieren Schema automatisch. In Next.js bauen Sie es selbst -- was Ihnen eigentlich mehr Kontrolle gibt:

// components/ArticleSchema.tsx
export function ArticleSchema({ post }) {
  const schema = {
    '@context': 'https://schema.org',
    '@type': 'Article',
    headline: post.title,
    datePublished: post.published_at,
    dateModified: post.updated_at || post.published_at,
    author: {
      '@type': 'Organization',
      name: 'Your Company',
    },
    image: post.og_image,
    description: post.seo_description,
  };

  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
    />
  );
}

Dynamische Sitemaps

// app/sitemap.ts
import { supabase } from '@/lib/supabase';

export default async function sitemap() {
  const { data: posts } = await supabase
    .from('posts')
    .select('slug, published_at')
    .eq('status', 'publish');

  return posts.map((post) => ({
    url: `https://yoursite.com/blog/${post.slug}`,
    lastModified: post.published_at,
    changeFrequency: 'monthly',
    priority: 0.8,
  }));
}

Dies wird zur Build-Zeit für statische Sites oder bei Bedarf für dynamische generiert. Kein Plugin, keine XML-Template-Dateien, keine Caching-Probleme.

Formulare: Gravity Forms zu Server Actions

Gravity Forms ist eines der besten WordPress-Plugins, die es je gab. Es kostet auch $259/Jahr für die Elite-Lizenz, und jedes Formular lädt 200 KB+ JavaScript.

Hier ist der Ersatz. Es sind etwa 20 Zeilen Code pro Formular.

Export bestehender Einträge

Zuerst exportieren Sie Ihre Gravity Forms-Einträge als CSV aus dem WordPress-Admin. Speichern Sie sie in Supabase für historische Aufzeichnungen, wenn nötig.

Server Action Kontaktformular

// app/contact/page.tsx
export default function ContactPage() {
  async function submitForm(formData: FormData) {
    'use server';

    const { createClient } = await import('@supabase/supabase-js');
    const supabase = createClient(
      process.env.SUPABASE_URL!,
      process.env.SUPABASE_SERVICE_KEY!
    );

    const entry = {
      name: formData.get('name') as string,
      email: formData.get('email') as string,
      message: formData.get('message') as string,
      submitted_at: new Date().toISOString(),
    };

    // Validate
    if (!entry.name || !entry.email || !entry.message) {
      throw new Error('All fields required');
    }

    await supabase.from('form_submissions').insert(entry);

    // Optional: send notification email via Resend
    await fetch('https://api.resend.com/emails', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.RESEND_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        from: 'forms@yoursite.com',
        to: 'team@yoursite.com',
        subject: `New contact: ${entry.name}`,
        html: `<p>${entry.message}</p><p>From: ${entry.email}</p>`,
      }),
    });
  }

  return (
    <form action={submitForm}>
      <input name="name" type="text" required placeholder="Name" />
      <input name="email" type="email" required placeholder="Email" />
      <textarea name="message" required placeholder="Message" />
      <button type="submit">Send</button>
    </form>
  );
}

Kein Plugin. Keine JavaScript-Last für das Formular selbst (es ist ein natives HTML-Formular mit einer Server Action). Progressive Enhancement -- es funktioniert ohne JavaScript aktiviert.

Für komplexere Formulare (Multi-Step, File Uploads, bedingte Felder) verwenden wir React Hook Form auf der Client-Seite mit dem gleichen Server Action-Muster. Der Schlüssel-Insight: Sie benötigen kein Form-Plugin, wenn Sie eine Datenbank und eine API haben.

WooCommerce zu Stripe

Dies ist der schwierigste Teil jeder WordPress-Migration. WooCommerce ist nicht nur ein Plugin -- es ist eine Commerce-Plattform mit Produkten, Variationen, Inventar, Bestellungen, Abonnements, Gutscheinen, Steuern und Versandregeln. Sie migrieren nicht einfach ein Feature. Sie ersetzen eine Plattform.

Wie migriere ich WooCommerce-Produkte zu Stripe?

Exportieren Sie Produkte über die WooCommerce REST API oder CSV, erstellen Sie dann entsprechende Produkte in der Stripe Products API mit Preisen. Für Sites unter 500 Produkten, pushen Sie direkt zu Stripe mit ihrer API: erstellen Sie ein Produkt mit Name, Beschreibung und Bildern, erstellen Sie dann ein Price-Objekt, das mit diesem Produkt verlinkt ist. Speichern Sie die WooCommerce-Produkt-ID in Stripe-Metadaten als Referenz. Verwenden Sie Stripe Checkout Sessions für die Zahlungsverarbeitung und Webhooks, um Bestellungen in Ihrer Datenbank zu verfolgen.

Produktmigration

Exportieren Sie Produkte aus WooCommerce über CSV oder REST API. Sie haben zwei Zieloptionen:

Ansatz Beste für Tradeoffs
Supabase-Produkttabelle Custom Storefronts, komplexes Filtern Sie verwalten Inventarlogik
Stripe Products API Einfache Kataloge, Subscription-Businesses Stripe verwaltet Preisgestaltung, Sie verwalten Anzeige

Für die meisten Sites unter 500 Produkten pushe ich direkt zu Stripe Products:

import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

async function migrateProducts(wooProducts) {
  for (const product of wooProducts) {
    const stripeProduct = await stripe.products.create({
      name: product.name,
      description: product.short_description,
      images: [product.images[0]?.src].filter(Boolean),
      metadata: {
        woo_id: String(product.id),
        slug: product.slug,
        sku: product.sku,
      },
    });

    await stripe.prices.create({
      product: stripeProduct.id,
      unit_amount: Math.round(parseFloat(product.price) * 100),
      currency: 'usd',
    });

    console.log(`Created: ${product.name} → ${stripeProduct.id}`);
  }
}

Checkout mit Stripe Checkout Sessions

// app/api/checkout/route.ts
import { NextResponse } from 'next/server';
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(request: Request) {
  const { priceId, quantity = 1 } = await request.json();

  const session = await stripe.checkout.sessions.create({
    mode: 'payment',
    payment_method_types: ['card'],
    line_items: [{ price: priceId, quantity }],
    success_url: `${process.env.NEXT_PUBLIC_URL}/order/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.NEXT_PUBLIC_URL}/shop`,
  });

  return NextResponse.json({ url: session.url });
}

Abonnements

Wenn Sie auf WooCommerce Subscriptions ($239/Jahr) sind, wechseln Sie zu Stripe Billing. Ändern Sie mode: 'payment' zu mode: 'subscription' und stellen Sie sicher, dass Ihre Preise recurring gesetzt haben. Das war es. Stripe behandelt Probezeiten, Anteilsmäßigkeit und Dunning.

Order Tracking über Webhooks

// app/api/webhooks/stripe/route.ts
import { headers } from 'next/headers';
import Stripe from 'stripe';
import { supabase } from '@/lib/supabase';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(request: Request) {
  const body = await request.text();
  const sig = headers().get('stripe-signature')!;

  const event = stripe.webhooks.constructEvent(
    body,
    sig,
    process.env.STRIPE_WEBHOOK_SECRET!
  );

  if (event.type === 'checkout.session.completed') {
    const session = event.data.object as Stripe.Checkout.Session;

    await supabase.from('orders').insert({
      stripe_session_id: session.id,
      customer_email: session.customer_details?.email,
      amount_total: session.amount_total,
      status: 'completed',
    });
  }

  return new Response('OK', { status: 200 });
}

Stripe's Transaktionsgebühren sind 2,9 % + 0,30 $ pro Transaktion. Vergleichen Sie das mit WooCommerce, wo Sie für Hosting ($30-100/Monat für verwaltete WP), das Subscriptions-Plugin ($239/Jahr), ein Payment-Gateway-Plugin und wahrscheinlich ein paar andere Add-ons zahlen. Die Mathematik funktioniert schnell.

Für komplexe Commerce-Migrationen bieten wir dies als Teil unserer Next.js Development Services an -- es ist eine der häufigsten Anfragen, die wir bekommen.

Post-Migration Überwachung

Die Anwendung ist nicht das Ende. Die ersten zwei Wochen nach der Migration sind kritisch.

Google Search Console

  • Reichen Sie Ihre neue Sitemap sofort ein
  • Verwenden Sie das URL Inspection Tool, um Indexierung Ihrer Top 20 Pages anzufordern
  • Überwachen Sie den Coverage Report täglich in der ersten Woche -- achten Sie auf 404-Spitzen
  • Überprüfen Sie den "Page Indexing" Report auf Pages, die in "Discovered -- currently not indexed" stecken

Analytics Vergleich

Richten Sie ein Dashboard ein, das wöchentlich vergleicht:

  • Gesamte Sessions
  • Organic Search Traffic speziell
  • Bounce Rate nach Page
  • Konversionsrate (Form Submissions, Käufe)

Ein kleiner Traffic-Rückgang in Woche eins ist normal. Wenn er bis Woche drei nicht wieder hergestellt wurde, ist etwas schiefgelaufen mit Weiterleitungen oder Indexierung.

Lighthouse Audits

Führen Sie Lighthouse auf jedem großen Template aus (Homepage, Blog Post, Product Page, Contact Page). Ziel:

  • Performance: 90+
  • Accessibility: 95+
  • Best Practices: 95+
  • SEO: 100

Bei unserer letzten Migration -- einer 400-seitigen Content-Site -- stieg der durchschnittliche Lighthouse Performance Score auf WordPress von 38 auf 96 auf Next.js deployed zu Vercel. Das ist nicht cherry-gepickt. Das ist der Durchschnitt.

Wann sollte ich bei WordPress bleiben

Hier ist der Teil, wo ich einige von Ihnen verliere, aber es ist der wichtigste Abschnitt in diesem Leitfaden.

Nicht migrieren, wenn:

  • Sie eine einfache Blog- oder Brochure-Website unter 20 Seiten haben
  • Ihr Team ist nicht-technisch und verlässt sich täglich auf den WordPress-Admin für Updates
  • Ihre Lighthouse-Scores sind bereits 70+ und Sie haben keine Performance-kritischen Geschäftsanforderungen
  • Sie haben keine Sicherheitsprobleme und Ihr Hosting ist stabil
  • Ihre gesamten Plugin-Kosten unter $200/Jahr liegen
  • Sie haben keinen Entwickler (oder Budget dafür), um eine Next.js-Site zu warten

WordPress mit einem guten Host (Cloudways, Kinsta), einem soliden Theme und minimalen Plugins ist in Ordnung. Tatsächlich, es ist mehr als in Ordnung -- es ist bewährt, gut dokumentiert und wird von Millionen von Entwicklern verstanden.

Die Migration macht Sinn, wenn:

  • Performance direkt an Umsatz gebunden ist (E-Commerce, SaaS Marketing Sites)
  • Sie $500+/Monat für verwaltetes Hosting und Sicherheits-Plugins ausgeben
  • Ihr Development Team bereits React schreibt
  • Sie eine Deployment-Pipeline mit Preview Builds, Staging-Umgebungen und Rollbacks benötigen
  • Sicherheitsoberfläche ist eine echte Bedenken (Regierung, Gesundheitswesen, Finanzen)

Ich sage das, weil Vertrauen wichtiger ist als ein Verkauf. Wenn Sie nicht sicher sind, ob eine Migration sich lohnt, kontaktieren Sie uns und wir geben Ihnen eine ehrliche Bewertung.

Performance Benchmarks: Vorher und nachher

Aus unseren letzten fünf Migrationen in 2024-2025:

Metrik WordPress (Durchschnitt) Next.js (Durchschnitt) Veränderung
TTFB 1.200 ms 85 ms 14x schneller
LCP 3,8 s 0,9 s 4,2x schneller
Gesamtes Seitengewicht 3,2 MB 620 KB 5x leichter
Anfragen pro Seite 47 11 77 % weniger
Lighthouse Performance 42 94 +52 Punkte
Monatliche Hosting-Kosten $75 $20 (Vercel Pro) 73 % Ersparnisse
Core Web Vitals Pass Rate 31 % of pages 100 % of pages

Dies sind echte Zahlen aus Produktionsseiten. Die WordPress-Sites liefen auf verwaltetem Hosting (WP Engine und Kinsta), optimiertem Caching und Image Optimization Plugins. Sie waren nicht vernachlässigt -- es waren gepflegte Sites, die einfach die Obergrenze dessen erreicht hatten, was WordPress liefern kann.

Wenn Sie interessiert sind, was mit modernen Frameworks möglich ist, schauen Sie sich auch unsere Astro Development Fähigkeiten an -- für Content-schwere Sites mit minimaler Interaktivität kann Astro noch kleinere Payloads als Next.js liefern.

Häufig gestellte Fragen

Wie lange dauert eine WordPress zu Next.js Migration?

Für eine typische Site mit 100-500 Seiten, rechnen Sie mit 4-8 Wochen Entwicklungszeit. Einfache Brochure-Sites können in 2-3 Wochen erledigt sein. Komplexe WooCommerce-Stores mit Tausenden von Produkten könnten 10-12 Wochen dauern. Die Content-Migration selbst ist schnell -- es ist das Rebuilding der Frontend-Templates und das Testen jeder Weiterleitung, das Zeit in Anspruch nimmt.

Werde ich SEO-Rankings verlieren, wenn ich von WordPress zu Next.js migriere?

Nein, wenn Sie Weiterleitungen und Metadaten korrekt handhaben. Die kritischen Teile sind: 301-Weiterleitungen für jede alte URL, Migration aller Yoast/RankMath Meta-Titles und Descriptions, Beibehaltung Ihrer Sitemap-Struktur und sofortige Einreichung der neuen Sitemap bei Google Search Console. Wir haben Sites gesehen, die sich innerhalb von 1-2 Wochen auf Pre-Migration Traffic erholt haben, mit signifikantem organischen Wachstum bei Monat drei aufgrund verbesserter Core Web Vitals.

Kann ich WordPress als Headless CMS mit Next.js verwenden?

Ja, und es ist ein populärer Ansatz. Sie behalten WordPress als Content-Backend, mit WP REST API oder WPGraphQL, und Next.js als Frontend. Dies behält die vertraute Edit-Erfahrung, während Sie Next.js Performance bekommen. Der Nachteil ist, dass Sie immer noch eine WordPress-Installation mit seiner Sicherheit und Update-Overhead warten. Wir empfehlen generell Payload CMS oder Sanity für neue Projekte, es sei denn, das Team ist tief in WordPress-Workflows investiert.

Wie viel kostet es, von WordPress zu Next.js zu migrieren?

DIY mit Entwickler-Zeit: kostenlos in Werkzeugen, aber budget 80-200 Stunden Entwicklungszeit. Agentur-Kosten: typischerweise $10.000-$50.000 abhängig von Site-Komplexität, Seitenanzahl, E-Commerce Features und Custom-Funktionalität. Überprüfen Sie unsere Pricing Page für Details zu unseren Paketen. Der ROI kommt normalerweise von reduzierten Hosting-Kosten ($50-100/Monat Ersparnisse), eliminierten Plugin-Lizenzgebühren und erhöhten Konversionsraten durch bessere Performance.

Was passiert mit meinen WordPress-Plugins nach der Migration?

Jedes Plugin braucht ein Next.js-Äquivalent. Contact Form 7 oder Gravity Forms werden zu einer Server Action. Yoast SEO wird die Next.js Metadata API. WooCommerce wird Stripe. Google Analytics bleibt gleich (verschieben Sie einfach das Tracking-Snippet). Einige Plugins wie Wordfence werden überflüssig, da es kein WordPress zum Angreifen gibt. Erstellen Sie ein komplettes Inventar Ihrer Plugins vor Beginn -- jedes Plugin ohne klare Replacementstrategie ist ein Risiko.

Sollte ich zu Next.js oder Astro von WordPress migrieren?

Es hängt von Ihren Interaktivitätsbedürfnissen ab. Next.js ist besser für Sites mit dynamischen Funktionen -- Benutzer-Authentication, E-Commerce, Dashboards, Echtzeit-Daten. Astro ist besser für Content-schwere Sites, die größtenteils statisch sind -- Blogs, Dokumentation, Marketing-Sites. Astro versandt standardmäßig null JavaScript, was noch kleinere Page Sizes bedeutet. Wir arbeiten mit beiden -- siehe unsere Astro Development und Next.js Development Pages für Details.

Kann ich WooCommerce Subscriptions zu Stripe migrieren?

Ja, aber es erfordert sorgfältige Behandlung aktiver Abonnenten. Sie werden Kunden und Abonnements in Stripe erstellen müssen, dann die Abrechnungsänderung den Kunden mitteilen. Stripe Billing behandelt Probezeiten, Anteilsmäßigkeit, Logik für fehlgeschlagene Zahlungen und Stornierungsflows. Die Migration selbst ist ein One-Time-Skript, aber das Testen dagegen echte Abonnement-Szenarien ist, wo die Zeit hingeht. Budget extra Zeit für dies, wenn Sie mehr als 100 aktive Abonnenten haben.

Was ist das beste Hosting für Next.js nach der Migration von WordPress?

Vercel ist die Standard-Wahl -- es ist von dem Team gebaut, das Next.js macht, und der kostenlose Tier handhabt die meisten Marketing-Sites. Vercel Pro ist $20/Monat für Teams. Alternativen umfassen Netlify, Cloudflare Pages (ausgezeichnet für Edge Performance) und Self-Hosting mit Docker auf einem VPS, wenn Sie volle Kontrolle möchten. All diese sind erheblich günstiger als verwaltetes WordPress-Hosting für entsprechende Traffic-Level.