Skip to main content
This example demonstrates bidirectional messaging: sending host-emitted events to the iframe, handling incoming requests with capability handlers, and observing all bridge traffic via a delegate.

What this example demonstrates

  • Sending events from host to iframe via host.sendEvent
  • Registering capture handlers via useBridgeCapability
  • Registering custom capabilities at runtime via host.registerCustomCapability
  • Responding with success / error / cancelled / unsupported statuses
  • Observing all bridge traffic via BridgeHostDelegate
  • Logging the full message stream in a debug panel

Complete source

The single-file React app below wires up event listeners, capability handlers, a runtime-registered custom capability, and a debug log panel showing every message crossing the bridge.

Code explanation

The sections below cover what each part of the app does, in roughly the order they’re exercised at runtime.

Sending events (host → iframe)

sendEvent posts a message to the iframe with type: "event" and a fresh correlation ID in the format web-event-{uuid}. Events are fire-and-forget — the iframe-side SDK delivers them to journey listeners. The “Send Ping” button shows how to send an event in response to a user action. Use BridgeActions constants for known event names — see the bridge event catalogue for the standard set.

Handling capture requests (iframe → host)

useBridgeCapability registers a handler for the camera.document.capture action. The hook wraps the inline { handle: ... } object in a stable proxy, so renders don’t cause re-registration. On unmount it cleans up only if its own proxy is still active. Pair the registration with a declaration in the provider’s capabilities prop so the journey’s pre-flight capability.query reports the capability as supported.

Custom capabilities at runtime

registerCustomCapability is the one-call alternative to the two-step declare-then-register pattern. It both declares the capability (appears in capability.query responses) and registers the handler. Use this for capabilities added dynamically after mount. The capability map is read live on every query, so capabilities registered this way show up immediately.

Unhandled requests (delegate fallback)

When the journey sends a request for an action no handler is registered for, the SDK pushes it into host.pendingRequests and fires delegate.onUnhandledRequest. The example responds immediately with unsupported so the journey isn’t left waiting. Without this fallback, pending requests accumulate up to the buffer cap (50) and then start being dropped. Always have a strategy for unhandled actions — either respond, or surface them in your UI so the user (or you, during dev) can decide.

Delegate-based observability

The delegate exposes four hooks for full message-flow visibility. Set it via the provider’s delegate prop (read from the latest render on each fire — no snapshot, unlike capabilities). The example pipes everything into a state-backed log panel so you can watch messages live during development.

Why a ref-less delegate works

The provider reads the delegate from the latest render on every dispatch — it does NOT snapshot it. That means inline delegate objects work without re-mount: changing the delegate prop changes which handlers run, no rebuild needed. Just be aware that recreating the delegate object on every render still triggers a re-render in the provider’s children (because the prop reference changed); wrap in useMemo if that matters for your app.

Running this example

  1. Install @gbg/go-bridge-web and @gbg/go-bridge-web-react.
  2. Replace your App.tsx with the code above.
  3. Update JOURNEY_URL to your journey endpoint.
  4. Run your dev server (Vite, Next.js, etc.).
  5. Open the page — the log panel at the bottom shows messages flowing in real time. Click “Send Ping” to fire an outbound event.

Next steps