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

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 in packages/core/src/embedding/react.tsx.
  • The iframe URL automatically receives embedded=1 to hide native navigation chrome.
  • Use ref.postMessage() for fire-and-forget events and ref.request() for promise-based request-response flows with configurable timeoutMs.
  • Handle inbound requests with onRequest, and listen for readiness via onReady.
  • Send messages from the iframe to the host with sendEmbeddedAppMessage from packages/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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →