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, sowindow.BookingWidgetexists when the effect runs. -
The example reads
VITE_API_BASE/VITE_PKfrom 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.