WordPress naar Next.js Migratie: Een Volledige Technische Gids
TLDR
Het migreren van WordPress naar Next.js in 2026 levert meetbare prestatieverbesseringen op: gemiddelde TTFB daalt van 1.200ms naar 85ms, paginagewicht krimpt van 3,2 MB naar 620 KB, en Lighthouse-scores stijgen van 42 naar 94. Het proces omvat het exporteren van inhoud via WP REST API naar Supabase of Payload CMS, het migreren van media naar object storage, het toewijzen van elke URL voor 301-omleidingen, het behouden van Yoast/RankMath SEO-gegevens in Next.js Metadata API, en het vervangen van plugins zoals Gravity Forms door Server Actions. Voor WooCommerce-sites vervangt Stripe de gehele commerce-stack. Verwacht 4-8 weken voor typische sites met 100-500 pagina's, met de grootste investeringsrit in omleidingstesten en template-herbouw.
Dit is geen voorbarige analyse. Ik bouw al meer dan 12 jaar met WordPress. Ik heb agency-sites, membership-platforms, WooCommerce-winkels met zes cijfers maandelijkse omzet en meer aangepaste post-types dan ik mij kan herinneren, op de markt gebracht. Ik heb ook productiesites naar Next.js + Supabase gemigreerd. Hier is elk technisch detail -- wat schoon in kaart wordt gebracht, wat niet, en wat je moet plannen.
Ik ga niet doen alsof WordPress slecht is. Dat is het niet. Het stuurt 43% van het web voor goede redenen. Maar voor bepaalde projecten -- sites waar prestaties een bedrijfsmetriek zijn, waar beveiligingsoppervlak ertoe doet, waar je je implementatiepijplijn wilt bezitten -- is Next.js het betere gereedschap. De migratie echter? Het is een minenveld als je het niet plant.
Deze gids behandelt het exacte proces dat ik gebruik, met echte code, echte valkuilen, en eerlijke beoordelingen van wat je wint en wat je verliest.
Inhoudsopgave
- Content Migration: WP REST API naar Supabase of Payload CMS
- Media Migration: Van wp-content naar Supabase Storage
- URL-structuur: Elke oude URL toewijzen
- SEO Migration: Yoast en RankMath-gegevens naar Next.js Metadata
- Formulieren: Gravity Forms naar Server Actions
- WooCommerce naar Stripe
- Monitoring na migratie
- Wanneer je op WordPress moet blijven
- Prestatieresultaten: Voor en na
- Veelgestelde vragen

