Booking confirm screen

ProReact Native · Expo

The last step of a booking flow: a summary card, client details, notes and the cancellation policy, booked with a slide (or a button). The slider spins while the request runs, then the screen gives way to a success check with confetti, Add to calendar and Done.

View as Markdown
Try it on your phone
Scan to open this demo in your phone's browser and use it with your fingers.

Installation

$ npx shadcn@latest add @goodui-native-pro/booking-confirm-screen

Then run npx expo install --fix so react-native-reanimated match your Expo SDK. Files land in components/goodui-native/booking-confirm-screen.

Pro screens install from the license-gated @goodui-native-pro registry. Setup: Pro access.

Usage

import { router, Stack } from "expo-router";
import { BookingConfirmScreen } from "@/components/goodui-native/booking-confirm-screen/booking-confirm-screen";
import { api } from "@/lib/api";
import { addToCalendar } from "@/lib/calendar"; // for example with expo-calendar
import { useBookingDraft, useSession } from "@/lib/booking";

export default function ConfirmRoute() {
  const draft = useBookingDraft();
  const { user } = useSession();
  return (
    <>
      <Stack.Screen options={{ headerShown: false }} />
      <BookingConfirmScreen
        business={draft.business}
        items={draft.items}
        staff={draft.staff}
        start={draft.start}
        client={{ name: user?.name, email: user?.email }}
        onBack={() => router.back()}
        onEdit={() => router.back()}
        onConfirm={(details) => api.book({ ...draft.toRequest(), client: details })} // resolves with { reference }
        onAddToCalendar={(appointment) => addToCalendar(appointment)}
        onDone={() => router.dismissAll()}
      />
    </>
  );
}

When to use

Use it for
  • Reviewing and confirming an appointment before it is booked, with the client's contact details.
  • Bookings with a cancellation or no-show policy the client must accept.
  • Flows that end on a clear booked moment with Add to calendar.
Reach for something else
  • Paying for physical goods: use checkout-screen.
  • Editing an existing appointment: use booking-details-screen with reschedule.
  • A single yes or no question: use alert-dialog.

Examples

Sample visit, no props

<BookingConfirmScreen />

Button instead of a slide, no policy checkbox

<BookingConfirmScreen confirmMode="button" requirePolicy={false} onConfirm={book} onDone={close} />

API reference

PropTypeDefault
business
The business; its name, address, currency and cancellation policy show on the screen.
BookingBusinesssampleBusiness
items
What is being booked, services then add-ons. Duration and total are their sums.
BookingLineItem[]—
staff
Who it is with. `null` is "Any available".
BookingStaff | nullsampleStaff[0]
start
Start of the appointment.
Date—
client
Prefill for the client fields, for example from the signed-in account.
Partial<BookingClient>—
onConfirm
Books it. Runs only once the fields are valid and the policy is accepted. Return a promise: while it is pending the slider spins; resolve (optionally with `{ reference }`) to show the success screen, reject with an `Error` whose message is person-facing ("That time was just taken") to shake the slider and show it.
(details: BookingConfirmDetails) => unknown—
confirmMode
`swipe` books with a slide (deliberate, no accidental taps); `button` with a primary button. Default `swipe`.
"swipe" | "button""swipe"
requirePolicy
Require ticking the cancellation policy before booking. Default `true`.
booleantrue
onAddToCalendar
"Add to calendar" on the success screen, with the booked appointment. Return a promise to show pending and done.
(appointment: Appointment) => unknown—
onDone
"Done" on the success screen. Navigate away here.
(appointment: Appointment) => void—
onEdit
Shows "Change" on the summary card, for going back to the time step.
() => void—
onBack
Shows a back button in the top bar. Hidden on the success screen.
() => void—
paymentNote
How payment works, under the total. Default "Pay at the salon after your visit. No card needed." Same as `labels.paymentNote`.
string—
step
Step of the booking flow in the top bar. `null` hides it. Default step 4 of 4.
BookingStep | null{ current: 4, total: 4 }
title
Headline. Default "Review and confirm". Same as `labels.title`.
string—
locale
BCP 47 locale for dates, times and prices. Defaults to the device locale.
string—
hour12
Force a 12 or 24 hour clock. Defaults to the locale's.
boolean—
labels
Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide.
Partial<BookingConfirmScreenLabels>—
style
Style for the outer container.
StyleProp<ViewStyle>—
testID
Identifier for end-to-end tests.
string—

Motion and haptics

  • The slide follows the finger 1:1 and spins while onConfirm runs; a rejection shakes it with the message and it springs back.
  • On success the slider shows its check, then the screen crossfades to a success check that springs in, draws its mark and bursts confetti (the one bouncy moment of the flow).
  • Reduced motion: the slider and the check follow their own reduced-motion rules (the check fades in complete, confetti shows a static sparkle), the screens crossfade.
  • Haptic: select at the slider's point of no return, confirm or error when the booking settles (SwipeToConfirm)
  • Haptic: celebrate as the success check lands with its confetti (SuccessCheck)
  • Haptic: error when a field error appears (TextField)
  • Validation is a pure function of the field values; nothing re-renders per keystroke beyond the field itself and its error line.
  • On Android the keyboard height pads the scroll view; on iOS the scroll view adjusts its own inset. The bottom bar steps aside while the keyboard is up.

Accessibility

  • Fields are TextFields with labels, autocomplete hints and errors that are announced; Next on the keyboard moves through name, email, phone and notes.
  • Errors show only after the first attempt and then follow the typing. A failed attempt scrolls the form into view.
  • The policy is a checkbox that turns red with a written reason when it is required and unticked.
  • The slider is the kit's SwipeToConfirm: a button for screen readers (activating it confirms) with a hint naming the day and time; confirmMode='button' gives a plain button.
  • The success screen's check announces its title; the reference and the confirmation email are text.

Notes for AI

  • onConfirm(details) runs only when name and email are valid and the policy is ticked. Resolve with { reference } to show it; reject with an Error whose message is person-facing to show it on the slider.
  • items are BookingLineItem[] (use lineItemsFor(cart, services, addOns) from booking-types). start is a Date.
  • Add to calendar shows only when onAddToCalendar is passed; it receives the booked Appointment.
  • Prefill client from the signed-in account; phone is optional.
  • Hide the navigator header (headerShown: false).