One website template, twenty-one live sites: every Lancaster hotel, suite and resort from Beirut to Djerba, each with its own admin, its own database and its own domain.
One website template, twenty-one live sites: every Lancaster hotel, suite and resort from Beirut to Djerba, each with its own admin, its own database and its own domain.
live · multi-site platform · hospitality · five languages
21Sites on one template
5Languages, incl. Arabic RTL
1,000+Commits across the repos
Mar → Aug 2026Built in
Lead developer
I designed and built all twenty-one hotel websites: the UI and UX, the front-end and API code, the admin panel and the way each site connects to it, the server and the deploy pipeline. Layth Ayache worked alongside me on property forks, content and media.
UI and UX design for the public sites and the admin
Front-end, API and admin panel for all twenty-one sites
Server, Caddy, systemd and the deploy pipeline
Content model, five languages, AI concierge and forms
Who did what
Tayseer
All twenty-one hotel websites: web development, UI and UX design, the admin panel and its connection to every site, architecture and infrastructure
Layth Ayache
Property forks, content seeding, photo scraping from the legacy sites, admin population
Structure, design and layout
Before the first site shipped, one plan had to work for every hotel: a page structure every property shares, a design system that carries a luxury brand without looking the same twice, and a layout every page is built from.
Site map, every property
Stay
Home
Rooms & suites
Room detail
Offers
Offer detail
Experience
Dining
Dining venue
Events
Event venue
Wellbeing
Wellbeing detail
Facilities
Gallery
Hotel
About
Contact
Booking
Chain hub only
Our hotels
Locations
Lancaster Loyalty
Media hub
Article
Careers
Development
Legal
Privacy policy
Cookies policy
Not found
Design system
Navy#173A63
Lancaster red#C41E3A
Ink#1A1A2E
Paper#FAF9F7
Surface#FFFFFF
Muted#6B7280
Headings
Aire Bold Pro, a high-contrast serif, with Cormorant Garamond as fallback
Body and UI
Gotham, with Montserrat as fallback; GE Hili for Arabic
Scale
Six sizes from 0.7rem labels to a fluid display at clamp(2.6rem, 6vw, 5rem)
Radii
2, 4 and 8px, plus pills for buttons: sharp, hotel-brochure corners
Containers
1280px wide, 880px narrow, 720px for reading
Page layout
Header
Hamburger and search on the left, the hotel's own logo centred, language, log in and Reserve on the right. Transparent over the hero, solid once scrolled.
Sub-nav
The hotel name plus Overview, Accommodation, Offers, Dining, Events, Wellbeing, Facilities and Contact, pinned under the hero on every page.
Hero
Full-bleed slideshow from the admin's hero slides, the page title in serif at the bottom left, arrows and dots.
Sections
Seven reusable blocks: overview, rooms, card grid, facilities, wellbeing, contact and newsletter, composed per page.
Listings and details
One listing layout and one detail layout shared by rooms, dining, events, offers and wellbeing, so every content type looks and behaves the same.
Footer and overlays
A full-screen navigation, a search overlay, the cookie bar and the AI concierge bubble, identical across the chain.
How it was built
01
One template, cloned per hotel
lancaster-acra is the canonical template. Each property is a clone with its own repo, its own GitHub variables for the hotel name, description and booking URL, and its own branch rules. Changes to the template are pulled into the forks.
02
Admin and content model
A React admin inside the same Vite app, with a generic content editor over ten tables: rooms, dining venues, event venues, facilities, gallery images, hero slides, news articles, offers, page content and wellbeing sections. Rich text through TipTap, sanitised with DOMPurify, ordered with dnd-kit.
03
Five languages
English, French, Arabic, Chinese and Vietnamese through i18next, with the admin's Translations page editing the locale files per site and Arabic rendering right to left.
04
The AI concierge
A chat route on each site that answers as the hotel's front desk, grounded in that hotel's rooms and venues, limited to the Lancaster group, with per-site custom instructions edited in the admin. Rate-limited per visitor.
05
Forms
Contact, careers and newsletter forms verified with reCAPTCHA, rate-limited, stored as submissions in the admin and emailed to the hotel.
06
Branding per site
Logo, share image, phone numbers, emails, social links, header and footer, SEO fields and page visibility, all in a site-settings table read by the public site at load.
07
Media
A single public-read bucket with one folder per hotel. The API signs uploads from the admin; the hero slider and galleries read the public URLs directly.
08
Security
Helmet, CORS allow-lists per site, three rate limiters for public, forms and chat, session auth for the admin, and no secrets in any repo: real values live on the server and in GitHub secrets.
Stack
Front end
Vite + React single-page app, Framer Motion, react-helmet-async for per-page SEO, served as static files.
API
Express on Node 20, one systemd unit per hotel on its own port.
Data
One Postgres database per hotel on a shared instance, each with its own user.
Media
Google Cloud Storage, one bucket, one prefix per hotel.
Edge
Caddy on the VM with one virtual host per hotel and the chain hub as the catch-all. Custom domains such as lancasterplaza.com point at the same box.
Deploy
Push to main runs a GitHub Action that builds the SPA, copies it over SSH, and runs the deploy script for that hotel's slug, with a health check at /api/health.
Earlier iteration
Accra first shipped on Vercel with Supabase before the move to the single VM that now runs all twenty-one.
The final product
Eight of the twenty-one live sites, captured from the servers today.
A resort site with a seasonal offer pop-up managed from the admin.
Also on the template: Lancaster Accra, Tamar, Lancaster Suites and Lancaster Hotel in Raouche, Re-Ndama in Libreville, Michelangelo, Al Salam Resort, and six Djerba properties: Djerba, Narjis, Suites Al Manara, Dhalia, Zahra and Yasmin.
What every site does
The same pages and the same admin on every property, filled with that hotel’s own content.
01
Rooms and suites
Every room is a database row: bed type, capacity, amenities, gallery and a book-now link that opens the hotel's booking engine. Listings and detail pages share one layout component.
02
Dining, events and wellbeing
Restaurants and bars, meeting and event venues, and spa and wellbeing sections each follow the same listing-and-detail pattern, so adding a venue is one admin form.
03
Gallery and hero
Hero slides and gallery images live in one Google Cloud Storage bucket with a prefix per hotel, uploaded straight from the admin, reordered by drag and drop.
04
The chain hub
The group site lists every property, carries the loyalty programme, the media hub with articles, careers with an application form, and the development page for new hotels.
05
The admin
Each site has its own Lancaster CMS at /admin: dashboard, analytics, a page editor for the hero and every banner, content tables for rooms, dining, events, wellbeing, offers, gallery and news, submissions, supplier offers, a media library, the chatbot's instructions, branding and settings, and translations in all five languages.
How it was built
01
One template, cloned per hotel
lancaster-acra is the canonical template. Each property is a clone with its own repo, its own GitHub variables for the hotel name, description and booking URL, and its own branch rules. Changes to the template are pulled into the forks.
02
Admin and content model
A React admin inside the same Vite app, with a generic content editor over ten tables: rooms, dining venues, event venues, facilities, gallery images, hero slides, news articles, offers, page content and wellbeing sections. Rich text through TipTap, sanitised with DOMPurify, ordered with dnd-kit.
03
Five languages
English, French, Arabic, Chinese and Vietnamese through i18next, with the admin's Translations page editing the locale files per site and Arabic rendering right to left.
04
The AI concierge
A chat route on each site that answers as the hotel's front desk, grounded in that hotel's rooms and venues, limited to the Lancaster group, with per-site custom instructions edited in the admin. Rate-limited per visitor.
05
Forms
Contact, careers and newsletter forms verified with reCAPTCHA, rate-limited, stored as submissions in the admin and emailed to the hotel.
06
Branding per site
Logo, share image, phone numbers, emails, social links, header and footer, SEO fields and page visibility, all in a site-settings table read by the public site at load.
07
Media
A single public-read bucket with one folder per hotel. The API signs uploads from the admin; the hero slider and galleries read the public URLs directly.
08
Security
Helmet, CORS allow-lists per site, three rate limiters for public, forms and chat, session auth for the admin, and no secrets in any repo: real values live on the server and in GitHub secrets.
Stack
Front end
Vite + React single-page app, Framer Motion, react-helmet-async for per-page SEO, served as static files.
API
Express on Node 20, one systemd unit per hotel on its own port.
Data
One Postgres database per hotel on a shared instance, each with its own user.
Media
Google Cloud Storage, one bucket, one prefix per hotel.
Edge
Caddy on the VM with one virtual host per hotel and the chain hub as the catch-all. Custom domains such as lancasterplaza.com point at the same box.
Deploy
Push to main runs a GitHub Action that builds the SPA, copies it over SSH, and runs the deploy script for that hotel's slug, with a health check at /api/health.
Earlier iteration
Accra first shipped on Vercel with Supabase before the move to the single VM that now runs all twenty-one.