Content Migration: WP REST API naar Supabase of Payload CMS
Elke WordPress-migratie begint hier. Je hebt posts, pages, aangepaste post-types, ACF-velden, taxonomieën -- jaren aan inhoud die ergens veilig moet landen.
Je hebt twee solide opties voor waar die inhoud heen gaat:
- Supabase -- als je een database wilt die je volledig controleert, met rijbeveiliging en een REST/GraphQL API uit de doos
- Payload CMS -- als je client een visuele bewerkingservaring nodig heeft die bekend voelt na WordPress
Voor onze headless CMS-ontwikkelingprojecten evalueren we dit per client. Payload wint wanneer redacteurs zelfbediening nodig hebben. Supabase wint wanneer ontwikkelaars de primaire inhoudsbeheerders zijn of wanneer je de gegevens voor meer dan alleen een website nodig hebt.
Welke inhoudsstructuur moet ik behouden bij migratie van WordPress naar Next.js?
Behoud post-metagegevens, taxonomieën, aangepaste velden en URL-slugs tijdens migratie. Je WordPress-posts bevatten jaren aan gestructureerde gegevens: categorieën, tags, ACF-velden, aanbevolen afbeeldingen en publicatiedatums. Exporteer al deze gegevens via WP REST API met de parameter _embed om media-URL's in één aanvraag op te halen. Sla zowel HTML- als Markdown-versies van inhoud op -- HTML als fallback, Markdown voor MDX-rendering. Wijs aangepaste post-types toe aan equivalente databasetabellen of CMS-collecties in je nieuwe systeem.
Het exportscript
Hier is het Node.js-script dat ik gebruik om inhoud uit de WP REST API op te halen, op te ruimen en in te voegen in Supabase. Dit verwerkt posts, maar je zou hetzelfde patroon dupliceren voor pages en CPTs (verander alleen het endpoint).
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();
Een paar dingen die ik op de harde manier heb geleerd:
- Gebruik altijd
_embedin je WP REST API-aanvragen. Zonder het krijg je media-ID's in plaats van URL's, wat betekent N+1-aanvragen om aanbevolen afbeeldingen op te lossen. - Turndown converteert HTML naar Markdown -- dit is essentieel als je later met MDX wilt renderen. Hou de originele HTML ook als fallback.
- Shortcodes overleven niet. WordPress rendert enkele shortcodes via REST API, maar veel (vooral van plugins zoals WPBakery of Elementor) komen door als onbewerkte bracettekst. Je hebt een shortcode-naar-componenttoewijzingsstrategie nodig. Ik houd een spreadsheet bij.
- ACF / Aangepaste velden: Als je ACF gebruikt, hebt je nodig dat de plugin ACF to REST API ingeschakeld is, dan verschijnen de aangepaste velden in de eigenschap
acfvan elk post-object.
Supabase-tabelschema
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()
);
Merk op dat ik kolommen seo_title, seo_description en og_image heb opgenomen. Je hebt die in de SEO-migratiesectie hieronder nodig.
Media Migration: Van wp-content naar Supabase Storage
Dit is het deel waar de meeste gidsen overheen gaan, en het is het deel dat het langst duurt. Een 12 jaar oude WordPress-site kan gemakkelijk 10.000+ bestanden in wp-content/uploads/ hebben.
De aanpak:
- Download de gehele
wp-content/uploads/directory - Upload naar Supabase Storage (of Cloudflare R2, of S3)
- Herschrijf elke media-URL in je inhoud
Hoe migreer ik WordPress-mediabestanden naar moderne object storage?
Download je gehele wp-content/uploads/ directory, upload naar Supabase Storage of Cloudflare R2, herschrijf dan alle media-URL's in je inhoud. Gebruik een script om elke afbeeldings-URL uit je WordPress-inhoud op te halen, upload naar object storage terwijl je de mapstructuur behoudt (2024/03/image.jpg), voer dan een tweede pass uit om oude URL's te vervangen door nieuwe storage-URL's. Stel wildcard-omleidingen in voor externe sites die rechtstreeks naar je oude afbeeldings-URL's linken.
Download- en uploadscript
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);
}
}
De afbeeldingsprestatiebewoning
Dit is waar de migratie echt beloond wordt. WordPress serveert originele uploads -- vaak 3000×2000px PNG's die iemand van zijn DSLR heeft geüpload. Zelfs met een plugin zoals ShortPixel, serveer je afbeeldingen nog steeds via PHP.
Next.js <Image> component met next/image doet automatische formaatonderhandeling (WebP/AVIF), responsieve grootte en lui laden. De cijfers van onze laatste migratie:
| Metriek | WordPress | Next.js + Image Component |
|---|---|---|
| Gemiddeld paginaafbeeldingsgewicht | 2,1 MB | 380 KB |
| Afbeeldingsaanvragen | 12 per pagina | 6 per pagina (lui geladen) |
| Formaat | JPEG/PNG | WebP (AVIF waar ondersteund) |
| Cumulatieve layout verschuiving | 0,18 | 0,02 |
Dat is geen tik. Gemiddelde afbeeldingspayload daalde van 2,1 MB naar 380 KB. En dit was zonder opnieuw opgeoptimaliseerde bestanden te uploaden -- gewoon next/image zijn werk laten doen.
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-structuur: Elke oude URL toewijzen
Dit is waar migraties sterven. Eén gemiste omleiding betekent een 404 voor een pagina die Google jarenlang heeft geïndexeerd. Ik behandel URL-toewijzing met dezelfde ernst als een databasemigratie -- test het, verifieer het, verifieer het dan opnieuw.
Wat is de juiste manier om URL-omleidingen af te handelen tijdens WordPress-migratie?
Exporteer elke gepubliceerde URL uit WordPress, kruisverwijzing met Google Search Console geïndexeerde URL's, implementeer dan 301-omleidingen voor al deze. Query je wp_posts tabel voor alle gepubliceerde URL's, exporteer GSC's geïndexeerde URL's, en maak een omleidingskaart. Gebruik next.config.js omleidingen voor onder de 50 URL's, een JSON-bestand voor 50-1.024 URL's, of middleware voor sites die de 1.024 omleidingslimiet van Vercel overschrijden. Voeg wildcard-omleidingen in voor categoriepagina's, paginering en wp-content/uploads paden.
Het toewijzingsproces
Exporteer eerst elke URL uit WordPress. Ik trek rechtstreeks uit de database:
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;
Kruisverwijzing dan met Google Search Console's geïndexeerde URL's. GSC toont vaak URL's die niet meer in je database bestaan -- oude categoriepagina's, paginering-URL's, bijlagepagina's. Je hebt omleidingen voor al deze nodig.
next.config.js omleidingen
Voor sites met onder de 50 omleidingen, voeg ze inline in:
// 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,
},
];
},
};
Voor 200+ omleidingen: Gebruik een JSON-bestand
Zodra je voorbij een paar honderd gaat, is het onderhouden van inline-omleidingen ellendig. Ik gebruik een JSON-bestand:
// 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;
},
};
De wildcard-omleiding voor wp-content/uploads is essentieel. Er zullen externe sites rechtstreeks naar je afbeeldingen linken. Verlies die backlinks niet.
Belangrijk: Vercel heeft een limiet van 1.024 omleidingen in next.config.js. Voor sites met meer dan dat, gebruik 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
);
}
}

