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's mount() API.</p>
<div ref={ref} />
</main>
);
} Notes
-
Load
widget.global.jsonce inapp/layout.tsx(a plain<script>tag ornext/scriptwithstrategy="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 withstrategy="afterInteractive", nomount()effect. Both patterns are correct; pick one.
Want it to match your site? See Theming the widget.