Skip to content
Menu

Website + bookings / Tourism

Website, bookings and performance for a campervan hire business in Málaga

A site with real-time availability, online payment and routes across Andalusia in nine languages, optimised after a mobile performance audit that found where the seconds were being lost.

Context
Campervan hire · Málaga · 9 languages
Project type
Website + bookings / Tourism
Status
Live

Project sheet

Project

sundaycampers.es

Type
Website + bookings
Stack
Next.js · Supabase · Stripe
Channel
Web, 9 languages
Status
In production
Project summary. Not a screenshot of the application.
Languages
9
Localised interface and content
Route pages
48
Six routes across Andalusia in eight languages
Analytics containers
1
Previously two running in parallel

01 / Problem

A campervan hire business sells mainly to foreign tourists comparing options on their phones, sometimes from abroad and in a hurry. It needs to show real availability, take payment without intermediaries and appear in several languages. The mobile audit in August 2026 showed that the main image took almost five seconds to render, not because of its size but because of avoidable waits.

02 / Context

Context

The audit broke the problem into measurable parts. 65% of the delay was time spent waiting for the main image to start loading, because the element did not exist until the page hydrated. On top of that came two analytics containers running in parallel, a redirect from the root to the Spanish version costing around 700 ms and images linked from third-party domains.

See the web design service

03 / Solution

How we solved it

The website is built with Next.js on Supabase and Stripe. Availability is read from a daily view per vehicle and synced with external calendars via iCal. The price is recalculated on the server before the payment is created, and the database itself prevents two bookings from overlapping. After the audit, each cause it found was fixed in the code, one by one.

Flow

  1. 01

    Exclusion constraint in Postgres so two bookings cannot overlap

  2. 02

    Price recalculated on the server, without trusting the total sent by the browser

  3. 03

    Checkout with an idempotency key and a webhook with a verified signature

  4. 04

    Hero rendered on the server with high priority instead of waiting for hydration

  5. 05

    Per-language hreflang with x-default and a sitemap with real modification dates

Workstreams

Bookings that never clash

Overbooking is prevented in the database, not in the interface. Even if two payments arrive at once, Postgres rejects the second.

  • Date ranges with an exclusion constraint
  • Idempotent iCal sync with other channels
  • Bookings awaiting payment reused, not duplicated

Performance tackled at the cause

Each fix responds to a measured audit finding, not to a generic list of best practices.

  • One analytics container instead of two
  • A rewrite at the root instead of a 307 redirect
  • Legacy polyfills removed with an up-to-date browser list

Properly international

Visitors arrive in many languages, so routes, vehicles and landing pages each have their own version with reciprocal hreflang.

  • Nine interface languages
  • Structured data for the rental business, routes and FAQs

04 / Scope

What it does

  • Shows each campervan's availability with blocked days on the calendar
  • Calculates the price with extras and takes payment through Stripe Checkout
  • Syncs bookings from other channels via iCal
  • Publishes routes across Andalusia and vehicle pages in several languages
  • Offers direct WhatsApp contact from each vehicle page

05 / Results

What changed

The main image is now rendered on the server with high priority, a single analytics container remains and loads deferred, the root resolves with a rewrite instead of a redirect and all images are served from the site's own domain. The site still needs to be re-measured with the same method before the after figures are published; once they are, they will appear here with their date.

Security and confidentiality

  • Row-level security on the Supabase tables.
  • The Stripe webhook verifies each event's signature before confirming a booking.
  • Third-party images were replaced with the business's own images hosted on its domain.

06 / Questions

Frequently asked questions

Why have the performance figures not been published yet?

Because we only publish numbers measured with a stated method and date. The fixes are in the code; the new PageSpeed Insights measurement, using the median of several runs, will be published on this page once it is done.

Why was the image size not the problem?

Downloading the image took about 126 ms. The time went on waiting: almost 1.8 seconds before it started loading, because the element was not in the initial HTML, and another 0.8 seconds before it could be painted.

Your next project

Want a case like this for your business?

Let's talk about your project