Online Language Lessons
A live WordPress migration to Next.js, without breaking production
2025–2026 · Sole developer · In active development
Migrating a live, revenue-generating language school platform from WordPress to a modern Next.js + Payload stack, keeping the existing backend intact to keep the business running.
Pattern: strangler-fig on a single domain.
| Before | After | |
|---|---|---|
| Page load | 6.6s | 0.77s |
| Server time | 5.9s | 0.1s |

The problem
Online Language Lessons (OLL) is a UK-based language school running on WordPress. The site is the storefront, the booking engine, the LMS, the checkout, the marketing site, and the blog, all in one. Years of plugins, page builders, and bolt-on services had compounded into something slow, hard to evolve, and fragile to change.
A rebuild was tempting. It was also off the table.
The WooCommerce checkout, the Tutor LMS, and the Amelia booking system were processing real transactions every day. Rewriting them was a multi-month project nobody had asked for, and rebuilding the front-end while the cash register kept ringing was non-negotiable.
The brief became sharper: modernise the public-facing experience without touching the parts that make money.
The approach: strangler-fig, single domain
Rather than rebuild everything, I put a new Next.js application in front of the existing WordPress. Same public domain, two systems under the hood.
The new stack serves everything visitors see and interact with on the public site. The existing WordPress continues to handle the transactional and authenticated workflows it was already running, behind a reverse proxy.
The result: no database migration, no content sync job, no broken internal links, no risk of half-state. The only downtime window is the final DNS cutover, a few seconds at most.
This is the single decision that shaped everything else.
What I built
A multi-route public-facing application replacing the WordPress front-end with a modern stack while leaving the transactional layer untouched. The work spans the full public site:









The most interesting piece of product work was the Private Classes flow: a 6-step quiz that drives a faceted catalog with a recommendation engine. The state is a single source of truth, persisted in the URL, so any answer combination is shareable, the back/forward buttons just work, and the entire interaction has one writer and one reader.






The site is bilingual (EN/IT) end-to-end, with translation parity enforced in CI so a screen reader on the Italian site never speaks English by accident.
Performance
A more detailed view of the numbers from the summary, measured with field data (CrUX P75 for WordPress, Vercel Real Experience Score for the new stack):
| Metric | WordPress | Next.js | Δ |
|---|---|---|---|
| Largest Contentful Paint | 6.6s | 0.77s | −88% |
| First Contentful Paint | 6.2s | 0.69s | −90% |
| Time to First Byte | 5.9s | 0.1s | −98% |
| Cumulative Layout Shift | 0.01 | 0 | parity |
Note: WordPress measured on production, Next.js on preview during cutover. INP is "Good" on both. Performance ≥90 in Lighthouse CI is enforced on every preview.
Unified sessions across two systems
One requirement made the migration meaningful for the end user: they shouldn't notice the architectural split. Logging in on the new Next.js front-end should authenticate them on the WordPress backend too, with no second login screen and no broken redirect.
Because both systems live on the same public domain, this is solvable cleanly: the WordPress session cookie (HttpOnly, Secure) is set with a scope that the new stack can recognise, so server-side requests from Next.js can validate the session against WordPress without managing a separate identity layer.
The trade-off is honest: no JWT, no separate auth provider, no parallel session store. One source of truth (WordPress), one cookie, two systems reading it. It's the simplest design that meets the requirement, and it keeps the existing user database as the canonical one.
Selected technical decisions
-
Strangler-fig over rebuild. Risk-managed by default. No database migration, no link rot, no payment downtime. Generalisable to any team modernising a live WordPress.
-
WordPress kept as the transactional backend. WooCommerce, Tutor LMS, and Amelia stay untouched. The team's existing workflows don't change.
-
Accessibility as a build gate, not a final audit. WCAG 2.1 AA enforced in CI on every preview. Catches regressions before merge, not after launch.
Stack
- Frontend: Next.js 16 (App Router, RSC, ISR), React 19, TypeScript 5.8 strict. 24 routes across the public site.
- CMS: Payload v3, co-located in the Next.js app. Editorial content (blog, FAQ, teachers, legal, live experiences) lives in Payload; course inventory keeps living in WordPress and is fetched via REST with on-demand revalidation.
- Database: Vercel Postgres
- Storage: Vercel Blob (client-side direct uploads)
- Styling: Tailwind v4 with semantic tokens (no hardcoded hex anywhere), shadcn/ui, Radix primitives. Typography pairs Poppins (body) with Yeseva One (headings), reserved strictly for
h1/h2to keep hierarchy honest. - i18n: next-intl, route-based EN/IT (
/en/…,/it/…). - Validation: Zod + zod-openapi
- Testing: Playwright on 5 engines (Chromium/WebKit/Firefox/iPhone/Pixel), Argos visual regression
- CI gates: Lighthouse ≥90, OpenAPI drift, proxy path consistency, axe-core a11y
Status
In active development. Working toward final client review, with launch targeted for Q2 2026.
Credits
Solo development.
Brand palette and illustration assets by Davide Borboni.