Embed in React (Vite)

The widget bundle is framework-agnostic: React loads the IIFE script once, then calls window.BookingWidget.mount() on a node it owns. The widget renders inside a Shadow DOM independently of React — no shared dependencies, no version coupling. mount() returns an unmount function, which is exactly what useEffect wants as its cleanup.

This is the complete examples/react/src/main.tsx host from the repo, verbatim:

import { StrictMode, useEffect, useRef } from "react";
import { createRoot } from "react-dom/client";

// The widget bundle is framework-agnostic: the React host loads the IIFE and calls
// mount() on a React-rendered node. The widget renders (Preact, in a shadow root)
// independently of React — no host-framework coupling.
declare global {
  interface Window {
    BookingWidget?: {
      mount(el: HTMLElement, cfg: { apiBase: string; publishableKey: string; tripTypeId?: string }): () => void;
    };
  }
}

const API_BASE = (import.meta.env.VITE_API_BASE as string) ?? "http://localhost:3010";
const PK = (import.meta.env.VITE_PK as string) ?? "";

function App() {
  const ref = useRef<HTMLDivElement>(null);
  useEffect(() => {
    if (ref.current && window.BookingWidget) {
      return window.BookingWidget.mount(ref.current, { apiBase: API_BASE, publishableKey: PK });
    }
  }, []);
  return (
    <main style={{ fontFamily: "system-ui, sans-serif", maxWidth: 720, margin: "0 auto", padding: "40px 16px" }}>
      <h1>Reel Time Charters — React (Vite) host</h1>
      <p style={{ color: "#475569" }}>The booking widget below is mounted by React via the widget's mount() API.</p>
      <div ref={ref} />
    </main>
  );
}

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

Notes

  • Load the widget script once in index.html (e.g. <script src="https://api.example.com/widget.global.js"></script>) before the React bundle, so window.BookingWidget exists when the effect runs.
  • The example reads VITE_API_BASE / VITE_PK from env — swap in your own config plumbing. apiBase: "" means same-origin relative URLs.
  • Returning the mount() result from the effect unmounts cleanly on re-render and under StrictMode's double-invoke.

Want it to match your site? See Theming the widget.