Nischal Tiwari

Case Study: Migrating a Live WordPress Cricket Platform to Headless React — With Zero Downtime

The brief: take CricNepal — a high-traffic, live-scoring cricket site running as a single WordPress theme — and turn it into a modern, headless platform. The constraints: it’s indexed, it’s live during matches, and it can never go dark.

We treated it as an architecture problem — event-sourcing, a real API, the strangler pattern — not a rewrite.

Starting point: the monolith

One WordPress install, one theme (rhinox2) doing everything — rendering, business logic, data access, presentation. Fine for a blog; wrong for a live sports product where scores change ball-by-ball and entities carry deep statistical histories.

1. Event-sourced live scoring — CN-Engine

Scoring was the first thing to leave the theme. We built CN-Engine, which records every delivery as a structured ball_event and derives all state from that event stream — score, on-strike batter, over rotations, fall-of-wickets, scorecard, commentary. Editing a mid-innings ball re-walks the entire chain deterministically: runs re-credited, strike rotations re-synced, no drift. State is computed, never a mutable field.

2. A domain REST layer

We exposed a proper API (a WordPress mu-plugin “domain API”): match bundles, player/team/tournament profiles, standings, archives, rankings, search. This seam reframed WordPress as CMS + engine behind an API, not “the website.”

3. CN-Engine v2 — operator-grade scoring

Live scoring is a high-pressure operator tool. v2 reworked the IA (grouped lifecycle nav, unified cockpit), added single-tap extras, and added loud guardrails: impossible states (e.g. a dismissed batter still at the crease) are detected and the operator is told exactly how to repair them — never silently corrupted. We also hardened the engine: canonical team-id resolution across all scoring paths, historical scorecard backfills, and a fix where a repair tool reported success while the replay layer silently rejected the input.

cricnepal brand

4. Design system v2026 + a two-layer token architecture

A unified identity from the Nepal kit (navy #2B2F4B, red #EC3339, white). Under it, a two-layer token system: semantic tokens (--surface, --text-heading) mapped to a Tailwind --color-* layer. That separation enabled a genuine dark mode — every surface/border/text token flips. We ran a full audit to eliminate hardcoded light values (the insidious kind, like a card hover that only broke in dark mode) and fixed the token bridge so bg-surface-* utilities flip correctly.

5. Figma as source of truth

Tokens, chips, and components live in Figma as the canonical system — design and code share one vocabulary, so a format pill in Figma is the same in production down to the accent border.

6. Account layer on Auth0

A fan account layer (follow topics, saved stories, quizzes, reading history) on Auth0, with its cn_session cleanly separated from WordPress’s editor login — two independent session layers, never conflated.

7. SEO + Schema.org, code-owned

SEO was first-class and server-generated: titles, meta, canonicals, OG, and rich Schema.org graphs (SportsEvent / Person+Athlete / SportsTeam). Sitemaps, robots, and feeds proxy through the same domain so nothing drops from the index during the migration.

8. The decouple — making “headless” actually true

The unglamorous core of the project: we moved everything the frontend depends on out of the theme and into the domain API — auth/account, quiz, follow-topics, nav menus, entity cards, photo galleries, the tournament header, team/player structured data. The theme becomes optional; the API becomes the contract.

9. SSR headless frontend

The experience layer is now a server-rendered Next.js app (App Router, ISR), hydrated into rich islands. Server renders real data, not skeletons — correct first paint for users and crawlers. Players, teams, tournaments, match centre, archives, search, rankings, homepage — all structured React fed by the API.

10. Structured data for entities

Player/team pages — profiles, career stats, rankings, debut records, squad memberships, recent matches, galleries, tagged news — converted from theme-injected HTML into typed endpoints + structured components, so the same data renders identically on the site, in islands, and in any future client.

11. Zero-downtime cutover (strangler pattern)

We ran the headless app alongside WordPress and migrated one route family at a time at the edge (nginx): /players as a canary → verify → teams → tournaments → matches → homepage. WordPress serves everything not yet migrated. Rollback is a single config change.

The production release was 148 commits promoted cleanly from staging, deployed with the live scorer sidecar rebuilt and Auth0 login verified healthy before the first route flipped. A real-world gotcha we solved: the origin sits behind Cloudflare with a Cloudflare Origin CA certificate, which Node’s fetch couldn’t verify — so server-side rendering 404’d until we added the CF Origin CA root to the trust store (no TLS weakening). Fans never saw a maintenance page.

Stack

WordPress (CMS + editorial) · mu-plugin domain REST API · CN-Engine event store · Next.js 16 SSR/ISR · Auth0 · Tailwind CSS + two-layer design tokens · Figma design system · RunCloud + Node supervisor sidecars · Cloudflare + nginx edge routing.

Takeaway

We turned a live WordPress monolith into a headless platform without a single minute of downtime — by treating the migration as an architecture problem rather than a rewrite. The same API now powers the website and can power mobile apps, partner widgets, and data products. WordPress does what it’s great at; the experience layer evolves on its own.

cricnepal.com got a complete rebuild — Here’s what changed for you

If the site feels faster, smoother, and more alive lately, that’s not your imagination. We rebuilt CricNepal from the ground up — and did it without ever taking the site offline.

Same site. Brand-new engine. Zero downtime.

Live cricket that’s actually live

Every ball is now recorded as it happens through our own match engine, CN-Engine. Scores, scorecards, fall-of-wickets, and commentary all update from a single live feed — so the number you see is the number that just happened.

A look built for Nepali cricket

We gave CricNepal a proper identity, drawn straight from the national kit — navy, red, and white. New player and team pages, cleaner cards, a sharper match centre, and a consistent feel across the whole site.

Dark mode 🌙

The whole site now has a true dark mode — easy on the eyes for those late-night World Cup League games, with the same Nepal-navy identity carried through.

Follow your favourites

Create an account and follow players, teams, and tournaments. Save stories, play quizzes, and pick up where you left off — on any device.

Faster pages, better on Google

Pages now render instantly and are built to be found — so the match report, the player profile, and the live scorecard all show up when you search for them.

Same site, brand-new engine

All of this happened with zero downtime. We migrated the site piece by piece behind the scenes — players, teams, tournaments, the match centre, the homepage — while the live site kept running for everyone.

This is just the start. The new foundation means we can move faster on everything next: more stats, more live features, and one day, a mobile app powered by the very same engine.

Thanks for following along — and for being part of Nepali cricket. 🇳🇵🏏