DEV Community

Cover image for Built a full salon booking website in a weekend — here's the stack
Stan
Stan

Posted on

Built a full salon booking website in a weekend — here's the stack

The thing nobody warns you about with booking sites

A booking website looks like a weekend project until you actually try to build one. The UI is easy — a service list, a calendar, a "Book now" button. Then you hit the real question: is this slot actually available?

That answer isn't a row in a calendar table. It depends on which staff member can do this service, which location they're at, how long the service runs, whether they're already booked, days off, buffers between appointments, and — for classes — how many seats are left. Get it slightly wrong and you double-book a stylist, which is a great way to lose a customer on day one.

I did not want to build that engine again. I've built scheduling logic before and it's the kind of thing that's "done" five times before it's actually done. So this time I let something else own the hard part and spent my weekend on the parts users actually see.

The stack

Layer Choice Why
Framework Next.js 15 (App Router) Server components for the data fetches, fast deploys to Vercel
Language TypeScript The API responses are typed end-to-end via the SDK
Styling Tailwind CSS The template ships with it; rebranding is mostly editing tokens
Booking backend Opencals Storefront API Availability, staff, locations, cart, Stripe checkout, customer accounts
Payments Stripe (through the API) No payments backend of my own to build or maintain
UI Claude (Opus 4.8) I described the screens; it wrote the React + Tailwind components against the typed SDK
Hosting Vercel git push and it's live

The part I want to be honest about: the Opencals Storefront API is the reason this was a weekend and not a month. It's the booking infrastructure behind Opencals (a service-commerce platform I work on), exposed as a plain REST API with a typed TypeScript SDK. Services, availability, staff, locations, cart, checkout, customer self-service — all endpoints I'd otherwise have had to design, build, and debug myself. With the scheduling logic owned by the API and a small typed surface to write against, Claude handled most of the UI components cleanly.

Step 1 — Clone the template

I didn't start from create-next-app. There's an open-source salon template — Haar — that already wires the whole flow to the API (announcement post · docs):

git clone https://github.com/letsopencals/template-haar.git
cd template-haar
npm install
Enter fullscreen mode Exit fullscreen mode

It's template-haar — Next.js 15, TypeScript, Tailwind, MIT license. Services (with variants), staff, multi-location, real-time availability, add-ons, Stripe checkout, and customer accounts are all already connected. There's a live demo if you want to click through it first. My weekend was mostly rebranding and tweaking, not plumbing.

The template's storefront out of the box — service menu with prices and durations, before any rebranding.

Step 2 — Create a store, seed it with Haar data, and deploy

This is the part that makes it fast. The template reads everything from one Opencals store, identified by a Storefront API key — and you can have a live, populated store in three steps, no code required:

  1. Create a free Dev Store in the Opencals dashboard.
  2. Choose the "HAAR Salon" seed dataset when prompted. It's built specifically for this template — it pre-loads the exact services, staff, and locations from the live demo, so your store isn't empty and the cloned site renders real content from the very first load.
  3. Create a Storefront API Key under Settings → API Keys (it starts with sfk_).

Then deploy: hit Deploy with Vercel in the README, paste your sfk_ key when Vercel asks for the environment variable, and it builds in about two minutes. That's a working booking site before you've written a line of code.

Running it locally instead? Drop the key into .env.local:

OPENCALS_API_KEY=sfk_your_key_here
Enter fullscreen mode Exit fullscreen mode

Keep the key server-side — it identifies your store and not recommended to ship to the browser. The quickstart covers it in more detail.

The whole no-code path in one place — create a store, choose the HAAR Salon seed data, grab your  raw `sfk_` endraw  key in Settings, and deploy with that key.

Step 3 — Listing services and fetching availability

The whole booking flow is seven typed calls (list services → pick staff → fetch slots → create appointment → cart → add-ons → checkout), and with the @opencals/storefront-sdk each one is basically a one-liner. You initialize the SDK once and the key stays server-side:

npm install @opencals/storefront-sdk
Enter fullscreen mode Exit fullscreen mode
// lib/opencals.ts — run once, server-side only
import { setupOpencals } from "@opencals/storefront-sdk";

