MessagePort channel between your iframe and the Fanvue page hosting it, and it exposes only the capabilities Fanvue has granted your app.
React apps use the hooks in @fanvue/builder-sdk/react; other surfaces call connectFanvueBridge from @fanvue/builder-sdk/bridge. You need a surface running inside Fanvue, set up as in Run your app inside Fanvue.
Capabilities and grants
Fanvue grants capabilities per app; contact Fanvue to request them. Until a capability is granted:isEnabledandisDialogAvailablestayfalsetrackdoes nothingopenDialogresolves{ status: "unavailable" }
The iframe sandbox blocks
window.alert, window.confirm and window.prompt, so dialog is how you ask the creator a blocking question in Fanvue’s own chrome.
Send analytics events
useFanvueAnalytics() returns { track, isEnabled }. track(eventName, properties?, options?) is fire-and-forget, so it never throws and never rejects. In development, a payload the host would reject logs a warning naming the event. In production it’s dropped silently.
embedded_app_, so the example lands as embedded_app_checkout_opened. That prefix means an app can never emit a core platform event.
The SDK validates the payload before sending, and the host validates it again. A local failure returns
invalid_payload.
Open a dialog
useDialog(options?) returns { openDialog, isDialogAvailable }. openDialog(payload) resolves a DialogOutcome and never rejects. Callbacks in options fire only while the component is mounted.
confirm dialog asks a yes or no question. A form dialog collects 1 to 10 fields and returns their values.
Every field carries
key (/^[a-z0-9_]{1,64}$/), label (1 to 80 characters), optional required and optional helperText (200 characters). The host renders all copy as plain text.
One dialog is open at a time. A second
openDialog while one is pending is refused with busy.
Errors
BridgeRequestError carries a code and a message. The host returns five codes, and the SDK adds two of its own.
Use the bridge without React
connectFanvueBridge({ timeoutMs? }) resolves a Result<FanvueBridge, BridgeConnectError>. A connected bridge exposes capabilities, has(capability), analytics.track and dialog.open, each returning a Result. Calling a capability without checking has is safe and returns capability_denied.
connectFanvueBridge again after the iframe reloads. The host closes the previous port and completes a new handshake.
Handshake
The SDK runs this handshake for you, and the details matter when a connection fails. Your frame postsfanvue:bridge:ready to window.parent with { v: 1, caps }, where caps names the capabilities this SDK build understands. The host verifies the message came from the mounted iframe at your registered surface URL’s origin, then answers fanvue:bridge:hello with { capabilities } and transfers a MessagePort. All later traffic runs over the port.
BridgeConnectError has two codes:
NOT_EMBEDDEDmeans the page is not in an iframe.TIMEOUTmeans nohelloarrived. Either something other than Fanvue is framing the page, or the host refused the handshake because the frame’s origin doesn’t match the registered surface URL.
allow-same-origin.
The React hooks share one connection per page. A TIMEOUT is retried on the next call, while a connected bridge and NOT_EMBEDDED are cached until reload.