Embed in Next.js

Same shape as the React guide, in App Router terms: the page is a "use client" component that calls window.BookingWidget.mount() from an effect. The widget itself is server-rendering-agnostic; it only ever runs in the browser, inside its own Shadow DOM.

This is the complete examples/next/app/page.tsx host from the repo, verbatim:

"use client";

import { useEffect, useRef } from "react";

// The Next host loads the IIFE bundle (layout) and mounts it from a client
// component — the widget (Preact, shadow DOM) is independent of Next/React. The
// `window.BookingWidget` ambient type lives in the sibling booking-widget.d.ts (generated from
// the widget's config-contract.ts by `bun run examples:sync-widget` — see that script).

const API_BASE = process.env.NEXT_PUBLIC_API_BASE ?? "http://localhost:3010";
const PK = process.env.NEXT_PUBLIC_PK ?? "";

export default function Page() {
  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={{ maxWidth: 720, margin: "0 auto", padding: "40px 16px" }}>
      <h1>Reel Deal Sportfishing — Next host</h1>
      <p style={{ color: "#475569" }}>Mounted by a Next client component via the widget&apos;s mount() API.</p>
      <div ref={ref} />
    </main>
  );
}

Notes

  • Load widget.global.js once in app/layout.tsx (a plain <script> tag or next/script with strategy="beforeInteractive") so the global exists before the effect fires.
  • Config rides NEXT_PUBLIC_* env vars in the example; both values are public by design (the publishable key is a public credential).
  • Works under static export (output: "export"); the example repo builds exactly that, with no server dependency.
  • Prefer less code? The platform setup guide shows the auto-mount pattern instead — a [data-booking-widget] div plus the script loaded with strategy="afterInteractive", no mount() effect. Both patterns are correct; pick one.

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