Multi-step Booking Wizard

Multi-step

Book in four steps

A guided wizard that walks customers through service, schedule, contact details, and a final review.

Step 1 of 4

Service

25%
1
Service
2
Schedule
3
Your details
4
Review
About this block

Multi-step Booking WizardPRO

Stepper-based booking flow with numbered progress dots, animated progress bar, and four steps: service selection, schedule, contact details, and review. Back/Next navigation, per-step validation, and a live summary.

Booking19: Multi-step Booking Wizard

A single-card booking wizard that walks a customer through four internally-tracked steps (service, schedule, contact, review) with a numbered dot stepper, an animated percentage progress bar, per-step validation gating the Next button, and a final live summary of the selected service, date/time, and contact details.

Upgrade to Pro

Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.

Upgrade Now

Installation

Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.

Radix flavor

bash
npx shadcn add "https://ui.beste.co/r/booking19?email=YOUR_EMAIL&license_key=YOUR_KEY"

Base UI flavor

bash
npx shadcn add "https://ui.beste.co/r-base/booking19?email=YOUR_EMAIL&license_key=YOUR_KEY"

This installs the block to components/beste/block/booking19.tsx, plus the badge, button, input, and label shadcn/ui primitives it uses for the eyebrow badge, navigation buttons, and the contact-step form fields.

Quick start

The installed file exports booking19Demo alongside the block: the exact props behind the preview above. Spread it to get a working booking wizard in one line.

tsx
import { Booking19, booking19Demo } from "@/components/beste/block/booking19";

export default function Page() {
  return <Booking19 {...booking19Demo} />;
}

Then replace the demo with your own props. Written out, a trimmed setup looks like this:

tsx
import { Briefcase, Sparkles } from "lucide-react";
import { Booking19 } from "@/components/beste/block/booking19";

export default function Page() {
  return (
    <Booking19
      badge={{ label: "Multi-step", variant: "secondary" }}
      heading="Book in <strong>four steps</strong>"
      description="A guided wizard for service, schedule, contact, and review."
      currency="$"
      services={[
        {
          value: "consult",
          label: "Strategy Consultation",
          description: "45-minute call to map out your next 90 days",
          duration: "45 min",
          price: 120,
          icon: Briefcase,
        },
        {
          value: "audit",
          label: "Brand Audit",
          description: "In-depth review of your brand touchpoints",
          duration: "90 min",
          price: 240,
          icon: Sparkles,
        },
      ]}
      timeSlots={["09:00", "10:00", "11:00", "13:00", "14:00"]}
      steps={[
        { key: "service", label: "Service", description: "Pick what you need" },
        { key: "schedule", label: "Schedule", description: "Choose a time" },
        { key: "contact", label: "Your details", description: "How we'll reach you" },
        { key: "review", label: "Review", description: "Confirm and book" },
      ]}
      labels={{ nextButton: "Next", backButton: "Back", confirmButton: "Confirm booking" }}
    />
  );
}

Props

PropTypeDefaultDescription
badge{ label: string; variant?: "default" | "secondary" | "outline" }Section eyebrow badge above the heading
headingstringSection heading, supports inline <strong>
descriptionstringSub-copy under the heading
currencystring"$"Prefix prepended to every numeric price
servicesBooking19Service[][]Selectable service rows on step one
timeSlotsstring[][]Time-slot buttons rendered in the schedule step's 4-column grid
stepsBooking19Step[][]Ordered step definitions; drives the dot stepper, progress, and which content panel renders
labelsBooking19Labels{}All inline copy for counters, field labels, placeholders, and buttons
classNamestringExtra classes for the outer <section>
ts
type Booking19Service = {
  value: string;
  label: string;
  description: string;
  duration: string;
  price: number;
  icon: LucideIcon;
};

type Booking19Step = {
  key: "service" | "schedule" | "contact" | "review";
  label: string;
  description: string;
};

type Booking19Labels = {
  stepCounterTemplate?: string;
  serviceLabel?: string;
  dateLabel?: string;
  timeLabel?: string;
  nameLabel?: string;
  emailLabel?: string;
  phoneLabel?: string;
  notesLabel?: string;
  namePlaceholder?: string;
  emailPlaceholder?: string;
  phonePlaceholder?: string;
  notesPlaceholder?: string;
  reviewHeading?: string;
  backButton?: string;
  nextButton?: string;
  confirmButton?: string;
  todayLabel?: string;
  totalLabel?: string;
};

Behavior notes

  • All wizard state (currentStep, selected service, dateIndex, timeSlot, and the four contact fields) lives in internal useState and is never lifted out; the only externally observable event is handleConfirm, which just does console.log(...), so persisting a booking is left to the integrator.
  • Which panel renders is keyed off steps[currentStep].key, not the array position, so a key must be one of "service" \| "schedule" \| "contact" \| "review" to render anything; an unrecognized key shows a blank content area.
  • heading is injected via dangerouslySetInnerHTML, and <strong> inside it is styled bold and colored with text-primary.
  • The Next button is gated by per-step validation: service requires a selection, schedule requires both a date and a time slot, contact requires non-empty trimmed name and email (phone and notes are optional), and review always passes; the Back button is disabled on the first step.
  • The 7 date buttons are generated at render from new Date() (today plus the next six days via addDays), independent of timeSlots; the first is labelled with todayLabel and the strip scrolls horizontally with its scrollbar hidden.
  • service initializes to services[0]?.value, so the first service is preselected, while dateIndex and timeSlot start null and must be chosen before advancing past the schedule step.
  • The progress bar width and the percentage badge are both derived from ((currentStep + 1) / steps.length) * 100; completed dots turn emerald with a check icon, the active dot uses the primary color, and the footer total block only appears on the final step when a service is active.
  • stepCounterTemplate is rendered by string-replacing {current} and {total}, and price display concatenates currency directly in front of the raw price number with no locale or thousands formatting.

Wiring the form up

This block ships the form markup only; state, validation, and submit are yours to add. Our guide wires the shadcn Field primitives to React Hook Form, TanStack Form, and Formisch on one field system.

More Booking blocks

View all Booking
PRO

booking24

Service And Slot Picker

An interactive appointment picker: a hairline panel pairing a selectable service list with a scrollable day strip and a time slot grid that marks unavailable times, beside a full-height provider photo with a gradient name card, closing on a live summary bar and confirm action.

PRO

booking6

Restaurant Reservation

Restaurant table reservation with guest count selector, date/time picker, seating preference (indoor/outdoor/bar), special requests textarea, and booking summary. Fine dining reservation flow.

PRO

booking1

Date & Time Picker

Split-panel booking interface with interactive monthly calendar on the left and time slot grid on the right. Features today highlight, disabled dates, selected state, morning/afternoon grouping, and booking summary footer.

PRO

booking9

Event Ticket Booking

Event ticket selection with event banner, date/venue info, ticket tier cards (General/VIP/Backstage) with perks list, quantity selector, and order total. Concert and conference ticket flow.

PRO

booking18

Cinema Seat Picker

Interactive seat selection grid for cinemas, theaters, and events. Row/column layout with an aisle gap, tier-based pricing (standard/premium/VIP), occupied seats, a live selection summary, and a legend. Includes screen indicator at the top.

FREE

booking4

Service Selector

Service selection grid with category filter tabs, service cards showing icon, name, description, duration, price, and selected state. Ideal for spa, salon, clinic, and fitness booking flows.