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#

AttributeValuesDefaultNotes
widgetWidget ID, w_…noneSaved widget from your dashboard. Wins if booking-url is also set.
booking-urlPublic Microsoft Bookings URLnoneLink-only mode, no account. Must be http(s).
modeinline | popup | floatinginlinePopup and floating render a button that opens a dialog.
button-textTextBook a timeLabel of the popup or floating button.
templateclassic | compact | minimal | split | week | stepper | cardsSaved templatePro templates fall back to classic in link-only mode and on the Free plan.
themelight | dark | autoSaved themeauto follows the visitor's system setting.
accentColour, for example #4f46e5Saved accentColours the widget and the popup or floating button. Use a hex value: the widget accepts #rgb or #rrggbb. Button default is #111827.
radiusNumber of pixels, 0 to 28Saved radiusCorner radius. Applied on Pro and Business; ignored on Free and link-only.
serviceService ID or service codenonePreselects a service and skips the service step. The code is the part after /s/ in a Bookings service link.
staffStaff member IDnonePreselects a staff member. Ignored if the ID is not on the page.
localeBCP-47 tag, for example de-DEBrowser languageDate, time and interface language.
nameTextnonePrefills the customer's name.
emailTextnonePrefills the email address.
phoneTextnonePrefills the phone number.
notesTextnonePrefills the notes field.
redirect-urlURLSaved redirectAfter a booking, the page navigates here. See placeholders below.
min-heightNumber of pixels480Height of the inline iframe before the widget reports its real height.
lazytrue | falsetrueInline widgets load when they come near the viewport. Set false to load immediately.
hide-branding-headerBooleanfalseHides the business name and logo header at the top of the widget.
base-urlURLOrigin of the scriptWhere 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:

HTML
<bookingsxp-widget
  widget="w_8fk2m1qz"
  template="compact"
  theme="dark"
  service="initial-consultation"
></bookingsxp-widget>

A popup button in German that loads immediately:

HTML
<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.

HTML
<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:

JavaScript
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.

HTML
<bookingsxp-widget
  widget="w_8fk2m1qz"
  redirect-url="/thank-you?ref={booking_id}&service={service}&start={start}"
></bookingsxp-widget>
PlaceholderReplaced withExample
{booking_id}The BookingsXP referenceBXP-7K3M9Q
{service}The service nameInitial%20consultation
{start}Start time, ISO 8601 in UTC2026-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: and https: URLs are followed.
  • The loader waits 600 ms before navigating, so pixels and dataLayer tags 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:

HTML
<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:

Text
https://bookingsxp.com/embed/w_8fk2m1qz?template=compact&theme=dark&service=initial-consultation&instance=…&parent=https%3A%2F%2Fwww.example.com&v=1

Only 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