SEO Migration: Yoast en RankMath-gegevens naar Next.js Metadata
Als je Yoast of RankMath hebt gebruikt, heb je jaren aan aangepaste meta-titels, beschrijvingen en Open Graph-gegevens opgeslagen in de tabel wp_postmeta. Verlies ze niet.
Hoe behoud ik Yoast SEO-gegevens bij migratie naar Next.js?
Exporteer Yoast meta-titels, beschrijvingen en Open Graph-afbeeldingen uit wp_postmeta, sla ze op in je nieuwe database, rendeer ze dan via Next.js Metadata API. Query wp_postmeta voor velden _yoast_wpseo_title, _yoast_wpseo_metadesc en _yoast_wpseo_opengraph-image. Importeer deze gegevens in speciale SEO-kolommen in je posts-tabel. Gebruik generateMetadata in Next.js App Router om deze gegevens weer te geven als juiste meta-tags en Open Graph-markup.
SEO-gegevens exporteren
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;
Voor RankMath, wissel de meta-sleutels uit: rank_math_title, rank_math_description, rank_math_facebook_image.
Importeer deze gegevens in je Supabase posts-tabel in de SEO-kolommen die we eerder hebben gedefinieerd.
Next.js Metadata API
Met de App Router is metadata een basisproduct:
// 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 }] : [],
},
};
}
Schemamarkering als JSON-LD Server Components
WordPress-plugins genereren schema automatisch. In Next.js bouw je het zelf -- wat je eigenlijk meer controle geeft:
// 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,
}));
}
Dit genereert op buildtijd voor statische sites of op aanvraag voor dynamische. Geen plugin, geen XML-sjabloonbestanden, geen cacheringsproblemen.
Formulieren: Gravity Forms naar Server Actions
Gravity Forms is een van de beste WordPress-plugins ooit gemaakt. Het kost ook $259/jaar voor de Elite-licentie, en elk formulier laadt 200 KB+ JavaScript.
Hier is de vervanging. Het is ongeveer 20 regels code per formulier.
Bestaande inzendingen exporteren
Exporteer eerst je Gravity Forms-inzendingen als CSV uit het WordPress-beheer. Sla ze in Supabase op voor historische records als nodig.
Server Action contactformulier
// 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>
);
}
Geen plugin. Geen JavaScript-payload voor het formulier zelf (het is een native HTML-formulier met een server action). Progressieve verbetering -- het werkt zonder JavaScript ingeschakeld.
Voor meer complexe formulieren (multi-stap, bestandsuploads, voorwaardelijke velden), gebruiken we React Hook Form aan de clientzijde met hetzelfde server action-patroon. Het belangrijkste inzicht: je hebt geen formulierplugin nodig als je een database en een API hebt.
WooCommerce naar Stripe
Dit is het moeilijkste deel van een WordPress-migratie. WooCommerce is niet alleen een plugin -- het is een commerceplatform met producten, variaties, inventaris, bestellingen, abonnementen, coupons, belastingen en verzendregels. Je migreert geen functie. Je vervangt een platform.
Hoe migreer ik WooCommerce-producten naar Stripe?
Exporteer producten via WooCommerce REST API of CSV, creëer dan overeenkomende producten in Stripe Products API met prijzen. Voor sites onder de 500 producten, push rechtstreeks naar Stripe via hun API: creëer een product met naam, beschrijving en afbeeldingen, creëer dan een prijsobject gekoppeld aan dat product. Sla de WooCommerce-product-ID op in Stripe-metagegevens ter referentie. Gebruik Stripe Checkout Sessions voor betalingsverwerking en webhooks om bestellingen in je database bij te houden.
Productmigratie
Exporteer producten uit WooCommerce via CSV of REST API. Je hebt twee bestemmingsopties:
| Aanpak | Beste voor | Afwegingen |
|---|---|---|
| Supabase-producttabel | Aangepaste storefronts, complex filteren | U beheert voorraadenlogica |
| Stripe Products API | Eenvoudige catalogi, abonnementsbedrijven | Stripe beheert prijzen, u beheert weergave |
Voor de meeste sites onder de 500 producten push ik rechtstreeks naar 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}`);
}
}
Afrekenen met 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 });
}
Abonnementen
Als je op WooCommerce Subscriptions bent ($239/jaar), ga dan naar Stripe Billing. Verander mode: 'payment' naar mode: 'subscription' en zorg ervoor dat je prijzen recurring hebben ingesteld. Klaar. Stripe handelt proefperioden, proration en dunning af.
Ordertracering via 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 transactiekosten zijn 2,9% + $0,30 per transactie. Vergelijk dat met WooCommerce waar je betaalt voor hosting ($30-100/maand voor beheerde WP), de Subscriptions-plugin ($239/jaar), een betalingsgateway-plugin en waarschijnlijk nog wat extra add-ons. De wiskunde gaat snel op.
Voor complexe commerce-migraties bieden we dit aan als onderdeel van onze Next.js-ontwikkelingsdiensten -- het is een van de meest voorkomende aanvragen die we krijgen.
Monitoring na migratie
Lanceren is niet het einde. De eerste twee weken na migratie zijn essentieel.
Google Search Console
- Dien je nieuwe sitemap onmiddellijk in
- Gebruik het URL Inspection-hulpprogramma om indexering van je top 20 pagina's aan te vragen
- Controleer het Coverage-rapport dagelijks gedurende de eerste week -- let op pieken in 404's
- Controleer het rapport "Page indexing" op pagina's die vast zitten in "Discovered -- currently not indexed"
Analyticsvergelij
Stel een dashboard in dat week-over-week vergelijkt:
- Totale sessies
- Organisch zoekverkeer specifiek
- Bounce rate per pagina
- Conversiesnelheid (formulierinzendingen, aankopen)
Een kleine verkeersval in week één is normaal. Als het tegen week drie niet is hersteld, ging iets mis met omleidingen of indexering.
Lighthouse-audits
Voer Lighthouse uit op elke grote sjabloon (homepage, blogpost, productpagina, contactpagina). Streef naar:
- Prestaties: 90+
- Toegankelijkheid: 95+
- Beste praktijken: 95+
- SEO: 100
Bij onze laatste migratie -- een 400-pagina inhoudsite -- gingen we van een gemiddelde Lighthouse-prestatiescore van 38 op WordPress naar 96 op Next.js op Vercel. Dat is niet geselecteerd. Dat is het gemiddelde.
Wanneer je op WordPress moet blijven
Hier is het deel waar ik wat van jullie verlies, maar het is het meest belangrijke gedeelte in deze gids.
Migreer niet als:
- Je hebt een eenvoudige blog of brochuresite onder de 20 pagina's
- Je team is niet-technisch en vertrouwt op de WordPress-beheerder voor dagelijkse updates
- Je Lighthouse-scores zijn al 70+ en je hebt geen prestatiebehoefte
- Je hebt geen beveiligingsproblemen en je hosting is stabiel
- Je totale pluginkosten liggen onder $200/jaar
- Je hebt geen ontwikkelaar (of budget voor één) om een Next.js-site te onderhouden
WordPress met een goeie host (Cloudways, Kinsta), een solide thema en minimale plugins is prima. Eigenlijk is het meer dan prima -- het is beproefd, goed gedocumenteerd en begrepen door miljoenen ontwikkelaars.
De migratie is voordelig wanneer:
- Prestaties zijn direct gekoppeld aan omzet (e-commerce, SaaS-marketingsites)
- Je geeft $500+/maand uit aan beheerde hosting en beveiligingsplugins
- Je ontwikkelingsteam schrijft al React
- Je hebt een implementatiepijplijn met voorbeeldbouwsels, staging-omgevingen en rollbacks nodig
- Beveiligingsoppervlak is een genuine bezorgdheid (overheid, gezondheidszorg, financiën)
Ik zeg dit omdat vertrouwen belangrijker is dan een verkoop. Als je niet zeker weet of migratie de moeite waard is, neem contact met ons op en we geven je een eerlijke beoordeling.
Prestatieresultaten: Voor en na
Van onze laatste vijf migraties in 2024-2025:
| Metriek | WordPress (Gemiddeld) | Next.js (Gemiddeld) | Verandering |
|---|---|---|---|
| TTFB | 1.200ms | 85ms | 14x sneller |
| LCP | 3,8s | 0,9s | 4,2x sneller |
| Totaal paginagewicht | 3,2 MB | 620 KB | 5x lichter |
| Aanvragen per pagina | 47 | 11 | 77% minder |
| Lighthouse-prestaties | 42 | 94 | +52 punten |
| Maandelijkse hostingkosten | $75 | $20 (Vercel Pro) | 73% besparing |
| Core Web Vitals Pass Rate | 31% van pagina's | 100% van pagina's | ✓ |
Dit zijn echte getallen van productiesites. De WordPress-sites liepen beheerde hosting (WP Engine en Kinsta), geoptimaliseerde caching en afbeeldingsoptimalisatieplugins. Ze werden niet verwaarloosd -- het waren onderhouden sites die eenvoudig het plafond hadden bereikt van wat WordPress kan leveren.
Als je geïnteresseerd bent in wat mogelijk is met moderne frameworks, bekijk ook onze Astro-ontwikkelingsvaardighedenhe -- voor inhoudsrijke sites met minimale interactiviteit kunnen Astro nog kleinere payloads leveren dan Next.js.
Veelgestelde vragen
Hoe lang duurt een WordPress-naar-Next.js-migratie?
Voor een typische site met 100-500 pagina's, verwacht 4-8 weken ontwikkelingstijd. Eenvoudige brochuresites kunnen in 2-3 weken klaar zijn. Complexe WooCommerce-winkels met duizenden producten kunnen 10-12 weken duren. De inhoudsmigratie zelf is snel -- het is het herbouwen van de frontendsjablonen en het testen van elke omleiding dat tijd kost.
Zal ik SEO-rankings verliezen bij het migreren van WordPress naar Next.js?
Niet als je omleidingen en metagegevens correct afhandelt. De essentiële onderdelen zijn: 301-omleidingen voor elke oude URL, migratie van alle Yoast/RankMath meta-titels en beschrijvingen, behoud van je sitemapstructuur, en onmiddellijke indiening van de nieuwe sitemap naar Google Search Console. We hebben sites gezien die zich binnen 1-2 weken herstelden tot pre-migratie-verkeer, met aanzienlijke organische groei tegen maand drie vanwege verbeterde Core Web Vitals.
Kan ik WordPress als headless CMS gebruiken met Next.js?
Ja, en het is een populaire aanpak. Je houdt WordPress als de inhoudback-end, met behulp van de WP REST API of WPGraphQL, en Next.js als de frontend. Dit behoudt de vertrouwde bewerkingservaring terwijl je Next.js-prestaties krijgt. Het nadeel is dat je nog steeds een WordPress-installatie met zijn beveiligings- en update-overhead onderhoudt. We raden over het algemeen Payload CMS of Sanity aan voor nieuwe projecten tenzij het team diep geïnvesteerd is in WordPress-workflows.
Hoeveel kost het om van WordPress naar Next.js te migreren?
DIY met ontwikkelaarstijd: gratis in tools, maar budget 80-200 uur ontwikkelingstijd. Agencykosten: doorgaans $10.000-$50.000 afhankelijk van sitecomplexiteit, aantal pagina's, e-commerce-functies en aangepaste functionaliteit. Controleer onze prijzingspagina voor specifieke informatie over onze pakketten. De ROI komt meestal voort uit gereduceerde hostingkosten ($50-100/maand besparing), geëlimineerde pluginlicentiebijdragen en verhoogde conversiesnelheden van betere prestaties.
Wat gebeurt er met mijn WordPress-plugins na migratie?
Elke plugin heeft een Next.js-equivalent nodig. Contact Form 7 of Gravity Forms wordt een Server Action. Yoast SEO wordt de Next.js Metadata API. WooCommerce wordt Stripe. Google Analytics blijft hetzelfde (verplaats gewoon het trackingsnippet). Sommige plugins zoals Wordfence worden onnodig omdat er geen WordPress is om aan te vallen. Maak een compleet inventarislijst van je plugins voordat je begint -- elke plugin zonder duidelijke vervangingsstrategie is een risico.
Moet ik naar Next.js of Astro migreren van WordPress?
Het hangt af van je interactiviteitsbehoeften. Next.js is beter voor sites met dynamische functies -- gebruikersauthenticatie, e-commerce, dashboards, realtime-gegevens. Astro is beter voor inhoudsrijke sites die meestal statisch zijn -- blogs, documentatie, marketingsites. Astro verstuurt standaard nul JavaScript, wat nog kleinere paginaformaten betekent. We werken met beide -- zie onze pagina's Astro-ontwikkeling en Next.js-ontwikkeling voor details.
Kan ik WooCommerce-abonnementen naar Stripe migreren?
Ja, maar vereist voorzichtig omgaan met actieve abonnees. Je moet klanten en abonnementen in Stripe creëren, vervolgens de factureringswijziging met klanten communiceren. Stripe Billing handelt proefperioden, proration, logica voor mislukte betalingen en annuleringsstroom af. De migratie zelf is een eenmalig script, maar het testen ervan tegen echte abonnementscenario's is waar de tijd heengaat. Budget extra tijd als je meer dan 100 actieve abonnees hebt.
Wat is de beste hosting voor Next.js na migratie van WordPress?
Vercel is de standaardkeuze -- het is gebouwd door het team dat Next.js maakt, en de gratis tier handelt de meeste marketingsites af. Vercel Pro is $20/maand voor teams. Alternatieven zijn Netlify, Cloudflare Pages (uitstekend voor edge-prestaties) en zelfhosting met Docker op een VPS als je volledige controle wilt. Al deze opties zijn aanzienlijk goedkoper dan beheerde WordPress-hosting voor equivalente verkeerniveaus.