Iframe Embed
The iframe integration lets you embed depositOS in any web application, regardless of framework. The widget runs in a separate hosted page and communicates with your parent page via the postMessage API.
This approach is ideal for non-React applications, environments where you cannot install npm packages, or when you need strong isolation between the widget and your app.
Integrator note: you do not need to provide API keys in frontend integration. The iframe host and backend handle provider credentials server-side.
Overview
The iframe integration has two parts:
- The iframe host — A standalone app that renders the depositOS widget. You host this on your own domain or subdomain (e.g.,
https://embed.deposit-os.com). - Your parent page — Loads the iframe and communicates with it via
postMessageto send configuration and receive events.
Hosting the Iframe App
The apps/iframe-host directory contains a ready-to-deploy Vite application. Build and host it on any static hosting provider:
cd apps/iframe-host
pnpm install
pnpm build
The build output in dist/ is a static site you can deploy to Vercel, Netlify, Cloudflare Pages, or any web server.
Iframe Host Environment Variables
Set these values on your iframe-host deployment:
| Variable | Required | Notes |
|---|---|---|
| VITE_API_BASE_URL | Recommended | depositOS API base URL used by the widget |
| VITE_DYNAMIC_ENVIRONMENT_ID | If wallet mode used | Dynamic environment ID for wallet connection |
| VITE_IFRAME_RENDER_TYPE | No | Default render type: inline, popup, or button |
| VITE_IFRAME_BUTTON_TEXT | No | Default launcher button text in button mode |
Parent Page Setup
On your page, create an iframe pointing to the hosted widget URL. You can pass initial configuration via URL parameters or via postMessage after the iframe loads.
Basic HTML Example
<div id="depositos-container" style="width: 420px; height: 600px;">
<iframe
id="depositos-iframe"
src="https://embed.deposit-os.com"
style="width: 100%; height: 100%; border: none;"
allow="clipboard-write"
></iframe>
</div>
<script>
const iframe = document.getElementById("depositos-iframe");
const config = {
destChain: 42161,
destToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
destAddress: "0xYourTreasuryAddress",
enableWallet: true,
enableLayerswap: true,
enablePrivateMode: true,
theme: "dark",
};
// Wait for the iframe to signal it's ready, then send config
window.addEventListener("message", function (event) {
const data = event.data;
if (!data || typeof data.type !== "string") return;
switch (data.type) {
case "depositos:ready":
// Iframe is loaded — send the configuration
iframe.contentWindow.postMessage(
{ type: "depositos:config", config: config },
"*"
);
break;
case "depositos:success":
console.log("Deposit successful!", data.txHash);
// Handle success — redirect, show confirmation, etc.
break;
case "depositos:close":
console.log("Widget closed by user");
// Hide the iframe or navigate away
break;
case "depositos:resize":
// Optionally auto-resize the iframe container
document.getElementById("depositos-container").style.height =
data.height + "px";
break;
case "depositos:error":
console.error("Widget error:", data.error);
break;
}
});
</script>
Passing Config via URL Parameters
As an alternative to postMessage, you can pass configuration directly in the iframe URL. This is useful for static pages or server-rendered HTML where you know the config at page load time.
Individual Parameters
<iframe
src="https://embed.deposit-os.com?destChain=42161&destToken=0xaf88d065e77c8cC2239327C5EDb3A432268e5831&destAddress=0xYourAddress&theme=dark"
style="width: 420px; height: 600px; border: none;"
></iframe>
Base64-encoded Config
For complex configurations, encode the full config object as base64 JSON:
<iframe
src="https://embed.deposit-os.com?config=eyJkZXN0Q2hhaW4iOjQyMTYxLC4uLn0="
style="width: 420px; height: 600px; border: none;"
></iframe>
Individual URL parameters take precedence over values in the base64 blob, so you can use the blob as a base and override specific fields.
Render Modes
The iframe host supports three display modes:
inline— renders the widget directly in the iframe (default)popup— auto-opens a modal when the iframe loadsbutton— shows a launcher button; clicking opens the modal
Use type (or renderType) in the iframe URL:
<iframe src="https://embed.deposit-os.com?type=inline&config=..." ...></iframe>
<iframe src="https://embed.deposit-os.com?type=popup&config=..." ...></iframe>
<iframe src="https://embed.deposit-os.com?type=button&buttonText=Deposit%20Now&config=..." ...></iframe>
Message Protocol
Communication between the parent page and the iframe follows a typed message protocol.
Parent to Iframe (Inbound)
| Message Type | Payload | Description |
|---|---|---|
| depositos:config | { config: Partial<DepositOSConfig> } | Send or update the widget configuration. Can be sent at any time to update config dynamically. |
| depositos:displayType | { displayType: "inline" \| "popup" \| "button", buttonText?: string } | Update render type and optionally set launcher button text dynamically. |
Iframe to Parent (Outbound)
| Message Type | Payload | Description |
|---|---|---|
| depositos:ready | — | The iframe has loaded and is ready to receive configuration. |
| depositos:success | { txHash?: string } | A deposit completed successfully. |
| depositos:close | — | The user clicked the close button. |
| depositos:resize | { height: number } | The widget content height changed. Use this to auto-size the iframe. |
| depositos:error | { error: string } | A configuration or runtime error occurred. |
Updating Config Dynamically
You can send a new depositos:config message at any time to update the widget without reloading the iframe. The new config is shallow-merged with the existing one:
// Update just the order reference
iframe.contentWindow.postMessage(
{
type: "depositos:config",
config: { orderRef: "order-99887" },
},
"*"
);
You can also switch render mode dynamically:
iframe.contentWindow.postMessage(
{
type: "depositos:displayType",
displayType: "button",
buttonText: "Open Deposit",
},
"*"
);
Dynamic recipient address per user
If each user in your app has a different recipient wallet, send destAddress dynamically through depositos:config.
Recommended pattern:
- Wait for
depositos:ready - Send user-specific config including
destAddress - Re-send when the active user changes
const iframeOrigin = "https://embed.deposit-os.com";
const iframe = document.getElementById("depositos-iframe");
function sendUserConfig(user) {
iframe.contentWindow.postMessage(
{
type: "depositos:config",
config: {
destChain: 42161,
destToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
destAddress: user.depositAddress, // dynamic recipient
orderRef: user.id, // optional tracking
},
},
iframeOrigin
);
}
window.addEventListener("message", (event) => {
if (event.origin !== iframeOrigin) return;
if (event.data?.type === "depositos:ready") {
sendUserConfig(currentUser);
}
});
// Later, if user changes:
// sendUserConfig(nextUser);
Security Considerations
- Always specify a target origin instead of
"*"when sending messages in production:
iframe.contentWindow.postMessage(
{ type: "depositos:config", config: config },
"https://embed.deposit-os.com"
);
- Validate the
originof incoming messages before processing them:
window.addEventListener("message", function (event) {
if (event.origin !== "https://embed.deposit-os.com") return;
// Process event.data
});
- Host the iframe app on the same domain or a trusted subdomain to align with your Content Security Policy.