Embed
Embed attributes
Every attribute of the bookingsxp-widget element and its data- form, with defaults, prefill fields and the placeholders for redirect-url.
Every option is an attribute on <bookingsxp-widget>. All are optional except one of widget or booking-url. Attributes set on the page override the widget's saved settings for that one embed; they never change the saved widget.
All attributes#
| Attribute | Values | Default | Notes |
|---|---|---|---|
widget | Widget ID, w_… | none | Saved widget from your dashboard. Wins if booking-url is also set. |
booking-url | Public Microsoft Bookings URL | none | Link-only mode, no account. Must be http(s). |
mode | inline | popup | floating | inline | Popup and floating render a button that opens a dialog. |
button-text | Text | Book a time | Label of the popup or floating button. |
template | classic | compact | minimal | split | week | stepper | cards | Saved template | Pro templates fall back to classic in link-only mode and on the Free plan. |
theme | light | dark | auto | Saved theme | auto follows the visitor's system setting. |
accent | Colour, for example #4f46e5 | Saved accent | Colours the widget and the popup or floating button. Use a hex value: the widget accepts #rgb or #rrggbb. Button default is #111827. |
radius | Number of pixels, 0 to 28 | Saved radius | Corner radius. Applied on Pro and Business; ignored on Free and link-only. |
service | Service ID or service code | none | Preselects a service and skips the service step. The code is the part after /s/ in a Bookings service link. |
staff | Staff member ID | none | Preselects a staff member. Ignored if the ID is not on the page. |
locale | BCP-47 tag, for example de-DE | Browser language | Date, time and interface language. |
name | Text | none | Prefills the customer's name. |
email | Text | none | Prefills the email address. |
phone | Text | none | Prefills the phone number. |
notes | Text | none | Prefills the notes field. |
redirect-url | URL | Saved redirect | After a booking, the page navigates here. See placeholders below. |
min-height | Number of pixels | 480 | Height of the inline iframe before the widget reports its real height. |
lazy | true | false | true | Inline widgets load when they come near the viewport. Set false to load immediately. |
hide-branding-header | Boolean | false | Hides the business name and logo header at the top of the widget. |
base-url | URL | Origin of the script | Where the widget is served from. Only for self-hosting or local development, for example http://localhost:3000. Never sent to the widget. |
Boolean attributes are on when present, so hide-branding-header and hide-branding-header="true" are the same. false, 0, no and off turn them off.
The seven templates are Classic (classic), Compact (compact) and Next available (minimal), which are free, and Split (split), Week view (week), Guided (stepper) and Service cards (cards) on Pro and Business. See Pricing.
Examples#
A dark, compact widget that opens straight on one service:
<bookingsxp-widget
widget="w_8fk2m1qz"
template="compact"
theme="dark"
service="initial-consultation"
></bookingsxp-widget>A popup button in German that loads immediately:
<bookingsxp-widget
widget="w_8fk2m1qz"
mode="popup"
button-text="Termin buchen"
locale="de-DE"
accent="#0f766e"
></bookingsxp-widget>Changing an attribute after the page loads rebuilds the widget with the new value. Changing name, email, phone or notes updates the form in place instead.
Prefill#
If you already know who the visitor is, for example on a logged-in page or after a lead form, prefill their details so they only pick a time.
<bookingsxp-widget
widget="w_8fk2m1qz"
name="Alex Morgan"
email="alex@example.com"
></bookingsxp-widget>Prefill values are personal data, so the loader never puts them in the iframe URL. It sends them to the widget in a postMessage that only the BookingsXP origin can read. Visitors can still edit prefilled fields, and a prefill sent after the widget has loaded only fills fields the visitor has left empty.
To prefill after the widget has loaded, set the attribute, or call prefill() on the handle returned by the JavaScript API:
const el = document.querySelector("bookingsxp-widget");
el.setAttribute("email", form.email.value);For a hand-written iframe without the script, name, email, phone and notes are also accepted as URL parameters. Avoid this where you can: URLs end up in logs and browser history.
Redirect after booking#
redirect-url sends the visitor to your own thank-you page once Microsoft confirms the booking. It is the simplest way to count conversions in any tool that can fire on a page view.
<bookingsxp-widget
widget="w_8fk2m1qz"
redirect-url="/thank-you?ref={booking_id}&service={service}&start={start}"
></bookingsxp-widget>| Placeholder | Replaced with | Example |
|---|---|---|
{booking_id} | The BookingsXP reference | BXP-7K3M9Q |
{service} | The service name | Initial%20consultation |
{start} | Start time, ISO 8601 in UTC | 2026-10-02T14%3A00%3A00.000Z |
Rules:
- Values are URL-encoded before they are inserted.
- Relative URLs resolve against the page the widget is on.
- Only
http:andhttps:URLs are followed. - The loader waits 600 ms before navigating, so pixels and
dataLayertags have time to send. - An attribute on the page beats the redirect URL saved in the dashboard.
The data- form#
Page builders and CMSes that strip unknown tags usually keep a div. Prefix each attribute with data- and add data-bookingsxp:
<div
data-bookingsxp
data-widget="w_8fk2m1qz"
data-mode="popup"
data-button-text="Book a call"
data-accent="#4f46e5"
data-redirect-url="/thank-you?ref={booking_id}"
></div>The loader finds these on page load and whenever new ones are added to the page. In a single-page app you can also call BookingsXP.scan() after rendering. Unlike the custom element, the div form does not rebuild when you change its attributes later; remove it and add a new one, or use BookingsXP.render().
How attributes reach the widget#
The loader turns the attributes into an iframe URL such as:
https://bookingsxp.com/embed/w_8fk2m1qz?template=compact&theme=dark&service=initial-consultation&instance=…&parent=https%3A%2F%2Fwww.example.com&v=1Only display options go in the URL (template, theme, accent, radius, service, staff, locale, hide_header=1, and mode when it is not inline). Prefill and attribution are sent by postMessage after the widget starts, and only to the BookingsXP origin. Link-only widgets use https://bookingsxp.com/embed/link?url=<encoded booking URL>&….
The custom element draws its iframe into a shadow root, so React, Vue and Svelte hydration never sees children it did not render. Popup dialogs and floating buttons are appended to body.
Edit or question? hello@bookingsxp.com