Events & Booking
Booking Calendar
Full booking flow in one component - month picker, time-slot selector, optional Stripe payment, and booking creation. Reads Opening Hours for availability, supports date overrides and blocked dates, and navigates to Booking List after confirmation.
Backend availability wins when it returns data. Falls back through date overrides, Opening Hours schedule, then legacy props.
Paid services go through PaymentSheet or browser checkout. Free services skip payment entirely.
Available on all plans.
Four things worth knowing
Slot resolution has a strict priority order
For any selected day, slots come from: (1) backend useAvailableSlots when non-empty, (2) dateOverrides for that date, (3) generated slots from the Opening Hours schedule for that weekday, (4) legacy businessHours / openingTime / closingTime props. Blocked and special closed dates always force no slots regardless of priority.
Opening Hours is the source of truth for availability
The component hydrates Opening Hours from _prefetchedOpeningHours in builder preview, or from the Opening Hours API endpoint at runtime. If Opening Hours data is unavailable, legacy props are used as fallback. Keep your Opening Hours component published and accurate - this calendar reads the same schedule.
Booking window is controlled by monthsToShow
monthsToShow (1–12) sets how far forward users can book. A “Booking window: X month(s)” indicator is shown on the calendar. Users can browse up to 12 months forward but can only select dates within the window. A legacy daysToShow prop is automatically converted to months when monthsToShow is absent.
Payment blocks confirmation for paid services
When a service has a price > 0, the component attempts native Stripe PaymentSheet first, then browser checkout session, then PaymentIntent fallback. If none succeed, booking is blocked. Free services skip payment and set paymentStatus to free automatically - no configuration needed.
Find it, drop it, wire the screens
Where to find itAdd it to any booking screen
- Component picker → Events & Booking → Booking Calendar.
- Drop it on the screen where users select a date and time.
- Pair it with a Booking List screen as the post-confirmation destination.
Before you publishThree things to check
- Set bookingListScreenId so the user lands on their bookings after confirming.
- Set monthsToShow to match how far in advance your service accepts bookings.
- Publish your Opening Hours component first - slot availability reads from the same schedule.
Booking window, slots, and navigation
| Prop | Type | Default | Description |
|---|---|---|---|
monthsToShow | number | 1 | How many months forward users can book (1–12). |
timeSlots | array | - | Explicit time slot list. When set, overrides generated slots. |
businessHours | object | - | Legacy per-day hours map. Used when Opening Hours data is unavailable. |
blockedDates | string[] | [] | Dates that are always unavailable regardless of other slot sources. |
dateOverrides | object | - | Per-date slot overrides. Applied after backend, before Opening Hours fallback. |
slotDurationMinutes | number | - | Slot length used when generating slots from opening hours. |
bookingListScreenId | string | - | Screen to navigate to after successful booking confirmation. |
backScreenId | string | - | Screen to navigate to on back action. |
Make it yours
Every colour and visibility toggle below is yours to change. The defaults are a neutral starting point - adjust them to match the brand without touching anything else.
| Prop | Default | What it controls |
|---|---|---|
showProviderInfo | true | Provider name and avatar above the calendar. |
showPrice | true | Service price shown in the booking summary. |
showDuration | true | Service duration shown in the booking summary. |
showConfirmButton | true | Confirm / pay button visibility. |
dayActiveBackgroundColor | #007AFF | Background of the selected day in the calendar. |
dayInactiveBackgroundColor | #F3F4F6 | Background of unavailable / out-of-window days. |
slotAvailableBackgroundColor | #F0F9FF | Background of an available time slot. |
slotUnavailableBackgroundColor | #F3F4F6 | Background of an unavailable time slot. |
slotSelectedBackgroundColor | #007AFF | Background of the currently selected time slot. |
slotSelectedColor | #FFFFFF | Text colour of the selected time slot. |
confirmButtonColor | #007AFF | Confirm / pay button background. |
confirmButtonTextColor | #FFFFFF | Confirm / pay button label colour. |
borderRadius | 8 | Corner radius applied to calendar cells and slot chips. |
Common mistakes to avoid
No slots showing on a day
Check in order: is the day in blockedDates or specialClosedDates? Is Opening Hours marking that weekday as closed? Is the backend returning an empty array due to an endpoint or data issue? Are dateOverrides accidentally setting an empty slot list for that date?
Confirm button does nothing for paid services
Paid booking confirmation requires secureApiCall and a valid appId to be present in the player context, and Stripe endpoints must be configured server-side. If either is missing, the booking is intentionally blocked.
Selected slot disappears after refresh
This is expected behaviour. If the selected slot becomes unavailable after a background refresh, the stale selection is invalidated automatically. It is not a bug - it prevents double-booking.
Publish Opening Hours before testing slots
The calendar reads the Opening Hours schedule from the backend. If Opening Hours has never been published, or has unpublished changes, slot generation will fall back to legacy props or produce no slots. Publish Opening Hours first, then test the calendar.