setupOpencals({
  apiKey: process.env.OPENCALS_API_KEY, // sfk_… from Settings → API Keys
});
Enter fullscreen mode Exit fullscreen mode

Then the two calls that drive the whole front page — the service menu and real-time slots:

import "@/lib/opencals";
import { ProductService } from "@opencals/storefront-sdk";

// 1. List the bookable services (your menu)
const { data } = await ProductService.list({ query: { take: 50 } });
const services = data!.data; // Product[] — title, price, duration, staffMembers…

// 2. Real-time availability for one service on a given day
const { data: slots } = await ProductService.getCurrentAvailabilities({
  path: { productId: services[0].id },
  query: {
    date: "2026-07-01",
    timezone: "America/New_York", // slots come back converted for display
  },
});
Enter fullscreen mode Exit fullscreen mode

That getCurrentAvailabilities call is the part I was dreading building. It already accounts for which staff can do the service, at which location, for how long, and what's already booked — pass an optional staffMemberId and it filters to one person. I render the slots and move on with my life.

Real-time slots for the selected day and stylist — straight from  raw `getCurrentAvailabilities` endraw .

From there it's create the appointment, drop it in a cart, optionally attach add-ons, and check out through Stripe. The full sequence is documented step by step in the Build a booking page guide, so I won't paste all seven here.

Add-ons (e.g.

Step 4 — Customer accounts (the part I almost skipped)

I assumed self-service rescheduling would be the thing I'd cut for v1. It wasn't — the API handles customer auth (JWTs) and the account actions, so the template already had a customer portal where someone can log in, see their bookings, and reschedule or cancel without emailing the salon. For a real salon that's the difference between a booking tool and a front desk that never sleeps.

A logged-in customer rescheduling their own appointment — no phone call, no email

Step 5 — Make it yours

Rebranding is one file. Everything salon-specific — name, tagline, logo, hero, team, hours, contact, social — lives in lib/site-config.ts, and the brand colors are CSS custom properties in app/globals.css. Edit those and Haar stops looking like Haar.

@theme {
  --color-accent: #E8530E;   /* your brand color */
  --font-display: 'Playfair Display', Georgia, serif;
  --font-body: 'Inter', system-ui, sans-serif;
}
Enter fullscreen mode Exit fullscreen mode

You already deployed in Step 2, so a rebrand is just editing that config and pushing — Vercel redeploys on every commit. That's the weekend.

What you actually get

  • A real Next.js booking website, not a calendar embed
  • Real-time availability across multiple staff and locations
  • Add-ons, Stripe checkout, and deposits without a payments backend
  • Customer accounts with self-service reschedule/cancel
  • One codebase you can rebrand per client — MIT, no royalties (relevant if you're an agency)

Try it without committing

You don't need an account to look around. The interactive API reference lets you browse every endpoint in the browser, no key required. If you want to build:

This is Part 1 of a series where I rebuild the whole thing piece by piece — next up, the API calls in full, then Stripe deposits, then rendering availability in React the right way. If you build something with it, drop a comment — I read all of them and I'm curious what verticals people try beyond salons.

— Stan, building Opencals

Top comments (1)

Collapse
 
stangineer profile image
Stan • Edited

A bit of the why behind this, for anyone curious...

Beauty might be the industry most underserved by booking software. Most tools funnel salons into non-branded marketplaces: you get a listing, a "book now" button, and a page that looks identical to the hundred competitors sitting right next to you. Your brand disappears, and you end up effectively renting customers from a platform that's also selling your competitors on the same screen.

The alternative is your own branded site with real booking, it has always been expensive. And even when a salon can afford a custom website, the booking logic underneath (availability across staff and locations, deposits, reschedules, cancellations) gets rebuilt from scratch every single time. That's the part nobody should be paying to rebuild.

So we open-sourced it. Haar is a real, deployable salon site, and the hard booking logic lives behind the Opencals API. In practice that means: clone it, spend up to $50 of AI tokens shaping it to your brand, deploy free on Vercel, point a domain at it - and you've got a genuinely nice working booking website for roughly the price of the domain.

And if you're not a salon: the template doubles as the best documentation we have for how the API and SDK actually work. Read it, copy the patterns, build your own thing on top.
Covering more verticals in the future - barbershops and fitness are already in progress.