How to Embed an Agent-Native Application Within an Existing App
You can embed an Agent-Native application within another existing application by rendering the <EmbeddedApp> component from @agent-native/embedding, which creates a sandboxed iframe and exposes a type-safe, origin-validated message bridge for bidirectional communication.
Agent-Native provides a lightweight embedding layer that lets you mount a full Agent-Native app, picker, or agent inside any existing React or plain-HTML host. According to the BuilderIO/agent-native source code, the @agent-native/embedding package is a thin re-export of the implementation found in packages/core/src/embedding/react.tsx, giving you a secure iframe-based integration with support for fire-and-forget messages and typed request-response patterns.
How Agent-Native Embedding Works
At its core, the embedding system renders an <iframe> that loads the remote Agent-Native app URL. The EmbeddedApp component in packages/core/src/embedding/react.tsx automatically appends embedded=1 to the query string via withEmbeddedAppParams so the remote app knows to hide its own navigation chrome. All cross-frame traffic is wrapped in a typed Agent-Native embed envelope defined in packages/core/src/embedding/protocol.ts, which includes a type field such as MESSAGE, REQUEST, RESPONSE, ERROR, or READY, plus an optional requestId for correlated conversations.
Key Source Files
packages/embedding/src/react.tsx— Public package entry point that re-exports the core React component.packages/core/src/embedding/react.tsx— FullEmbeddedAppimplementation that manages the iframe lifecycle, message routing, and request storage.packages/core/src/embedding/protocol.ts— Defines envelope types, URL helpers likeembeddedAppOrigin, and validation utilities.packages/core/src/embedding/bridge.ts— Convenience functions for the embedded side to post back to the host.packages/embedding/README.md— Quick-start documentation and example snippets.
How to Embed an Agent-Native Application in React
The simplest way to embed an Agent-Native application is to import the EmbeddedApp component and provide a url prop.
import { EmbeddedApp } from "@agent-native/embedding";
function Host() {
return (
<EmbeddedApp
url="https://assets.agent-native.com/picker"
// optional: add custom query params or disable the default ?embedded=1
// embed={false}
onLoad={(ref) => {
// fire-and-forget message to configure the picker
ref.postMessage("configure", { accept: ["image/*"] });
}}
onMessage={(name, payload) => {
if (name === "chooseImage") {
console.log("User picked:", payload);
}
}}
style={{ height: 500 }} // optional custom iframe styling
/>
);
}
In packages/core/src/embedding/react.tsx, the component derives the iframe origin from the URL using embeddedAppOrigin and uses it as the default targetOrigin. You can override this with targetOrigin or whitelist additional origins via allowedOrigins.
Sending Fire-and-Forget Messages to the Embedded App
The host communicates with the embedded app through an imperative ref provided to the onLoad callback. Use ref.postMessage(name, payload) to send fire-and-forget events to the Agent-Native UI. The embedded app signals readiness by emitting a READY envelope, which the host receives through onReady, while general one-way messages arrive via onMessage.
Request-Response Communication Between Host and Embedded App
For asynchronous operations that require an answer, the host can call ref.request(name, payload, { timeoutMs }). Internally, packages/core/src/embedding/react.tsx generates a unique correlation ID via createEmbeddedAppRequestId and stores the pending promise in a Map. The promise resolves when a RESPONSE envelope arrives or rejects on ERROR or timeout.
import { useRef } from "react";
import { EmbeddedApp, type EmbeddedAppRef } from "@agent-native/embedding";
export function HostWithImperative() {
const appRef = useRef<EmbeddedAppRef>(null);
const generate = async () => {
const result = await appRef.current?.request<string>(
"GenerateBlogHero",
{ topic: "AI agents" },
{ timeoutMs: 60_000 },
);
console.log("Generated hero:", result);
};
return (
<>
<button onClick={generate}>Generate Hero</button>
<EmbeddedApp
url="https://assets.agent-native.com/blog-writer"
ref={appRef}
onReady={() => console.log("Embedded app ready")}
/>
</>
);
}
Handling Inbound Requests from the Embedded Agent-Native App
The embedded app can also act as a client while the host acts as a server. Use the onRequest prop to handle inbound requests, return data directly, or throw an error to reject the call.
import { EmbeddedApp } from "@agent-native/embedding";
function HostWithRequests() {
return (
<EmbeddedApp
url="https://assets.agent-native.com/agent"
onRequest={async (name, payload) => {
if (name === "fetchUserData") {
// Perform a server-side fetch or DB query here
const user = await fetch("/api/user").then((r) => r.json());
return user;
}
throw new Error(`Unsupported request ${name}`);
}}
/>
);
}
If you throw inside onRequest, the bridge sends an ERROR envelope back to the embedded app. This lets the embedded side catch the exception exactly like a failed network call.
Communicating from the Embedded App Back to the Host
Inside the Agent-Native application running in the iframe, import the bridge helpers to send messages upward to the parent window.
import { sendEmbeddedAppMessage } from "@agent-native/embedding/bridge";
sendEmbeddedAppMessage("chooseImage", {
url: "https://cdn.example.com/photo.png",
});
This helper lives in packages/core/src/embedding/bridge.ts and wraps the payload in the standard createAgentNativeEmbedEnvelope format before calling window.parent.postMessage. It ensures that every payload conforms to the protocol expected by the host, regardless of how the embedded app is built.
Security, Origins, and Cleanup
The embedding layer validates communication through origin checks. By default, targetOrigin is derived from the provided URL using embeddedAppOrigin, but you can explicitly set it or use allowedOrigins to support multiple trusted domains. All messages are scoped to these origins to prevent unauthorized cross-frame access.
When the EmbeddedApp component unmounts, all pending requests stored in the internal Map are automatically rejected. This prevents memory leaks and dangling promises if the user navigates away while requests are in flight.
Summary
- Mount an Agent-Native app with
<EmbeddedApp>from@agent-native/embedding, which renders a sandboxed iframe implemented inpackages/core/src/embedding/react.tsx. - The iframe URL automatically receives
embedded=1to hide native navigation chrome. - Use
ref.postMessage()for fire-and-forget events andref.request()for promise-based request-response flows with configurabletimeoutMs. - Handle inbound requests with
onRequest, and listen for readiness viaonReady. - Send messages from the iframe to the host with
sendEmbeddedAppMessagefrompackages/core/src/embedding/bridge.ts. - Origins are validated by default using
embeddedAppOrigin, and pending promises are cleaned up on unmount to avoid memory leaks.
Frequently Asked Questions
Can I embed an Agent-Native app inside a non-React application?
Yes. While the official @agent-native/embedding package provides a React component in packages/embedding/src/react.tsx, the underlying protocol in packages/core/src/embedding/protocol.ts is standard postMessage. You can recreate the iframe and envelope logic in any framework or vanilla JavaScript.
How does the embedded app know it is running inside an iframe?
The EmbeddedApp component automatically appends embedded=1 to the iframe URL via withEmbeddedAppParams. The remote Agent-Native app reads this query parameter and conditionally hides its own navigation chrome.
What happens to pending requests when the iframe unmounts?
All pending promises are automatically rejected. The EmbeddedApp component tracks requests in a Map and cleans them up during unmount to prevent memory leaks and unresolved promises.
Is the host-to-embedded communication secure?
Yes. By default, messages are scoped to the iframe's origin via targetOrigin derived from embeddedAppOrigin. You can further restrict communication by passing an allowedOrigins array. All data is wrapped in typed envelopes created by createAgentNativeEmbedEnvelope for structured, validated exchanges.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →