How We Built SPH Services' New Site
The SPH Services site is a React app that ships a finished page for every route, with a page for each airport and area the company serves and a dashboard the team runs itself. This is how we built it, and why.
What was the problem with a single-page app?
A single-page React app sends the browser an almost empty page, then builds the content with JavaScript. That is fast to build and pleasant to use, but the first response contains none of your words. For a site that has to answer questions in plain text, that was the problem to solve.
How does every page ship as real HTML?
We added two steps to the build. The first generates the sitemap. The second opens every route in a headless browser and saves the finished page as a static HTML file.
The list of routes comes from two places.
- A fixed list of pages, such as services, fleet, aviation, about, contact and the legal pages.
- The database, which supplies every active car, service and aircraft, and every published journal entry.
A new car or journal entry gets its own page and its own sitemap entry the next time the site builds. A watchdog stops the prerender step from ever hanging a deploy.
Which pages did we add, and why?
Clients ask about specific places and specific jobs, so we gave each its own page.
- Four airport pages: Heathrow, Gatwick, Stansted and London City.
- Four area pages: Mayfair, Knightsbridge, Chelsea and Kensington.
- Four dedicated service pages: airport transfers, weddings, corporate travel and long-term hire.
The pages link to their siblings, carry breadcrumbs, and are reachable from a service areas row in the footer. The home page answers the four questions clients ask most, and marks those answers up as an FAQ.
What structured data does the site carry?
Each page type has its own markup: the organisation, services, cars and aircraft, breadcrumbs, FAQs and journal articles. A price goes into the markup only when a real one exists. A car or aircraft priced on request carries no price, rather than an empty or invented one.
How do we stop it breaking?
A check runs in GitHub Actions on every pull request. It builds the site and then fails if any prerendered page lacks a title, a description or structured data, or has anything other than a single main heading. It also confirms the sitemap lists as many cars, services and aircraft as the database holds.
What does the team manage themselves?
The dashboard sits behind a sign-in. A sidebar groups bookings, enquiries, customers, the fleet and the content, so the team can add a car or publish a journal entry without a developer. It also shows counts of calls, WhatsApp taps and form submissions, and Google Search Console performance. The dashboard code loads only when someone opens the dashboard, so it stays out of the code every visitor downloads.
Analytics is split on purpose. Google Analytics loads only after a visitor accepts a short consent banner. The dashboard's own counts use no cookie or identifier, so they do not need consent.
What is it built with?
- Vite, React and TypeScript, with React Router.
- Supabase for the database and the sign-in, and an Edge Function that sends booking and enquiry emails through Resend.
- Tailwind for styling.
- Hosted on Vercel.
First published
Pulock Deb Roy is the Co-Founder and Chief Marketing Officer of Ravenence, leading brand strategy, digital growth, and business development.