Embedding the Booking UI
Use data attributes and showUI() to embed the Resytech booking modal on any page.
The booking UI opens as a full-screen modal. You can trigger it with HTML data attributes (zero JavaScript) or programmatically with showUI().
Data Attributes (No JavaScript)
Add data-resytech-* attributes to any clickable element. The library automatically binds click handlers on DOM ready.
<button
data-resytech-activity-id="activity-uuid"
data-resytech-equipment-id="equipment-uuid">
Book Now
</button>Clicking this button opens the booking modal with the specified activity and equipment pre-selected.
Available Data Attributes
| Attribute | Type | Description |
|---|---|---|
data-resytech-activity-id | string | Activity UUID to pre-select |
data-resytech-equipment-id | string | Equipment UUID to pre-select. Quantity defaults to 1. |
data-resytech-equipment-quantity | number | Quantity for the equipment in data-resytech-equipment-id. Defaults to 1. |
data-resytech-addons | string (JSON) | JSON array of {addonUuid, quantity} to attach to the equipment in data-resytech-equipment-id. |
data-resytech-equipment | string (JSON) | JSON array of EquipmentSelection. Use this for multi-equipment selections. Overrides the three attributes above. |
data-resytech-category-id | string | Equipment category UUID to filter the activity's selection page to. Copy the ID with the copy button on Dashboard > Checkout Settings > Equipment Categories. Use with data-resytech-activity-id (and without equipment attributes — a concrete equipment selection wins). Great for "Book a Mosaic Lamp session" buttons that open one category of a multi-style activity. |
data-resytech-calendar-pricing | "false" | Set to "false" to hide the per-day starting prices inside the date calendar. Prices show by default. |
data-resytech-date | string | Date to pre-select (YYYY-MM-DD) |
data-resytech-duration | number | Duration in minutes |
data-resytech-time-start | string | Time slot start (ISO 8601 datetime) |
data-resytech-time-end | string | Time slot end (ISO 8601 datetime) |
data-resytech-price | number | Price for the time slot |
data-resytech-lock-schedule | "true" | When set to "true", hides the "Choose another date/time" link inside the modal so the customer can't swap the slot. With the modal's hardcoded SchedulingOnly nav mode, this makes the navbar empty (and self-hiding). |
data-resytech-presentation | "modal" or "drawer" | Presentation for this button, overriding the client-level setting. Defaults to the client setting, which itself defaults to "modal". See Presentation: modal or drawer. |
data-resytech-gift-card | string (JSON), "", or "true" | Opens the modal in gift card purchase mode instead of the activity flow. Accepts an empty value or "true" for an empty form, or a JSON object matching GiftCardSelection to pre-fill amount and purchaser/recipient details. When this attribute is present, the activity-related attributes are ignored. See Gift Card Mode below. |
At least one of data-resytech-activity-id, data-resytech-equipment-id, data-resytech-equipment, or data-resytech-gift-card must be present for the element to be auto-bound. The location is always taken from the client's locationId config.
EquipmentSelection (and AddonSelection)
Each equipment selection describes one equipment item in the cart along with its quantity and any add-ons:
interface EquipmentSelection {
equipmentUuid: string;
quantity: number;
addons?: AddonSelection[];
}
interface AddonSelection {
addonUuid: string;
quantity: number;
}Minimal Example
Pre-select only the activity. The customer chooses equipment, date, and time in the modal:
<button data-resytech-activity-id="kayak-uuid">
Book a Kayak
</button>Fully Pre-Filled Example
Skip straight to checkout with everything pre-selected:
<button
data-resytech-activity-id="kayak-uuid"
data-resytech-equipment-id="single-kayak-uuid"
data-resytech-date="2025-07-15"
data-resytech-duration="120"
data-resytech-time-start="2025-07-15T10:00:00"
data-resytech-time-end="2025-07-15T12:00:00"
data-resytech-price="89.99">
Book: Single Kayak - Jul 15, 10am-12pm ($89.99)
</button>Equipment with Quantity and Add-ons
Pre-select two single kayaks plus a dry-bag add-on:
<button
data-resytech-activity-id="kayak-uuid"
data-resytech-equipment-id="single-kayak-uuid"
data-resytech-equipment-quantity="2"
data-resytech-addons='[{"addonUuid":"dry-bag-uuid","quantity":2}]'>
Book Two Kayaks + Dry Bags
</button>Locking the Schedule
When the parent page has already chosen the slot, add data-resytech-lock-schedule="true" to prevent the customer from changing it inside the modal:
<button
data-resytech-activity-id="kayak-uuid"
data-resytech-equipment-id="single-kayak-uuid"
data-resytech-date="2025-07-15"
data-resytech-duration="120"
data-resytech-time-start="2025-07-15T10:00:00"
data-resytech-time-end="2025-07-15T12:00:00"
data-resytech-price="89.99"
data-resytech-lock-schedule="true">
Confirm: Single Kayak - Jul 15, 10am-12pm ($89.99)
</button>This hides the "Choose another date/time" crumb inside the modal. The customer can still close the modal (X button, ESC, or click-outside) and pick a different slot from your own UI.
Multiple Equipment Selections
For carts that include more than one piece of equipment, use the JSON data-resytech-equipment attribute. It accepts an array of EquipmentSelection and takes precedence over data-resytech-equipment-id / -quantity / -addons:
<button
data-resytech-activity-id="kayak-uuid"
data-resytech-equipment='[
{"equipmentUuid":"single-kayak-uuid","quantity":2},
{"equipmentUuid":"tandem-kayak-uuid","quantity":1,"addons":[
{"addonUuid":"dry-bag-uuid","quantity":2}
]}
]'>
Book the Group Package
</button>Programmatic Usage with showUI()
Call showUI() directly for dynamic scenarios:
const client = window.resytech; // or your own instance
client.showUI({
activity: 'kayak-uuid',
equipment: [
{ equipmentUuid: 'single-kayak-uuid', quantity: 1 }
],
date: '2025-07-15',
durationMins: 120,
timeSlotStart: '2025-07-15T10:00:00',
timeSlotEnd: '2025-07-15T12:00:00',
timeSlotPrice: 89.99
});equipment is an array of EquipmentSelection — each entry carries the equipment UUID, quantity, and optional add-ons (with their own quantities). For example:
client.showUI({
activity: 'kayak-uuid',
equipment: [
{
equipmentUuid: 'single-kayak-uuid',
quantity: 2,
addons: [
{ addonUuid: 'dry-bag-uuid', quantity: 2 }
]
},
{
equipmentUuid: 'tandem-kayak-uuid',
quantity: 1
}
]
});String shorthand
If you only need to pre-select one equipment with quantity 1 and no add-ons, you can pass the equipment UUID as a string:
client.showUI({
activity: 'kayak-uuid',
equipment: 'single-kayak-uuid' // shorthand for [{ equipmentUuid: 'single-kayak-uuid', quantity: 1 }]
});Locking the schedule
Pass lockSchedule: true to hide the "Choose another date/time" link inside the modal — same effect as the data-resytech-lock-schedule attribute, just from JS:
client.showUI({
activity: 'kayak-uuid',
equipment: [{ equipmentUuid: 'single-kayak-uuid', quantity: 1 }],
date: '2025-07-15',
durationMins: 120,
timeSlotStart: '2025-07-15T10:00:00',
timeSlotEnd: '2025-07-15T12:00:00',
timeSlotPrice: 89.99,
lockSchedule: true
});See CheckoutRequest for the full option reference.
All properties are optional. Calling showUI() with no arguments opens the full booking flow from the start.
// Open with no pre-fill - customer picks everything
client.showUI();
// Pre-select just the activity
client.showUI({ activity: 'kayak-uuid' });
// Pre-select activity and date
client.showUI({ activity: 'kayak-uuid', date: '2025-07-15' });Gift Card Mode
The same modal can open directly to the gift card purchase flow instead of the activity flow. Pass a giftCard property on showUI() (or set the data-resytech-gift-card attribute). The presence of giftCard is the discriminator — when it's set, any activity/equipment pre-fill is ignored.
GiftCardSelection
All fields are optional. Omit giftCard entirely to stay on the standard activity flow; pass giftCard: {} (or data-resytech-gift-card="") to open the gift card flow with no pre-fills.
| Property | Type | Description |
|---|---|---|
amount | number | Pre-fill the gift card amount. If it matches one of the operator's flat-amount options, that option is selected; otherwise the form switches to custom-amount mode (if enabled). |
purchaser.name | string | Pre-fill the purchaser's full name |
purchaser.email | string | Pre-fill the purchaser's email |
purchaser.phone | string | Pre-fill the purchaser's phone number |
recipient.name | string | Pre-fill the recipient's name |
recipient.email | string | Pre-fill the recipient's email. Required at submit time when the operator has "send to recipient" enabled. |
recipient.message | string | Pre-fill the custom message sent with the gift card |
Programmatic (showUI)
// Open the gift card form with no pre-fills
client.showUI({ giftCard: {} });
// Open with just an amount pre-selected
client.showUI({ giftCard: { amount: 50 } });
// Pre-fill the full form
client.showUI({
giftCard: {
amount: 100,
purchaser: {
name: 'Alex Morgan',
email: 'alex@example.com',
phone: '+1 555 0100'
},
recipient: {
name: 'Jamie Lee',
email: 'jamie@example.com',
message: 'Happy birthday — enjoy a day on the water!'
}
}
});Data Attribute
The simplest form: bare attribute opens the gift card form with no pre-fills.
<button data-resytech-gift-card>Buy a Gift Card</button>Pre-fill with a JSON object matching GiftCardSelection:
<button data-resytech-gift-card='{
"amount": 100,
"recipient": { "email": "jamie@example.com" }
}'>
Send Jamie a $100 Gift Card
</button>Listening for Gift Card Events
Gift card purchases emit their own events — checkout:complete does not fire (no booking is created):
client.on('giftcard:complete', ({ giftCardCode, amount }) => {
console.log(`Sold gift card ${giftCardCode} for $${amount}`);
});
client.on('giftcard:close', () => {
// The customer dismissed the success view
});See Checkout Events for the full event reference.
Activity Voucher Mode
An Activity Voucher is a prepaid entitlement to a specific experience (for example "one 2-hour kayak rental, any weekend this season"), sold from an offer the operator publishes at a slug and redeemed later against a normal booking. The modal can open straight to an offer's purchase page, or to the redeem page where a customer enters an emailed code and picks a date.
Pass a voucher property on showUI() (or set data-resytech-voucher). Presence of voucher is the discriminator: activity/equipment pre-fills are ignored. If both giftCard and voucher are set, gift card mode wins.
VoucherSelection
| Property | Type | Description |
|---|---|---|
slug | string | Offer slug. Opens that offer's purchase page. |
code | string | Voucher code (AV + 32 hex). Opens the redeem page with the code checked. Wins over slug when both are set. Pass "" to open the redeem page empty. A malformed code is ignored and the page opens empty. |
quantity | number | Pre-fill the number of vouchers on the purchase form (1 to the offer's per-order maximum). |
purchaser.name | string | Pre-fill the purchaser's full name |
purchaser.email | string | Pre-fill the purchaser's email |
recipient.name | string | Pre-fill the recipient's name |
recipient.email | string | Pre-fill the recipient's email. Setting it switches the form to "this is a gift". |
A voucher open needs a slug or a code; unlike gift cards there is no "no pre-fill" voucher landing page. An empty data-resytech-voucher="" is ignored entirely.
Programmatic (showUI)
// Sell an offer
client.showUI({ voucher: { slug: 'black-friday-kayak' } });
// Sell two, as a gift, with the form pre-filled
client.showUI({
voucher: {
slug: 'black-friday-kayak',
quantity: 2,
purchaser: { name: 'Alex Morgan', email: 'alex@example.com' },
recipient: { name: 'Jamie Lee', email: 'jamie@example.com' }
}
});
// Redeem: open the code entry page
client.showUI({ voucher: { code: '' } });
// Redeem: land with a code already checked (e.g. from your own email link)
client.showUI({ voucher: { code: 'AV0123456789ABCDEF0123456789ABCDEF' } });Data Attribute
A bare value is treated as the offer slug:
<button data-resytech-voucher="black-friday-kayak">Buy a Kayak Voucher</button>JSON for a code or pre-fills:
<button data-resytech-voucher='{"code":""}'>Redeem a Voucher</button>
<button data-resytech-voucher='{
"slug": "black-friday-kayak",
"quantity": 2,
"recipient": { "email": "jamie@example.com" }
}'>
Gift 2 Kayak Vouchers
</button>The per-button data-resytech-presentation="drawer" override works here too.
Listening for Voucher Events
A voucher purchase is not a booking, so it emits its own event instead of checkout:complete:
client.on('voucher:complete', ({ orderId, quantity, total }) => {
console.log(`Sold ${quantity} voucher(s) for $${total}`);
});A voucher redemption creates a booking and fires the normal checkout:complete. When the customer closes the success view, checkout:close fires as usual.
See Checkout Events for the full event reference. To sell or redeem vouchers with your own UI instead of the modal, see Activity Vouchers on the API.
Multiple Book Now Buttons
You can have as many booking buttons on a page as you need. Each one can pre-fill different options:
<script
src="https://js.resytech.com/latest/resytech.js"
data-resytech-location-id="YOUR_LOCATION_ID"
data-resytech-base-url="https://booking.yourdomain.com">
</script>
<h2>Kayak Rentals</h2>
<button data-resytech-activity-id="kayak-uuid" data-resytech-equipment-id="single-kayak-uuid">
Book Single Kayak
</button>
<button data-resytech-activity-id="kayak-uuid" data-resytech-equipment-id="tandem-kayak-uuid">
Book Tandem Kayak
</button>
<h2>Paddleboard Rentals</h2>
<button data-resytech-activity-id="paddleboard-uuid">
Book Paddleboard
</button>Each button opens the same modal but pre-selects different activities or equipment. Only one modal can be open at a time -- if the modal is already open, additional showUI() calls are ignored.
Real-World Examples
Activity Page with Book Now
<div class="activity-card">
<img src="/images/sunset-cruise.jpg" alt="Sunset Cruise">
<h2>Sunset Cruise</h2>
<p>Enjoy a 2-hour cruise along the coastline at golden hour.</p>
<p class="price">From $149 per person</p>
<button
class="btn-primary"
data-resytech-activity-id="sunset-cruise-uuid"
data-resytech-duration="120">
Book This Experience
</button>
</div>Pricing Table
<table>
<thead>
<tr>
<th>Duration</th>
<th>Price</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>1 Hour</td>
<td>$49</td>
<td>
<button
data-resytech-activity-id="jet-ski-uuid"
data-resytech-duration="60">
Book Now
</button>
</td>
</tr>
<tr>
<td>2 Hours</td>
<td>$89</td>
<td>
<button
data-resytech-activity-id="jet-ski-uuid"
data-resytech-duration="120">
Book Now
</button>
</td>
</tr>
<tr>
<td>Half Day</td>
<td>$159</td>
<td>
<button
data-resytech-activity-id="jet-ski-uuid"
data-resytech-duration="240">
Book Now
</button>
</td>
</tr>
</tbody>
</table>Calendar Integration
If you have your own calendar UI, open the booking modal when a user picks a date:
// Your calendar component calls this when a date is clicked
function onDateSelected(dateString) {
window.resytech.showUI({
activity: 'kayak-uuid',
date: dateString // e.g. '2025-07-15'
});
}Dynamic Button from API Data
Fetch activities from the API and generate buttons dynamically:
const api = new ResytechApi({
baseUrl: 'https://api.bookingui.com/v1',
cmsBaseUrl: 'https://cms.bookingui.com'
});
const init = await api.initialization.initialize({ identifier: 'my-session' });
init.activities.forEach(activity => {
const btn = document.createElement('button');
btn.textContent = `Book ${activity.name} - from $${activity.priceFloor ?? activity.startingAtPrice}`;
btn.setAttribute('data-resytech-activity-id', activity.uuid);
document.getElementById('activity-list').appendChild(btn);
});
// Re-bind data attributes after adding new elements
// (auto-binding only runs once on DOM ready, so for dynamic elements use showUI() instead)
document.querySelectorAll('#activity-list button').forEach(btn => {
btn.addEventListener('click', () => {
window.resytech.showUI({
activity: btn.getAttribute('data-resytech-activity-id')
});
});
});Important Notes
- Auto-binding runs once on
DOMContentLoaded. If you add elements to the DOM after page load, useshowUI()programmatically or attach your own click handlers. - One modal at a time. If the modal is already open,
showUI()does nothing and logs a debug message. - The
locationproperty is always set from the client'slocationIdconfig. You do not need to set it in data attributes orshowUI()options.
