Frameworks
Microsoft Bookings widget for React
@bookingsxp/react wraps the BookingsXP embed in a typed component. It renders the <bookingsxp-widget> custom element, loads the script once, and turns widget events into React callbacks, so you can react to a confirmed booking without touching window listeners.
- Plan
- Plan: FreeWorks on every plan, including Free ($0, no card).
- In short
- @bookingsxp/react: a <BookingsXPWidget> component with onEvent and onBooked, plus a useBookingsXP hook.
- Microsoft Bookings
- Stays your system of record. Bookings land in Outlook and Teams and Microsoft sends the invites and reminders.
How it works
What happens with React
- The component renders <bookingsxp-widget> on the server and the client, and injects the loader in an effect.
- The loader draws the iframe inside the element's shadow root, so React's hydration never sees foreign children and does not warn.
- Props are the embed options in camelCase: widget, bookingUrl, mode, buttonText, template, theme, accent, radius, service, staff, locale, prefill, redirectUrl, minHeight, lazy, hideHeader and baseUrl, plus className, style and id.
- onEvent receives every WidgetEvent; onBooked receives booking_completed. The loader's dataLayer and pixel calls still run, so GTM keeps working.
Setup
Set up React
4 steps. Names of menus and buttons are as they appear today; vendors rename things, so look for the closest match.
Step 1: Install
Add the package to your app.
Shell npm i @bookingsxp/react # npm release pending — until then use the script embedStep 2: Render the widget
Use a saved widget ID, or bookingUrl for link-only mode.
TSX import { BookingsXPWidget } from "@bookingsxp/react"; export function Booking() { return ( <BookingsXPWidget widget="w_8fk2m1qz" theme="auto" onBooked={(e) => console.log("booked", e.booking?.id, e.slot?.start)} /> ); }Step 3: Open a popup from your own button
useBookingsXP loads the script and returns { api, ready, lastEvent, open }.
TSX import { useBookingsXP } from "@bookingsxp/react"; export function BookButton() { const { ready, open } = useBookingsXP({ event: "booking_completed", onEvent: (e) => console.log("booked", e.booking?.id), }); return ( <button type="button" disabled={!ready} onClick={() => open({ widget: "w_8fk2m1qz" })}> Book a call </button> ); }Step 4: Prefill known details
Pass prefill for signed-in users. Prefill is sent to the widget over postMessage, never in the iframe URL.
TSX <BookingsXPWidget widget="w_8fk2m1qz" prefill={{ name: user.name, email: user.email }} />
Outcomes and caveats
What you get
The result
- A typed component and hook; types for WidgetEvent, EmbedOptions and more are re-exported.
- No hydration warnings in SSR frameworks.
- Booking callbacks in React, alongside the dataLayer and pixel events for your tags.
Good to know
Limits and caveats
- The component is a client component ("use client"). In server-rendered pages it still renders the element on the server; the script loads on the client.
- Changing a prop after mount rebuilds the widget with the new options, except name, email, phone and notes, which update the form in place.
- Premium templates need a saved Pro widget; in link-only mode they fall back to classic.
FAQ
React questions
Something not covered? Email hello@bookingsxp.com.
Start free, no card
One widget, three templates, GTM and GA4 events and attribution in every booking. No card, no time limit.