react-frame-bridge
React provider and hooks. Manages bridge lifecycle — opens on mount, closes on unmount.
BridgeProvider
Creates and owns the bridge instance. The bridge is created once on mount — changes tochannelName, role, or enabledTransports after mount are ignored. Remount the provider to apply new configuration.
import { BridgeProvider, IframeBridgeHost, useBridge } from "@mhanzelka/react-frame-bridge";
type Messages = { type: "ping" | "pong"; value: number };
// Parent page
function Parent() {
return (
<BridgeProvider<Messages>
open={true}
channelName="my-channel"
role="parent"
targetOrigin="https://child.example.com"
enabledTransports={["post-message-channel"]}
>
<IframeBridgeHost
src="https://child.example.com/app"
targetOrigin="https://child.example.com"
style={{ width: "100%", height: 500 }}
/>
<Controls />
</BridgeProvider>
);
}
// Child iframe at child.example.com
function Child() {
return (
<BridgeProvider<Messages>
open={true}
channelName="my-channel"
role="child"
targetOrigin="https://parent.example.com"
enabledTransports={["post-message-channel"]}
>
<Handler />
</BridgeProvider>
);
}| Prop / Method | Type | Default | Description |
|---|---|---|---|
| open | boolean | — | Set to false to keep the bridge closed. |
| channelName | string | — | Must match on both sides. |
| role | "parent" | "child" | — | Role of this side. |
| enabledTransports | TransportType[] | ["post-message-channel"] | Which transports to use. |
| targetOrigin | string | — | Required for "post-message-channel". |
| prefix | string | — | Optional prefix for the bridge ID. |
| options.observer | (event) => void | — | Called for every message sent/received. |
| bridgeOptions | CreateBridgeOptions | — | Advanced options passed to createBridge. |
useBridge<T>()
Returns the Bridge instance from the nearest BridgeProvider. Throws if used outside a provider.
function Controls() {
const bridge = useBridge<Messages>();
const ping = async () => {
const reply = await bridge.send({ type: "ping", value: 1 });
console.log(reply); // { type: "pong", value: 2 }
};
return <button onClick={ping}>Ping</button>;
}Handling incoming messages
Use bridge.onMessage() inside a useEffect. The return value is the unsubscribe function.
function Handler() {
const bridge = useBridge<Messages>();
useEffect(() => {
return bridge.onMessage(async (msg) => {
if (msg.type === "ping")
return { type: "pong", value: msg.value + 1 };
});
}, [bridge]);
return null;
}useBridgeState()
Subscribes to bridge state changes via useSyncExternalStore. Re-renders on every state update.
function StatusBar() {
const state = useBridgeState();
return (
<div>
{state.state === "open" && <span>● Connected</span>}
{state.state === "closed" && <span>● Disconnected</span>}
<span>Pending: {state.pendingCount}</span>
</div>
);
}
// state shape:
// state.state → "open" | "closed" | "partially-open"
// state.pendingCount → number of unanswered requests
// state.transports → { [type]: { state, messageCount } }IframeBridgeHost
Drop-in <iframe> wrapper that automatically sets contentWindow as the postMessage target on the parent bridge.
| Prop / Method | Type | Default | Description |
|---|---|---|---|
| src | string | — | iframe URL. |
| targetOrigin | string | — | Origin to use for postMessage. |
| ...rest | IframeHTMLAttributes | — | All standard iframe attributes. |
useBridgeWindow<T>()
Opens a popup window with a bridge connection. Tears down the bridge when the popup closes.
import { useBridgeWindow } from "@mhanzelka/react-frame-bridge";
function OpenPopupButton() {
const { open } = useBridgeWindow<Messages>({
onMessage: async (msg) => ({ ...msg, handled: true }),
onBridgeReady: async (bridge) => {
await bridge.send({ type: "init" });
},
});
return (
<button onClick={() => open({ url: "/popup", name: "settings" })}>
Open Settings
</button>
);
}