Reservation Summary

Almost done

Review your reservation

Double-check the details below and confirm your appointment in one click.

Deep Tissue Massage

Full body deep tissue massage targeting muscle tension and chronic pain relief.

Saturday, April 18, 2026
14:00 · 60 min
Serenity Spa, 42 Wellness Ave

Dr. Emily Carter

Licensed Massage Therapist

4.9(127 reviews)
Deep Tissue Massage (60 min)$120.00
Hot Stone Add-on$25.00
First-time Discount-$15.00
Tax$10.40
Total$140.40

Free cancellation up to 24 hours before your appointment. Late cancellations may incur a 50% charge.

About this block

Reservation SummaryPRO

Booking confirmation card with service details, date/time, provider info with avatar, price breakdown with subtotal/tax/discount/total, promo code input, cancellation policy, and confirm CTA.

Booking2: Reservation Summary

A centered booking confirmation card that stacks a service name and description, a muted panel of date/time/duration/location rows, a provider block with avatar and star rating, a promo-code input that validates against a known code, a price breakdown with subtotal/tax/discount/total lines that recomputes the total when a valid promo is applied, a full-width confirm button, and a cancellation-policy footer.

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/booking2?email=YOUR_EMAIL&license_key=YOUR_KEY"

Base UI flavor

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

This installs the block to components/beste/block/booking2.tsx, plus the avatar, badge, and button shadcn/ui primitives it uses for the provider photo, the section eyebrow, and the apply/confirm actions.

Quick start

The installed file exports booking2Demo alongside the block: the exact props behind the preview above. Spread it to get a working reservation summary in one line.

tsx
import { Booking2, booking2Demo } from "@/components/beste/block/booking2";

export default function BookingReviewPage() {
  return <Booking2 {...booking2Demo} />;
}

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

tsx
import { Booking2 } from "@/components/beste/block/booking2";

export default function BookingReviewPage() {
  return (
    <Booking2
      badge={{ label: "Almost done", variant: "secondary" }}
      heading="Review your <strong>reservation</strong>"
      serviceName="Deep Tissue Massage"
      date="Saturday, April 18, 2026"
      time="14:00"
      duration="60 min"
      location="Serenity Spa, 42 Wellness Ave"
      provider={{ name: "Dr. Emily Carter", role: "Massage Therapist", rating: 4.9, reviews: 127 }}
      pricing={[
        { label: "Deep Tissue Massage (60 min)", amount: "$120.00" },
        { label: "Tax", amount: "$10.40", type: "tax" },
        { label: "Total", amount: "$130.40", type: "total" },
      ]}
      validPromoCode="123456"
      promoDiscount={{ label: "Promo code ({code})", amount: "-$20.00" }}
      labels={{ applyButton: "Apply", confirmButton: "Confirm Booking" }}
    />
  );
}

Props

PropTypeDefaultDescription
badge{ label: string; variant?: "default" | "secondary" | "outline" }Centered eyebrow badge above the heading
headingstringSection heading, supports inline <strong>
descriptionstringMuted subheading under the heading
serviceNamestringBold service title at the top of the card
serviceDescriptionstringMuted line under the service name
datestringDate row with a calendar icon
timestringTime row with a clock icon
durationstringAppended to the time row after a · separator
locationstringLocation row with a map-pin icon
providerProviderProvider block with avatar, role, and rating
pricingPriceItem[][]Line items in the price breakdown
cancellationPolicystringFooter policy text with a shield icon
validPromoCodestringCode the promo input is compared against
promoDiscountPromoDiscountDiscount line inserted when the code matches
labelsBooking2Labels{}Copy for the promo input, buttons, and messages
classNamestringExtra classes for the outer <section>
ts
type Provider = {
  name: string;
  role: string;
  avatar?: string;
  rating?: number;
  reviews?: number;
};

type PriceItem = {
  label: string;
  amount: string;
  type?: "regular" | "discount" | "tax" | "total";
};

type PromoDiscount = {
  label: string;
  amount: string;
};

type Booking2Labels = {
  promoPlaceholder?: string;
  applyButton?: string;
  appliedButton?: string;
  confirmButton?: string;
  reviewsSuffix?: string;
  promoSuccessMessage?: string;
  promoInvalidMessage?: string;
};

Behavior notes

  • The promo code is validated purely client-side by string-comparing the trimmed input against validPromoCode; there is no async call and no callback, so a real coupon check must be wired in by the integrator.
  • Applying a valid promo recomputes the breakdown in a useMemo: it pulls the type: "total" row out, appends the promoDiscount line, then re-adds a total whose amount is re-derived by parseAmount/formatAmount (a naive regex that reads the first number and reuses the currency symbol from the original total string).
  • If pricing has no type: "total" item, the promo line is simply appended and nothing is recalculated.
  • Promo status lives in internal useState ("idle" \| "valid" \| "invalid") that is never lifted out; the Apply button is disabled while the input is empty or the code is already valid, and typing resets the status back to "idle".
  • Both promoInvalidMessage and promoDiscount.label substitute {code} with validPromoCode at render time, so the invalid message can leak the working code (the demo does this on purpose).
  • heading is rendered through dangerouslySetInnerHTML, and its inline <strong> is auto-styled bold and text-primary.
  • The "Confirm Booking" button has no onClick handler at all: it is a decorative CTA, so submitting the reservation is left entirely to the integrator.
  • Every content block is conditionally rendered, so omitting serviceName, provider, pricing, cancellationPolicy, or the whole badge/heading/description header collapses that region without leaving empty spacing.

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
FREE

booking15

Booking Confirmation Receipt

Post-booking success screen with animated check icon, confirmation number, booking summary (service, date/time, location, total), scannable QR code ticket, action buttons (calendar, receipt, directions), and email confirmation note.

PRO

booking3

Appointment List

Upcoming and past appointments list grouped by date, with service name, provider avatar, time, duration, status badges (confirmed/pending/cancelled), and reschedule/cancel action buttons.

PRO

booking7

Hotel Room Booking

Hotel room selection with room type cards showing images, amenity icons, guest capacity, price per night, check-in/check-out date picker, guest counter, and total price summary.

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

booking23

Cancellation Flow

Booking cancellation screen with the original booking summary, cancellation reason picker (radio list with optional notes), live refund breakdown based on cancellation policy, confirmation checkbox, and destructive cancel action.

PRO

booking25

Booking Confirmed

A confirmation screen with an accent eyebrow, a two-column hairline table of the appointment details, a checked list of what to bring, and a sticky tile floating the live phone view the member also receives.