Skip to main content

Iframe Quickstart

This is the fastest way to try the Developer API without building a backend first.

In iframe mode, your app talks to the Chastify parent via postMessage, and Chastify calls server APIs on your behalf.

What Chastify Passes To Your Iframe

On open, Chastify puts a JSON payload in location.hash.
Important fields:

  • bridge.nonce: request signing value for bridge messages.
  • bridge.parentOrigin: required target origin for postMessage.
  • sessionId: stable extension session id for this lock.
  • lockId: active lock id.
  • ui: theme values from parent page.
  • Optional: homeActionSlug, homeAction, intent, regularActionsSummary, mainToken.

Use helpers from apps/extension/src/lib/ChastifyBridge.ts:

  • parseHashPayload()
  • ChastifyBridgeClient
  • startAutoResizeToParent(...)
  • themeVars(...)

Minimal React bootstrap

import { useEffect, useMemo, useState } from "react";
import {
parseHashPayload,
ChastifyBridgeClient,
startAutoResizeToParent,
themeVars,
} from "../lib/ChastifyBridge";

const payload = parseHashPayload();
if (!payload?.bridge?.nonce || !payload?.bridge?.parentOrigin) {
throw new Error("Missing bridge payload in iframe hash");
}

export function App() {
const [client, setClient] = useState<ChastifyBridgeClient | null>(null);
const [session, setSession] = useState<any>(null);
const [stateDoc, setStateDoc] = useState<any>(null);
const vars = useMemo(() => themeVars(payload.ui ?? null), []);

useEffect(() => {
const c = new ChastifyBridgeClient({
nonce: payload.bridge!.nonce,
targetOrigin: payload.bridge!.parentOrigin,
});
setClient(c);
return () => c.destroy();
}, []);

useEffect(() => {
if (!client) return;
(async () => {
const s = await client.request("session.get", {});
setSession(s);

await client.request("state.put", { data: { hello: "world", counter: 1 } });
const st = await client.request("state.get", {});
setStateDoc(st);
})().catch(console.error);
}, [client]);

useEffect(() => {
return startAutoResizeToParent({
nonce: payload.bridge!.nonce,
targetOrigin: payload.bridge!.parentOrigin,
extraPx: 12,
});
}, []);

return (
<div style={{ background: vars.pageBg, color: vars.text, minHeight: "100%" }}>
<h2>Extension Quickstart</h2>
<pre>{JSON.stringify(session, null, 2)}</pre>
<pre>{JSON.stringify(stateDoc, null, 2)}</pre>
</div>
);
}

Raw postMessage Example (No Helper Class)

If you do not use ChastifyBridgeClient, send the full request envelope manually:

const req = {
type: "chastify:ext:req",
v: 1,
id: crypto.randomUUID(),
nonce,
action: "session.get",
payload: {},
};

window.parent.postMessage(req, parentOrigin);

Listen for matching responses:

window.addEventListener("message", (event) => {
if (event.origin !== parentOrigin) return;
const msg = event.data;
if (!msg || msg.type !== "chastify:ext:resp" || msg.v !== 1) return;
if (msg.id !== req.id) return;

if (msg.ok) console.log("Bridge success:", msg.data);
else console.error("Bridge error:", msg.error);
});

Test Flow You Should Run First

  1. session.get
  2. state.put with { data: { hello: "world" } }
  3. state.get
  4. state.patch with { data: { counter: 2 } }
  5. state.get again to verify merged state

This confirms bridge auth, state persistence, and request/response handling.

Theme Support (ui + themeVars)

Chastify passes a ui object in the iframe hash.
themeVars(ui) converts it into usable design tokens:

  • pageBg
  • text
  • panel
  • border
  • muted
  • inputBg

Use these values as your extension’s base styling so your iframe matches host theme.

Live theme updates

The parent can send chastify:ext:ui events while your iframe is open.
If you want live theme sync, listen for that event and update local theme state.

Auto Resize Support

Use startAutoResizeToParent(...) so the parent can resize the iframe to your content.

Why this matters:

  • Prevents scroll traps inside the iframe.
  • Keeps setup modals and extension pages sized correctly.
  • Works well for dynamic sections that expand/collapse.

Common mistakes

  • Wrong targetOrigin (must match bridge.parentOrigin).
  • Missing or stale nonce.
  • Not calling destroy() on unmount for ChastifyBridgeClient.
  • Sending unsupported actions without checking session.get capabilities first.

Reference Files

  • apps/extension/src/lib/ChastifyBridge.ts
  • apps/extension/src/pages/MainPage.tsx
  • apps/extension/src/pages/SetupPage.tsx