Mein Platz in der WordPress-Community
- Mein wordpress.org-Konto besteht seit 2006.
- Ich habe zum WordPress-Core in den Versionen 7.0 und 7.1 beigetragen.
- Ich betreue 30 Plugins auf wordpress.org.
- Ich organisiere WordCamps und trete dort als Speaker auf.
- Ich war bei jedem WordCamp Europe von 2017 bis 2026 dabei.
Die Anwendung läuft, die Kunden nutzen sie, und doch liegt sie noch im Verzeichnis pages. Neue Funktionen von Next.js entstehen im App Router, und wer dort weiterbauen will, muss einmal hinüber. Auf dieser Seite beschreibe ich, wie ich eine bestehende Anwendung Route für Route umstelle, was sich bei Datenabruf, Layouts und Metadaten ändert und was ich vor der Abnahme teste. Ich arbeite allein, remote und ausschließlich in Textform. Grundlage sind die Anleitung How to migrate from Pages to the App Router und das Upgrade auf Version 15 auf nextjs.org.
Beide Router laufen nebeneinander
Der Router im Verzeichnis app kam mit Next.js 13; für die Migration verlangt die Anleitung Version 13.4 oder neuer. Laut npm-Registry erschien 13.4.0 am 4. Mai 2023, die aktuelle Version 16.4.6 am 9. November 2026. Die Anleitung betont, dass app absichtlich gleichzeitig mit pages läuft, damit Seite für Seite umgestellt werden kann. Genau so plane ich es: keine Umstellung in einem Zug, sondern eine Liste von Routen, die einzeln wandern.
Zwei Folgen davon sollten Sie vorher kennen:
- Beim Wechsel zwischen einer Route aus
pagesund einer ausappgibt es eine harte Navigation, undnext/linklädt über die Grenze hinweg nichts vor. Während der Übergangszeit ist das normal und kein Fehler. _appund_documentbleiben stehen, bis die letzte Route im alten Verzeichnis verschwunden ist. Die Anleitung rät dazu, weil Stile aus dem neuen Wurzel-Layout nicht auf Routen inpageswirken.
Der erste Schritt im Code ist klein: Version anheben, Verzeichnis app anlegen, eine Datei app/layout.tsx mit <html> und <body> schreiben. Dieses Wurzel-Layout ersetzt am Ende _app und _document.
Pro Route zwei Schritte statt einer Neuschreibung
Seiten im Verzeichnis app sind standardmäßig Server Components, Seiten in pages sind Client Components. Wer beides in einem Zug ändert, sucht Fehler an zwei Stellen. Die Anleitung empfiehlt deshalb zwei Schritte, die ich übernehme:
- Die Seitenkomponente wandert unverändert in eine neue Datei mit der Direktive
'use client'. Sie verhält sich wie vorher, wird weiter auf dem Server vorgerendert und bekommt ihre Daten als Props. - Eine neue
page.tsxim Verzeichnisappbleibt Server Component, lädt die Daten und reicht sie an die Client Component weiter.
Erst wenn die Route so läuft und getestet ist, verlagere ich Teile der Oberfläche, die keine Interaktion brauchen, aus der Client Component in die Server Component. So sinkt das JavaScript, das zum Browser geht, Schritt für Schritt und nicht auf Verdacht.
Datei oder Funktion in pages | Entsprechung in app |
|---|---|
index.js, about.js, blog/[slug].js | page.js, about/page.js, blog/[slug]/page.js |
_app.js und _document.js | ein Wurzel-Layout app/layout.js |
getServerSideProps, getStaticProps | async Server Component mit fetch |
getStaticPaths | generateStaticParams |
fallback in getStaticPaths | dynamicParams |
pages/404.js | not-found.js |
pages/_error.js | error.js je Routensegment |
pages/api/* | Route Handler route.js |
API-Routen in pages/api laufen laut Anleitung ohne Änderung weiter. Ich ziehe sie erst um, wenn sie ohnehin angefasst werden.
Datenabruf: aus getServerSideProps wird fetch im Server Component
Ein Beispiel aus der Anleitung, leicht gekürzt. Vorher, im Verzeichnis pages:
export async function getServerSideProps() {
const res = await fetch('https://...')
const projects = await res.json()
return { props: { projects } }
}
export default function Dashboard({ projects }) {
return (
<ul>
{projects.map((p) => <li key={p.id}>{p.name}</li>)}
</ul>
)
}Nachher, im Verzeichnis app:
async function getProjects() {
const res = await fetch('https://...', { cache: 'no-store' })
return res.json()
}
export default async function Dashboard() {
const projects = await getProjects()
return (
<ul>
{projects.map((p) => <li key={p.id}>{p.name}</li>)}
</ul>
)
}Die Funktion getProjects darf laut Anleitung beliebig heißen. Die Option cache: 'no-store' sagt, dass die Antwort nie zwischengespeichert wird, das entspricht getServerSideProps. Wer in getServerSideProps an req.cookies oder Header kam, liest sie nun mit cookies() und headers() aus next/headers. In Version 15 sind beide asynchron, ebenso params und searchParams: Aus const { slug } = params wird const { slug } = await params. Für diesen Umbau gibt es ein Codemod, das die Upgrade-Anleitung nennt.
Die Stelle, an der sich Verhalten still ändert
Aus getStaticProps mit revalidate: 60 wird in der Anleitung ein fetch mit next: { revalidate: 60 }. Wichtig ist, was zwischen Version 14 und 15 geschah: Laut der Upgrade-Anleitung werden fetch-Anfragen seit Version 15 nicht mehr standardmäßig zwischengespeichert, und GET-Route-Handler ebenfalls nicht. Eine Seite, die früher zur Bauzeit entstand, kann nach einer unbedachten Umstellung bei jedem Aufruf das Backend belasten.
Deshalb schreibe ich für jede ersetzte Funktion das Zwischenspeichern ausdrücklich hin:
getServerSidePropswird zucache: 'no-store'.getStaticPropswird zucache: 'force-cache'.getStaticPropsmitrevalidatewird zunext: { revalidate: n }.
Dazu kommt getStaticPaths: Es wird zu generateStaticParams, und fallback wird zu dynamicParams. Mit dem Standard true werden noch nicht erzeugte Pfade bei der ersten Anfrage gerendert und zwischengespeichert, mit false liefern sie einen 404, das entspricht fallback: false. Für fallback: 'blocking' gibt es laut Anleitung keine eigene Option, weil der Unterschied zu true mit Streaming vernachlässigbar ist.
Layouts und Kontext statt getLayout
Wer in pages das Muster Page.getLayout verwendet, bekommt im App Router verschachtelte Layouts als Dateikonvention. Die Anleitung beschreibt den Weg: Den Inhalt der bisherigen Layout-Komponente in eine Client Component verschieben, in einer layout.js einbinden und das getLayout an der Seite löschen. Später lassen sich die nicht interaktiven Teile in das Server-Layout heben.
Der häufigere Stolperstein sind React-Kontexte. Provider für Warenkorb, Sitzung oder Thema aus _app müssen laut Anleitung in eine Client Component. Ich bündele sie in einer Datei providers.tsx und binde sie im Wurzel-Layout ein.
Zwei Einzelheiten aus der Anleitung, die oft vergessen werden: Skripte mit der Strategie beforeInteractive wandern aus _document in ein Wurzel-Layout, und die Handler onLoad, onReady und onError funktionieren nicht in Server Components. Bei Tracking- und Einwilligungsskripten ist das keine Kleinigkeit, siehe den Abschnitt zur LDI NRW weiter unten.
Metadaten: next/head wird zu metadata
Die Komponente next/head entfällt im App Router. An ihre Stelle tritt für feste Angaben ein exportiertes Objekt metadata in layout.js oder page.js. Hängt der Titel von Daten ab, etwa vom Namen eines Produkts, exportieren Sie die Funktion generateMetadata. Beide gibt es laut Dokumentation nur in Server Components.
Lädt generateMetadata dieselben Daten wie die Seite, wäre das ein doppelter Abruf. Die Dokumentation schlägt dafür vor, die Ladefunktion mit cache aus React zu umhüllen, sodass sie nur einmal läuft.
Ein Detail, das für die Suche zählt: Bei dynamisch gerenderten Seiten streamt Next.js die Metadaten getrennt und fügt sie nach dem Laden in das HTML ein. Für Bots, die die Metadaten im <head> erwarten, ist das Streaming laut Dokumentation abgeschaltet; genannt werden Twitterbot, Slackbot und Bingbot. Ich prüfe trotzdem bei jeder Route den ausgelieferten Kopf, weil ein vergessenes Head ohne Fehlermeldung zu einer Seite ohne Titel führt.
Router-Hooks
In Client Components kommen useRouter, usePathname und useSearchParams aus next/navigation. Der neue useRouter liefert keinen pathname und kein query-Objekt mehr, dafür gibt es usePathname, useSearchParams und useParams. Auch asPath, isReady und die Werte der eingebauten Mehrsprachigkeit (locale, locales, defaultLocale) sind weggefallen. Und router.push gibt kein Promise mehr zurück.
Komponenten, die in beiden Verzeichnissen laufen müssen, bekommen in der Übergangszeit den useRouter aus next/compat/router. Nach der letzten Route tausche ich ihn gegen den aus next/navigation.
Was ich vor der Abnahme teste
Die Dokumentation von Next.js weist darauf hin, dass manche Testwerkzeuge asynchrone Server Components noch nicht vollständig unterstützen, und empfiehlt Ende-zu-Ende-Tests statt Unit-Tests. Deshalb lege ich vor der ersten Umstellung Playwright-Tests für die wichtigsten Wege an: Anmeldung, Suche, Formulare, Bestellung oder was Ihre Anwendung trägt.
| Prüfpunkt | Was ich vergleiche |
|---|---|
| Ausgelieferter HTML-Kopf | Titel, Beschreibung, kanonische Adresse und Robots-Angaben je Route, alt gegen neu |
| Daten | gleicher Inhalt wie vorher, und wie oft das Backend gefragt wird |
| Übergang der Router | harte Navigation zwischen pages und app an den Stellen, an denen Nutzer sie spüren |
| Fehlerseiten | 404 und Fehlerseite zeigen weiter Impressum und Datenschutzlink |
| Skripte | Einwilligung und Tracking laden erst nach der Zustimmung |
| Formulare und Anmeldung | Cookies, Weiterleitungen und Fehlermeldungen wie bisher |
| Leistungswerte | Messung der Core Web Vitals nach den Schwellen von web.dev: LCP bis 2,5 s, INP bis 200 ms, CLS bis 0,1 |
Die Liste lässt sich auch ohne mich abarbeiten. Wichtig ist die Reihenfolge: erst die Tests anlegen, solange die Anwendung noch im alten Zustand läuft, dann umstellen. Dann zeigt jede rote Zeile eine Änderung, die durch die Umstellung entstand.
Duisburg: Hochschulen, Institute und die LDI NRW

Foto: Harald Lordick, CC BY-SA 3.0, Wikimedia Commons.
Duisburg Business & Innovation nennt Duisburg einen Wissenschaftsstandort mit fünf Hochschulen und zehn Forschungsinstituten, darunter die Universität Duisburg-Essen mit knapp 40.000 Studierenden, die Folkwang Universität der Künste und die FOM Hochschule. Quelle: Wissenschaftsstandort Duisburg. Für Anwendungen, die an solchen Einrichtungen oder in Unternehmen der Region laufen, gilt dieselbe Vorgehensweise: erst Bestand aufnehmen, dann Route für Route umstellen.
Für Datenschutz in Nordrhein-Westfalen ist bei privaten Betreibern die LDI NRW zuständig. In ihrem Muster für Datenschutzhinweise steht, dass die Hinweise den Nutzern zum Zeitpunkt der Datenerhebung vorliegen müssen und der Link von jeder einzelnen Seite mit einem Klick erreichbar sein muss. Die Erklärung ist zu ergänzen, wenn Cookies, aktive Inhalte, Kontaktformulare oder Social-Media-Plugins im Einsatz sind.
Für die Umstellung folgt daraus:
- Der Link zu Impressum und Datenschutzhinweisen gehört in das Wurzel-Layout. Dann steht er auch auf
not-found.jsund in den Fehlerseiten. Das Impressum verlangt § 5 DDG leicht erkennbar und unmittelbar erreichbar. - Skripte, die Informationen im Endgerät speichern oder auslesen, brauchen nach § 25 TDDDG grundsätzlich eine Einwilligung. Wandert ein Skript aus
_documentin das Layout, prüfe ich, dass es weiterhin erst nach der Zustimmung lädt. - Ändert sich beim Umbau, welche Dienste Daten empfangen, aktualisiere ich die Liste dafür, damit die Datenschutzhinweise stimmen. Die Bewertung selbst ist Sache Ihrer Datenschutzberatung, ich gebe keine Rechtsberatung.
So läuft die Umstellung mit mir ab
- Ihre Anfrage in Textform: Adresse, Version, Backend, Ziel, Termin.
- Meine Rückfragen, schriftlich: meist zu Routen, Authentifizierung, Hosting und dazu, was nicht kaputtgehen darf.
- Bestandsaufnahme: Routen, Datenfunktionen, Abhängigkeiten und Skripte als Liste, mit einer Reihenfolge der Routen.
- Umfang als Dokument: Leistungen, Ausschlüsse und Annahmen. Ich beginne erst nach Ihrer schriftlichen Bestätigung, zusätzliche Wünsche schätze ich getrennt ein.
- Tests zuerst, dann Route für Route auf einer Testumgebung, die Sie selbst durchklicken können.
- Abnahme gegen den Umfang, offene Punkte als Liste.
- Danach: Updates und kleine Änderungen übernehme ich auf Wunsch in der Wartung in Duisburg. Nicht genutzte Stunden eines Monats gehen nicht auf den nächsten über.
Was Sie mitbringen: Gestaltung, Mockups und Wireframes liefern Sie oder Ihre Agentur, ich entwerfe nichts selbst. Fehlt ein Entwurf, markieren Sie in meiner Tabellenvorlage je Ansicht, welches Element wohin gehört. Dazu brauche ich Zugang zu Repository, Hosting und Testumgebung.
Verwandte Seiten für Duisburg
- Migration von Next.js zu Astro in Duisburg: wenn die Anwendung eigentlich eine Inhaltsseite ist.
- Astro Entwickler in Duisburg: für Seiten mit wenig Interaktion.
- PHP Entwickler in Duisburg: wenn ein PHP-Backend die Daten liefert.
- WordPress Entwickler in Duisburg: wenn ein klassisches WordPress genügt.
- Headless CMS Entwickler: wenn Redaktionssystem und Frontend getrennt laufen.
Fragen zur Umstellung
Muss ich die ganze Anwendung auf einmal umstellen?
Nein. Laut Next.js-Dokumentation ist das Verzeichnis app absichtlich so gebaut, dass es gleichzeitig mit pages läuft. Sie können Route für Route verschieben. Der Wechsel zwischen beiden Routern ist dabei eine harte Navigation.
Was ersetzt getServerSideProps im App Router?
Ein async Server Component, das die Daten mit fetch lädt. Für Daten, die bei jeder Anfrage frisch sein müssen, gibt die Dokumentation die Option cache: 'no-store' an. Cookies und Header liest die Seite mit cookies() und headers() aus next/headers, beide asynchron.
Warum liefert mein ehemaliges getStaticProps plötzlich ständig frische Daten?
Seit Next.js 15 werden fetch-Anfragen nicht mehr standardmäßig zwischengespeichert. Für das Verhalten von getStaticProps schreibe ich cache: 'force-cache' hin, für getStaticProps mit revalidate die Option next: { revalidate: n } mit der Sekundenzahl.
Verliere ich beim Umstellen Metadaten wie Titel und Beschreibung?
Nur wenn next/head vergessen wird. Im App Router ersetzen das Objekt metadata und die Funktion generateMetadata die Komponente Head. Ich vergleiche vor der Abnahme den ausgelieferten HTML-Kopf jeder Route mit dem alten Stand.
Lassen sich asynchrone Server Components mit Unit-Tests prüfen?
Laut Next.js-Dokumentation unterstützen manche Werkzeuge asynchrone Server Components noch nicht vollständig, empfohlen werden Ende-zu-Ende-Tests. Ich lege deshalb vor der Umstellung für die wichtigsten Wege Playwright-Tests an und lasse sie nach jeder verschobenen Route laufen.
Was schicke ich Ihnen vorab, damit die Einschätzung konkret wird?
Die Adresse der Anwendung, die Next.js-Version aus der package.json, das Backend oder CMS, die Zahl der Routen und den gewünschten Termin. Wenn ein Layout existiert, legen Sie es bei; Gestaltung und Platzierung der Elemente liefern Sie, ich setze sie um.
Ihre Umstellung in Duisburg
Ob ein Kundenportal, ein Shop-Frontend oder eine interne Anwendung noch im Verzeichnis pages liegt: Schreiben Sie mir über das Kontaktformular. Nennen Sie die Adresse der Anwendung, die Next.js-Version, das Backend, das Ziel und den gewünschten Termin. Ich antworte an Werktagen in Textform, und die erste Einschätzung ist kostenlos.
Ausgewählte Projekte
Beispiele aus meiner Arbeit.
Real Estate Platform: DUNE Resort
In der östlichen Region von Mielno entsteht ein exklusiver Apartmentkomplex an der Ostsee, DUNE Resort. Dieses außergewöhnliche Projekt erinnert an luxuriös...
satyr.pl - Medien- und Publishing-Projekt | WPPoland
Satyr.pl ist eines der interessantesten Projekte in meinem Portfolio als WordPress-Entwickler. In den Jahren 2000 bis 2015 war es ein populäres polnisches In...
scalac.io - WordPress Projekt | WPPoland
scalac.io ist eine internationale Plattform, die im Jahr 2014 ins Leben gerufen wurde, um den Herausforderungen im Umgang mit Big Data sowie der Skalierung v...
WordPress Support & Entwicklung in Duisburg
Brauchen Sie die Leistung: Next.js-Entwickler in Duisburg?
Beschreiben Sie Ihr Projekt per E-Mail. Antwort innerhalb von 48 Stunden.
Projekt beschreibenWeitere WordPress-Dienste und Wissensbasis entdecken
Migration zu Astro, Next.js und Headless WordPress.
WooCommerce-Synchronisierung mit ERP und Großhandel.
Geschäftsprozessautomatisierung rund um WordPress und Firmensysteme.
Bibliothek konkreter Automatisierungsfälle.
Headless WordPress, Sanity, Strapi und Contentful mit Astro oder Next.js.
Astro, MDX, Edge Delivery und an echtem Traffic gemessene Core Web Vitals.
Individuelle WordPress-Entwicklung und Architektur.
Skalierbare Headless-, ERP- und KI-Architektur für Enterprise.
Verwandte Kategorien
Unterstützende Artikel

Sechs bis sechzehn Wochen für typische Projekte, in vier Phasen: Analyse, Scoping, Build und Umstellung, Tuning. Die Variablen sind Katalogumfang, Anzahl der Integrationen, URL-Erhalt und die Bereitschaft des Redaktionsteams, nicht die Wahl des Frameworks.

Umfassende 4-Jahres-TCO-Analyse (Total Cost of Ownership), reale Core-Web-Vitals-Benchmarks, Astro 7 GraphQL APQ-Architektur und eine 10-Punkte-Entscheidungsmatrix für Enterprise-Entscheider zwischen entkoppeltem und traditionellem WordPress.

Die Entscheidung Shopify Plus vs WooCommerce headless im Jahr 2026 ist kein binärer "Plattform vs Custom"-Kompromiss mehr. Beide laufen headless, beide integrieren KI, beide liefern am Edge aus. Die echten Achsen sind Kontrolle, Gesamtkosten über fünf Jahre und Exit-Strategie. Dieser Artikel durchläuft die Entscheidungsmatrix mit bestätigten Plattformfakten.