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.
  1. Step 1: Install

    Add the package to your app.

    Shell
    npm i @bookingsxp/react   # npm release pending — until then use the script embed
  2. Step 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)}
        />
      );
    }
  3. 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>
      );
    }
  4. 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.