# IFrames

Source: https://docs.interop.io/browser/capabilities/windows/iframes/index.html

## Overview

Available since io.Connect Browser 4.5

The [`@interopio/iframes-api`](https://www.npmjs.com/package/@interopio/iframes-api) library provides a convenient way to open io.Connect apps in `<iframe>` elements.

The IFrames API is accessible via the [`io.iframes`](https://docs.interop.io/browser/reference/javascript/iframes/api/index.md) object.

## Settings for IFrame Apps

You can use the `iframePermissionsPolicy` and the `iframeSandbox` properties of the `details` object in [app definitions](https://docs.interop.io/browser/capabilities/app-management/index.md#app_definitions) to provide settings for the `allow` and the `sandbox` attributes of the `<iframe>` elements in which the apps will be opened. These settings are valid for apps opened in standalone `<iframe>` elements via the IFrames API as well as for apps opened in `<iframe>` elements within a Workspace by the io.Connect platform.

The following example demonstrates how to define an app with settings for the `allow` and the `sandbox` attributes of the `<iframe>` element in which it will be opened:

```javascript
import IOBrowserPlatform from "@interopio/browser-platform";

const config = {
    licenseKey: "my-license-key",
    applications: {
        local: [
            {
                name: "my-app",
                type: "window",
                title: "My App",
                details: {
                    url: "https://my-domain.com/my-app",
                    iframePermissionsPolicy: {
                        flags: "geolocation 'self' https://a.example.com https://b.example.com; fullscreen 'none'"
                    },
                    iframeSandbox: {
                        flags: "allow-scripts allow-same-origin"
                    }
                }
            }
        ]
    }
};

const { io } = await IOBrowserPlatform(config);
```

## Enabling IFrames

To use the IFrames API in your interop-enabled apps, install the [`@interopio/iframes-api`](https://www.npmjs.com/package/@interopio/iframes-api) library in your project and reference it in your app. Depending on whether you want to use the IFrames API in your [Main app](https://docs.interop.io/browser/developers/browser-platform/overview/index.md), or in a [Browser Client](https://docs.interop.io/browser/developers/browser-client/overview/index.md) app, see the respective initialization examples in the following sections.

## Main App

Install the necessary packages:

```cmd
npm install @interopio/browser-platform @interopio/iframes-api
```

Initialize the [`@interopio/browser-platform`](https://www.npmjs.com/package/@interopio/browser-platform) library and enable the IFrames API by passing the `IOIFrames()` factory function to the `libraries` array of the `browser` configuration object:

```javascript
import IOBrowserPlatform from "@interopio/browser-platform";
import IOIFrames from "@interopio/iframes-api";

const config = {
    licenseKey: "my-license-key",
    browser: {
        libraries: [IOIFrames]
    }
};

const { io } = await IOBrowserPlatform(config);

// Now you can access the IFrames API via `io.iframes`.
```

## Browser Clients

To enable the IFrames API in your [Browser Client](https://docs.interop.io/browser/developers/browser-client/overview/index.md) apps, install the [`@interopio/browser`](https://www.npmjs.com/package/@interopio/browser) and [`@interopio/iframes-api`](https://www.npmjs.com/package/@interopio/iframes-api) libraries and initialize the [`@interopio/browser`](https://www.npmjs.com/package/@interopio/browser) library by passing the `IOIFrames()` factory function in the configuration object. When `IOBrowser()` resolves, the IFrames API will be accessible via the `iframes` property of the returned object - e.g., `io.iframes`. The following examples demonstrate how to enable the IFrames API in JavaScript, React, and Angular apps.

### JavaScript

Install the necessary packages:

```cmd
npm install @interopio/browser @interopio/iframes-api
```

Initialize the [`@interopio/browser`](https://www.npmjs.com/package/@interopio/browser) library and pass the `IOIFrames()` factory function to the `libraries` array of the configuration object:

```javascript
import IOBrowser from "@interopio/browser";
import IOIFrames from "@interopio/iframes-api";

const config = {
    libraries: [IOIFrames]
};

const io = await IOBrowser(config);

// Now you can access the IFrames API via `io.iframes`.
```

By default, the `IOBrowser()` and `IOIFrames()` factory functions are injected in the global `window` object.

### React

Install the necessary packages:

```cmd
npm install @interopio/react-hooks @interopio/iframes-api
```

Initialize the [`@interopio/react-hooks`](https://www.npmjs.com/package/@interopio/react-hooks) library in one of the following ways and pass the `IOIFrames()` factory function to the `libraries` array of the configuration object for initializing the library.

- using the `<IOConnectProvider />` component:

```javascript
import { createRoot } from "react-dom/client";
import { IOConnectProvider } from "@interopio/react-hooks";
import IOBrowser from "@interopio/browser";
import IOIFrames from "@interopio/iframes-api";

const settings = {
    browser: {
        factory: IOBrowser,
        config: {
            libraries: [IOIFrames]
        }
    }
};

const domElement = document.getElementById("root");
const root = createRoot(domElement);

root.render(
    <IOConnectProvider fallback={<h2>Loading...</h2>} settings={settings}>
        <App />
    </IOConnectProvider>
);
```

- using the `useIOConnectInit()` hook:

```javascript
import { useIOConnectInit } from "@interopio/react-hooks";
import IOBrowser from "@interopio/browser";
import IOIFrames from "@interopio/iframes-api";

const App = () => {
    const settings = {
        browser: {
            factory: IOBrowser,
            config: {
                libraries: [IOIFrames]
            }
        }
    };

    const io = useIOConnectInit(settings);

    return io ? <Main io={io} /> : <Loader />;
};

export default App;
```

### Angular

Install the necessary packages:

```cmd
npm install @interopio/ng @interopio/iframes-api
```

Initialize the [`@interopio/ng`](https://www.npmjs.com/package/@interopio/ng) library and pass the `IOIFrames()` factory function to the `libraries` array of the configuration object for initializing the library.

In `app.config.ts` of the Browser Client app:

```typescript
import { ApplicationConfig } from "@angular/core";
import { provideIoConnect } from "@interopio/ng";
import IOBrowser, { IOConnectBrowser } from "@interopio/browser";
import IOIFrames from "@interopio/iframes-api";

const config: IOConnectBrowser.Config = {
    libraries: [IOIFrames]
};

export const appConfig: ApplicationConfig = {
    providers: [
        provideIoConnect({
            browser: {
                factory: IOBrowser,
                config
            }
        })
    ]
};
```

> ℹ️ *For more details on initializing and configuring the `@interopio/ng` library depending on whether you are using Angular standalone components or modules, see the [Developers > Browser Client > Angular](https://docs.interop.io/browser/developers/browser-client/angular/index.md) section.*

## Opening Apps in IFrames

To open an app in an `<iframe>` element within the current app instance, use the [`openApp()`](https://docs.interop.io/browser/reference/javascript/iframes/api/index.md#API-openApp) method and pass an [`OpenAppConfig`](https://docs.interop.io/browser/reference/javascript/iframes/openappconfig/index.md) object as an argument:

```javascript
const container = document.getElementById("my-iframe-container");

const config = {
    appName: "my-app",
    container,
    context: { ticker: "MSFT" },
    iframeConfig: {
        styles: {
            width: "100%",
            height: "100%",
            border: "none"
        }
    }
};

const { id, iframe } = await io.iframes.openApp(config);
```

If `container` property is omitted, the created `<iframe>` element will be appended to `document.body`.

The `openApp()` method resolves with an [`Instance`](https://docs.interop.io/browser/reference/javascript/iframes/instance/index.md) object describing the started app.

> ⚠️ *Note that if you provide a container element, it must be connected to the DOM - otherwise, `openApp()` will throw an error. If the container element is moved (e.g., via drag and drop), this will lead to disconnecting and reconnecting the `<iframe>` element to the DOM. The app instance will continue to run in the `<iframe>` element, but it won't be able to reconnect to the platform, because it has already been removed from the platform state. If the container element is deleted without closing the app instance in the `<iframe>`, it will be removed from the lists of io.Connect app and window instances tracked by the platform only if it's interop-enabled. That's why it's recommended to always close the app instance running in the `<iframe>` when removing the container element.*

> ⚠️ *Note that all Browser Client apps opened via the `openApp()` method of the IFrames API must use `@interopio/browser` 4.5 or later in order to be able to properly connect to the io.Connect platform.*

## Events

To handle close requests for apps opened in `<iframe>` elements within the current app instance, use the [`onCloseRequested()`](https://docs.interop.io/browser/reference/javascript/iframes/api/index.md#API-onCloseRequested) method and provide a callback function for handling the event:

```javascript
const iframeContainers = {};

const handler = async (instance, confirmClose) => {
    // Confirm to the platform that the `<iframe>` can be closed.
    await confirmClose();

    // Remove the `<iframe>` container with your custom logic.
};

const unsubscribe = io.iframes.onCloseRequested(handler);
```

> ℹ️ *The `onCloseRequested()` method notifies only about close requests only for `<iframe>` elements that have been created inside the current app instance.*

## Example

The following is a complete example demonstrating how to open an app in an `<iframe>` element, keep track of the `<iframe>` container, and handle close requests for the app:

```javascript
// Create a map to store `<iframe>` containers by ID.
const iframeContainers = {};

// Handler for close requests.
const handler = async (instance, confirmClose) => {

    // Confirm to the platform that the `<iframe>` can be closed.
    await confirmClose();

    // Remove the `<iframe>` container.
    iframeContainers[instance.id]?.remove();
    delete iframeContainers[instance.id];
};

// This method will notify only about close requests for `<iframe>` elements that have been created inside the current app instance.
const unsubscribe = io.iframes.onCloseRequested(handler);

// Start the app.
const start = async () => {

    // Create a container where the `<iframe>` will be visualized.
    const createIFrameContainer = () => {
        const container = document.createElement("div");

        container.style.width = "400px";
        container.style.height = "300px";
        container.style.boxSizing = "border-box";
        container.style.border = "1px solid orange";

        document.body.appendChild(container);

        return container;
    }

    const container = createIFrameContainer();

    // Passing CSS classes and styles before opening the app instance in an `<iframe>` element
    // may help avoid undesirable visual flickering that may be caused by manipulating the `<iframe>` element after creation.
    const iframeConfig = {
        styles: {
            width: "100%",
            height: "100%",
            border: "none",
            display: "block"
        }
    };

    const config = {
        appName: "my-app",
        container,
        context: { ticker: "MSFT" },
        iframeConfig
    };

    // Start the app.
    const { id, iframe } = await io.iframes.openApp(config);

    // Store the container so it can be removed when a close request is received.
    iframeContainers[id] = container;

    return { windowId: id, iframe };
};

const { windowId, iframe } = await start();

// Close the app.
await io.windows.findById(windowId).close();
```

## API Reference

For a complete list of the available IFrames API methods and properties, see the [IFrames API Reference Documentation](https://docs.interop.io/browser/reference/javascript/iframes/api/index.md).
