FAQ
Quick answers to the questions we hear most. If you don't see yours here, the Troubleshooting page covers diagnostic workflows, and Error Codes catalogs every error the SDK can throw.
Can I use this with React, Vue, Svelte, or Angular?
Yes. The SDK is framework-agnostic. It only needs a DOM element (or selector) and a URL — you supply the container, the SDK creates the iframe, and you get back a plain IframeBridge object you can use from anywhere.
// Inside any framework lifecycle
import { createIframeBridge } from 'iframe-helper-sdk';
const bridge = createIframeBridge({
container: '#partner-root',
src: 'https://partner.example/app',
});
In React, call createIframeBridge inside a useEffect and store the bridge in a useRef. In Vue, call it in onMounted. In Svelte, call it in onMount. The pattern is the same: create the bridge once the DOM is ready, clean it up when the component unmounts.
The SDK does not wrap its API in React components, Vue composables, or Angular services — it stays a plain TypeScript library so it works everywhere.
What if the iframe doesn't support the bridge protocol?
Nothing breaks. The iframe loads normally, and the bridge transitions to handshake_failed after bootstrap.handshakeTimeoutMs (default 10 seconds).
This is a normal integration outcome — not every iframe needs the bridge. The parent SDK detects the missing protocol by timeout, and you can handle it in your application:
try {
await bridge.whenReady();
// Bridge is active — communicate normally
} catch (error) {
if (error instanceof IframeBridgeError && error.code === 'HANDSHAKE_TIMEOUT') {
// Iframe doesn't support the bridge protocol — that's fine
console.log('Bridge not supported, iframe is loaded as a static embed');
}
}
The iframe stays mounted and visible. The bridge simply won't accept communication calls — request and sendEvent will fail with BRIDGE_DESTROYED if you let the handshake fail without a remount.
How do I embed multiple iframes?
Call createIframeBridge once per iframe. Each call returns an independent bridge instance — they don't share state, listeners, or queues.
const partnerBridge = createIframeBridge({
container: '#partner-frame',
src: 'https://partner.example/app',
});
const chatBridge = createIframeBridge({
container: '#chat-frame',
src: 'https://chat.example/widget',
});
await partnerBridge.whenReady();
await chatBridge.whenReady();
Each bridge gets a unique session ID, so messages are routed to the correct instance. A bridge:ready from the partner iframe won't trigger the chat bridge, and an event from the chat iframe won't leak into the partner bridge.
There is no per-page limit — create as many as your containers and CSP allow. See Configuration for the full options you can tune per iframe.
Can I pass authentication tokens?
You can include any data in request and sendEvent payloads:
await bridge.request('user:get', {
id: '123',
token: sessionToken,
});
However, we strongly recommend handling authentication server-side instead:
- If the iframe app is on your domain, a
SameSitecookie sent with the iframe request is the simplest approach. - If the iframe app is cross-domain, use a server-to-server token exchange or a dedicated auth endpoint inside the iframe.
- The bridge session ID is not an auth token — it's correlation metadata only (see below).
- Client-side JavaScript cannot protect a token from XSS in either the parent or the iframe.
Treat any token you send through the bridge as observable by the iframe document, by any scripts running in the iframe, and by anyone inspecting the parent's memory.
Does this work with sandboxed iframes?
Yes, with caveats. Sandbox tokens can change the iframe origin and break exact-origin message validation:
- Without
allow-same-origin, a sandboxed iframe sendsevent.origin === 'null'. The SDK rejects this becauseallowedOriginmust be an exact HTTPS origin. You cannot use the bridge with a sandbox that omitsallow-same-origin. - With
allow-scripts+allow-same-origin, the SDK emits aCONFIG_UNSAFE_SANDBOXdiagnostics warning indevelopmentsecurity profile. This combination removes most of the isolation sandboxing provides, but it's required if the iframe needs JavaScript and you want origin-based routing.
const bridge = createIframeBridge({
container: '#sandboxed-frame',
src: 'https://partner.example/app',
targetOrigin: 'https://partner.example',
allowedOrigin: 'https://partner.example',
securityProfile: 'development', // Allows the reviewed combination
sandbox: ['allow-scripts', 'allow-same-origin'],
diagnostics: {
logger: createDiagnosticRecorder({ maxEntries: 100 }).logger,
},
});
In strict security profile, the allow-scripts + allow-same-origin combination throws CONFIG_UNSAFE_SANDBOX. Only use development profile when this combination has been reviewed and documented for your integration.
See the Security page for the full security model and the Configuration page for sandbox details.
Is the session ID a security token?
No. The session ID is correlation and routing metadata only.
It identifies one bridge attempt so the parent can match inbound messages to the correct bridge instance. It is:
- Not authentication
- Not authorization
- Not a CSRF token
- Not a secret
- Not proof that the iframe is trusted
Every message is still validated against exact origin, source window, protocol name, and protocol version — the session ID alone does not grant access. If you set a custom bootstrap.session.paramValue, treat it like any other URL parameter: observable, not confidential.
Real authentication belongs server-side, in your application layer, outside this generic transport bridge.
What's the bundle size?
The current build produces:
| Format | Size | Gzipped |
|---|---|---|
| ESM | ~37 kB | ~9.5 kB |
| CJS | ~28 kB | ~8.3 kB |
The SDK has zero runtime dependencies — you aren't pulling in a framework, a schema library, or a utility belt. The gzipped ESM payload (the one modern bundlers tree-shake) is under 10 kB.
The package publishes both ESM and CJS, with TypeScript declarations. Your bundler picks the right one automatically through the exports map in package.json.
Does this work with SSR / Next.js?
The SDK is browser-only. It calls document.querySelector, creates HTMLIFrameElement, and listens for message events on window — none of these exist at build time or during server-side rendering.
In Next.js (Pages Router): Import and use the SDK inside a useEffect so it only runs on the client:
import { useEffect, useRef } from 'react';
import type { IframeBridge } from 'iframe-helper-sdk';
export default function PartnerPage() {
const bridgeRef = useRef<IframeBridge | null>(null);
useEffect(() => {
const { createIframeBridge } = require('iframe-helper-sdk');
bridgeRef.current = createIframeBridge({
container: '#partner-frame',
src: 'https://partner.example/app',
});
return () => bridgeRef.current?.destroy();
}, []);
return <div id="partner-frame" />;
}
In Next.js (App Router): Mark the component with 'use client' and follow the same pattern.
In Nuxt / SvelteKit / other SSR frameworks: Mount the bridge in a client-only lifecycle hook (onMounted, onMount, browser check, etc.). The principle is the same: delay createIframeBridge until document and window are available.
The import itself is side-effect-free — you can import type the types at the top of any file without triggering browser-only code.
Can I resize the iframe?
Yes. For parent-controlled sizing, the SDK exposes the owned iframe element (bridge.iframe), so you can resize it directly:
await bridge.whenReady();
bridge.iframe.style.width = '800px';
bridge.iframe.style.height = '600px';
For child-driven cross-domain sizing, register resizePlugin() in the parent options and have the iframe send the reserved iframe-bridge:resize event:
import { resizePlugin } from 'iframe-helper-sdk/resize';
plugins: [resizePlugin({ axis: 'height', maxHeightPx: 900 })];
The parent still validates the message through the normal bridge chain before applying dimensions. Use max bounds so the iframe cannot force unreasonable layout changes. Full setup: Resize Plugin.