Command Palette

Search for a command to run...

Building a Booking System with Next.js

Lessons from shipping salon and tour booking platforms — forms, payments, admin flows, and keeping UX simple.

Nguyễn Đình Giang

· · 1 min read

Frontend developer in Ha Noi. I write about the Next.js, WordPress, and performance work I actually ship.

Booking flows look simple on the surface: pick a date, pick a slot, pay, done. In practice, they touch authentication, inventory, payments, notifications, and admin dashboards.

I've shipped this pattern on a salon booking platform (appointments + e-commerce) and a tour-booking platform (search, filters, and custom requests).

Core user flow

  1. Browse services or tours
  2. Filter by date, time, stylist, or destination
  3. Add to cart or book directly
  4. Pay via an integrated payment gateway
  5. Receive confirmation (email / admin notification)

The goal is zero confusion between steps. Every screen should answer: where am I, what did I pick, and what's next?

Frontend architecture

// Simplified booking state
const [step, setStep] = useState<"browse" | "details" | "payment">("browse")
const [selection, setSelection] = useState<BookingSelection | null>(null)

I typically use Zustand for multi-step booking state — lighter than Redux for form-heavy flows, but still easy to debug.

Admin side matters too

Clients need to manage appointments, update availability, and handle cancellations. WordPress admin + custom post types often handle this, while Next.js serves the customer-facing UI.

What I learned

  • Validate early: disable invalid dates/times before the user reaches payment
  • Show loading states on payment redirects — users panic on blank screens
  • Log booking failures server-side; "payment failed" without context is useless
  • Mobile-first: most booking traffic is on phones

Those lessons show up in the salon booking platform and the tour booking platform write-ups. For the CMS side of the same builds, see WordPress as a headless CMS.

A good booking system feels boring in the best way — predictable, fast, and trustworthy.

Further reading: Zustand, Next.js Forms and Mutations, and payment-provider docs for the gateway you actually wire (never invent a PCI flow in the article).

River BotAsk me