Velo Tutorial: Creating a Custom Bookings Experience
Prerequisites
Bookings API
getServiceAvailability()
- Called to retrieve available slots for a given service. Before calling this function you must first retrieve the ID of the service to be booked.checkoutBooking()
- Called to book a service and to prompt the current site visitor to enter payment information if required. This function requires you to pass the specific service slot to be booked and values for any form fields that are needed when booking the service.
Bookings Lifecycle
- You get a list of services from the Bookings/Services collection and display them.
- A user selects one of the displayed services.
- You call the
getServiceAvailability()
function using the selected service's Service ID (_id
) value. You get that ID from the results of the query performed above. (Optionally, you can pass aServiceAvailabilityOptions
object to change the limits on the slots that are returned.) - You display the returned slots. You may also want to retrieve the staff member items that relate you the returned slots from the Bookings/Staff collection.
- You also display input elements to gather the service's form fields if there are any. You retrieve the list of form fields from results of the query performed above in the
fields
property. - A user enters values for the form fields and indicates that the booking should be checked out.
- You call the
checkoutBooking()
function. You pass the selected slot object, the values for the form fields, and the payment type if neccessary. Note, the specified payment type must match the service's configuration in your site's Dashboard. You cannot book a paid service as if it were free.- If the service is free, you do not need to pass a
paymentOptions
object. - If the service is not free and you pass a
paymentOptions
object indicating the payment should be online, a payment popup is presented for the user to enter payment information, such as credit card information. - If the service is not free and you pass a
paymentOptions
object indicating the payment should be offline, the payment popup is not presented to the user.
- If the service is free, you do not need to pass a
Bookings Example
Step 1: Retrieve and Display Services
Another option for retrieving the service list is:
- Query your site's Bookings/Services collection.
Other options for displaying the service list are:
- Table
- Gallery
- Dropdown
- Radio Buttons
- Checkboxes
Type | ID | Connected to field |
---|---|---|
Repeater | servicesRepeater | - |
Text | titleText | Service Name |
Image | serviceImage | Service Image |
Text | taglineText | Service Tagline |
Text | priceText | Price Summary |
Button | bookButton | - |

Step 2: User Selects Service
In our example, the user clicks a button in a repeater connected to dataset, so we can use the button's click event to get the ID of the selected service.
We add an onClick event event handler to the bookButton using the Properties & Events Panel.
1export function bookButton_click(event) {
2}
Step 3: Retrieve Available Slots
You retrieve the selected service's available slots by calling getServiceAvailability()
. The function requires that we pass it the ID of the selected service.
In our example, we've already gotten the ID of the selected service. So all we need to do is use it when calling getServiceAvailability()
.
1import wixBookings from 'wix-bookings';
- availableSlots
- slotOptions
- selectedSlot
- slotIndex
1let availableSlots;
2let slotOptions;
3let selectedSlot;
4let slotIndex;
5
6export function bookButton_click(event) {
7
8 wixBookings.getServiceAvailability(event.context.itemId)
9 .then( (availability) => {
10 availableSlots = availability.slots;
11 } );
12}
When calling getServiceAvailability()
, you can optionally pass an object that defines a datetime range that refines which slots will be returned.
Step 4: Display Available Slots
1export function bookButton_click(event) {
2
3 wixBookings.getServiceAvailability(event.context.itemId)
4 .then( (availability) => {
5 availableSlots = availability.slots;
6 //---Added in this step---//
7 slotOptions = availableSlots.map((slot) => {
8 let date = slot.startDateTime;
9 return {
10 "slotDate": date.toLocaleDateString() + " " + date.toLocaleTimeString(),
11 }
12 });
13 $w('#slotTable').rows = slotOptions;
14 //---End of added in this step---//
15 } );
16}
Step 5: Store the User's Selected Slot
1export function slotTable_rowSelect(event)
2 selectedSlot = event.rowData;
3 slotIndex = event.rowIndex;
4}
Step 6: Display Form Fields
1export function bookButton_click(event) {
2
3 wixBookings.getServiceAvailability(event.context.itemId)
4 .then((availability) => {
5 availableSlots = availability.slots;
6 slotOptions = availableSlots.map((slot) => {
7 let date = slot.startDateTime;
8 return {
9 "slotDate": date.toLocaleDateString() + " " + date.toLocaleTimeString(),
10 }
11 });
12 $w('#slotTable').rows = slotOptions;
13 });
14
15 //---Added in this step---//
16 let $item = $w.at(event.context);
17 let formFields = $item("#bookingsDataset").getCurrentItem().form.fields;
18
19 formFields = formFields.filter(field => field.constraints.required)
20
21 $w('#formFieldRepeater').data = formFields;
22 //---End of added in this step---//
23 } );
24}
1export function formFieldRepeater_itemReady($item, itemData, index) {
2 $item("#fieldInput").placeholder = itemData.label;
3}


Step 7: User Enters Form Field Values and Books Service
1export function checkoutButton_click(event) {
2 let formFieldValues = []; // list of values user entered
3
4 // for each item in the repeater
5 $w('#formFieldRepeater').forEachItem( ($item, itemData, index) => {
6 // add an object containing the corresponding form field's
7 // ID and the value that the user entered
8 formFieldValues.push( {
9 "_id": itemData._id,
10 "value": $item("#fieldInput").value
11 } );
12 } );
13}
Step 8: Booking Checkout
You perform a booking checkout by calling checkoutBooking()
. The function requires that we pass it the selected slot object and the form field values that the user entered. We package these together in an object named bookingInfo
. Remember, we've already collected the form field data in a variable named formFieldValues
.
When calling checkoutBooking()
, you can optionally pass an object that defines the payment options. In our example, we are using services which are paid for offline, so we will not be passing any payment options.
1export function checkoutButton_click(event) {
2 let formFieldValues = [];
3
4 $w('#formFieldRepeater').forEachItem( ($item, itemData, index) => {
5 formFieldValues.push( {
6 "_id": itemData._id,
7 "value": $item("#fieldInput").value
8 } );
9 } );
10
11 //---Added in this step---//
12 // build bookingInfo object
13 let bookingInfo = {
14 // selected slot object
15 "slot": availableSlots[slotIndex],
16 // form filed values collected above
17 "formFields": formFieldValues
18 };
19
20 // booking checkout
21 wixBookings.checkoutBooking(bookingInfo)
22 .then( (results) => {
23 $w("#confirmationText").text = `Booking ID: ${results.bookingId} Status: ${results.status}`;
24 } );
25 //---End of added in this step---//
26}
- If the service is free, the checkout is booked without any other input.
- If the service is not free and you pass a
paymentOptions
object indicating the payment should be online, a payment popup is presented for the user to enter payment information, such as credit card information. - If the service is not free and you pass a
paymentOptions
object indicating the payment should be offline, the payment popup is not presented to the user.