Academy Marketing Platform
An SEO-first marketing platform and full CMS for a UK children's football academy

The academy runs weekly classes, holiday camps, birthday parties, and school programmes across a growing list of UK towns. Its entire acquisition channel is one search: "football classes near me." We delivered the three applications behind that funnel, the public website, the admin CMS, and the REST API, along with the AWS media pipeline, SEO architecture, and analytics wiring that connect them.
The project was as much a re-architecture as a build. The site began life as a client-rendered React single-page application: content arrived after page load, metadata lived in the browser, and every new town page was hand-built by a developer. We migrated it to server-rendered Next.js incrementally, using scripted, idempotent migrations so the live site kept shipping throughout the transition.
The result is a platform where a small academy team runs its full acquisition funnel, indexable town pages, class discovery, enquiry routing, and ad-spend attribution, without calling a developer for content changes.
A local-search business built on an unindexable site
Parents need to find a class close to home, for the right age group, and book a free trial with as little friction as possible. Meanwhile the academy's small team needs to update programmes, venues, prices, testimonials, and blog content across multiple towns without developer involvement.
The existing client-rendered architecture worked against both goals. Search engines saw an empty shell rather than real content, a serious limitation when the business model depends on ranking for "football classes in {town}", and each town landing page was hardcoded, making geographic expansion a development task instead of a content task.

Incremental re-architecture, with SEO as a product feature
We migrated the public site to Next.js server rendering without a big-bang rewrite. An idempotent migration script converted image tags to optimized image components, replaced browser-side SEO tooling with Next.js metadata, and centralized the shared layout, while legacy routes stayed mounted through a catch-all until each page was migrated.
We made locations data, not code. A migration consolidated per-town collections and hardcoded pages into a single locations collection with admin CRUD, so the academy opens a page for a new town from the CMS. A URL rewrite serves each page at a keyword-rich address like /football-classes-in-barnet while one implementation renders them all.
We treated SEO and measurement as product features: structured data on every page type, canonical URLs with slug normalization and redirects, a database-driven sitemap, per-page titles and descriptions editable from the CMS, and named conversion events wired for Google Ads import, including the outbound click that hands a parent off to the booking platform.

From "classes near me" to a booked free trial
A parent lands on the home page or a town landing page, opens the class finder, and types an address or shares their location. The finder geocodes the input through Google Maps, queries the API for classes within a 10 km radius filtered by the child's age, computes driving distance to each venue with a straight-line fallback, and lists classes sorted by miles. Radius search runs on a MongoDB 2dsphere geospatial index, so proximity filtering happens in the database rather than in application code.
The admin panel gives the academy control over effectively every word and image on the site: home page sections, programmes, holiday camps, birthday parties, school and nursery programmes, franchise recruitment, testimonials, FAQs, a blog with slug management, a hall of fame for player success stories, coupons, and the location pages themselves. A dedicated Page SEO module sets title, description, keywords, and social-share image per URL, merged over per-entity defaults at render time.
Around the core funnel we built the operational machinery of a production site: enquiry forms delivering templated emails per enquiry type, an S3/CloudFront media pipeline with presigned browser-direct video uploads and FFmpeg thumbnails, a bulk WebP conversion pipeline that rewrites stored URLs across the database with dry-run and restore paths, and scheduled jobs for coupon and membership expiry.
Geolocation class finder
Address geocoding, 10 km radius search on a MongoDB geospatial index, age filtering, and results sorted by driving distance, answering the core customer question in one flow.
CMS-managed local-SEO landing pages
Town pages served at keyword-rich URLs like /football-classes-in-barnet, opened from the CMS without developer involvement, with structured data, canonical redirects, and a database-driven sitemap.
Full-site content management
Rich-text editing, drag-and-drop ordering, and validated forms across programmes, camps, parties, schools, franchise recruitment, blog, testimonials, FAQs, success stories, and coupons.
Per-page SEO control
A dedicated CMS module for titles, descriptions, keywords, and social-share images per URL, merged into Next.js generateMetadata with a secured revalidation endpoint to refresh cached pages.
Conversion tracking across a third-party handoff
GA4 events wired for Google Ads import capture the outbound booking click with a callback-and-timeout pattern, so the event is recorded before navigation but a blocked analytics script never stops a parent from booking.
Enquiry and lead capture
Contact, franchise, birthday-party, school-programme, and recruitment forms post to dedicated endpoints delivering templated HTML emails, giving each enquiry type its own routing.



- 01
Server-rendered Next.js public site
Next.js App Router with server-side CMS fetches so copy ships in the initial HTML, JSON-LD for Organization, LocalBusiness, and Breadcrumb schemas, dynamic sitemap and robots, and Vercel deployment pinned to the London region with hardened security headers.
- 02
Three-application architecture
A React and Material UI admin panel with Redux Toolkit, Formik validation, and drag-and-drop ordering; an Express API with JWT-authenticated route groups and roughly 90 admin endpoints; and MongoDB with around 50 Mongoose collections.
- 03
AWS media pipeline
S3 and CloudFront storage, presigned URLs for browser-direct 100 MB video uploads that bypass the API server, FFmpeg video thumbnails, and Sharp-based bulk WebP conversion that rewrites every stored image URL, runnable as a dry-run CLI or an authenticated admin endpoint.
- 04
Repeatable migration tooling
Scripted migrations for the CRA-to-Next.js transition, legacy location consolidation, blog slug changes, and image URL rewrites, all supporting dry runs, reflecting the caution of operating against live production data.
- 05
Quality and measurement wiring
GA4, Meta Pixel, and optional GTM loaded from server-read configuration with route-change tracking, plus Lighthouse CI asserting 90+ targets for performance, accessibility, best practices, and SEO across five key URLs.
- Every page, including town-level landing pages, is server-rendered, indexable, structured-data-annotated, and editable by non-developers.
- Opening a page for a new town became a content task in the CMS rather than a development task.
- The academy can attribute ad spend to trial bookings despite bookings completing on a third-party platform.
- The site was re-architected from a client-rendered SPA to server-rendered Next.js incrementally, while it kept running in production.
- The full historical media library was converted to WebP with database URL rewriting, safely enough to run against live data.