# Iframe guidelines

*Category:* Getting started
*Section:* Guidelines
*Source:* https://uikit.developers.rio.cloud/#/start/guidelines/iframe
*Captured:* 2026-04-27T14:57:07.135Z

Web applications can embed smaller SPAs as widgets using an &lt;iframe&gt; to enrich it's functionality provided by other services and teams. This page explains what the parent application (host) and the child application (widget) must do to ensure correct styling, sizing, and communication.

## Iframe guidelines

## Working with iframes

## Terminology

- Parent application: the host SPA that renders the &lt;iframe&gt;.
- Widget (child application): the small SPA running inside the iframe.
- Iframe integration: layout, sizing, and communication between parent and widget.

## Enable widget-specific styling

If an application is shipped as a widget (i.e., it is meant to be embedded into other applications via an iframe), the widget must mark itself accordingly by adding the classis-iframe-widget to the document’s <html> element.

**Who needs to add this?**

The child application (widget) must add <html class="is-iframe-widget">. The parent application typically cannot (and should not) modify the widget’s HTML root.

## What the window-iframe is and why the RIO Pageloader matters

The RIO UIKIT uses the class window-iframe on the <html> element to enable iframe-specific baseline styles. This helps widgets render cleanly when embedded (for example, ensuring the document background behaves correctly in an iframe context).

**Who sets window-iframe?**

This class is added automatically by the RIO UIKIT, but only if your SPA includes the RIO Pageloader (the initial loading animation shown until the application is ready).

**What is the RIO Pageloader?**

The RIO Pageloader is the initial loading animation displayed while the application bundles load and the SPA initializes. It is also the recommended place to bootstrap UIKIT early, so iframe-related classes can be applied before the first render to avoid visual flicker.

The loader is already part of the RIO frontend template and added to the index.html page inside the body element.

For reference:

```html
<link
rel='stylesheet'
type='text/css'
href='https://cdn.rio.cloud/pageLoader/pageLoader.css'
/>
<script src='https://cdn.rio.cloud/pageLoader/pageLoader.js'></script>
```

&lt;link rel='stylesheet' type='text/css' href='https://cdn.rio.cloud/pageLoader/pageLoader.css' /&gt;

&lt;script src='https://cdn.rio.cloud/pageLoader/pageLoader.js'&gt;

&lt;/script&gt;

## Auto-resizing the iframe (recommended)

For widgets with dynamic height, scrolling inside the iframe often leads to a poor user experience. To avoid nested scrolling, we recommend using iframe-resizer-react from https://www.npmjs.com/package/iframe-resizer-react. This library adapts the iframe height automatically to the widgets content.

**Parent application (host)**

The parent initializes the resizer on the iframe element. Use this if you want to embed an iframe into your application.

```js
import IframeResizer from 'iframe-resizer-react';
```

import

from

'iframe-resizer-react'

;

**Child application (widget)**

The widget must include the resizer content script so it can report size changes to the parent.

```js
npm install iframe-resizer --save
```

-

--

For more information vist https://github.com/davidjbradshaw/iframe-resizer

```js
import iframeResizerContentWindow from 'iframe-resizer/js/iframeResizer.contentWindow';
```

import

from

'iframe-resizer/js/iframeResizer.contentWindow'

;

or simply

```js
import 'iframe-resizer/js/iframeResizer.contentWindow';
```

import

'iframe-resizer/js/iframeResizer.contentWindow'

;

Alternatively, you can add the iframe contentWindow script from a public CDN into yourindex.html

```html
<script
defer
type='text/javascript'
src='https://cdn.rio.cloud/libs/iframeResizer/4.2.8/iframeResizer.contentWindow.min.js'
></script>
```

&lt;script defer type='text/javascript' src='https://cdn.rio.cloud/libs/iframeResizer/4.2.8/iframeResizer.contentWindow.min.js' &gt;

&lt;/script&gt;

### Example: Example 1



#### React (tsx)

```tsx
import IframeResizer from 'iframe-resizer-react';

const IFrameWidgetExample = () => (
    <div className='display-flex justify-content-center'>
        <div className='iframe-wrapper width-400'>
            <IframeResizer src='https://menu.rio.cloud' />
        </div>
    </div>
);

export default IFrameWidgetExample;
```

#### HTML (html)

```html
<div class="display-flex justify-content-center">
    <div class="iframe-wrapper width-400">
        <iframe title="iframe" src="https://menu.rio.cloud" id="iFrameResizer0" scrolling="no" style="overflow: hidden; height: 250px;">
        </iframe>
    </div>
</div>
```

## Embedding a widget (iframe) in a fullscreen dialog

When a widget needs to be displayed as a full page inside a dialog (for example, showing a full-featured SPA), the iframe often requires additional layout and styling adjustments.

Rendering a full fledged SPA, there is the need to hide it's main navigation. The best and cleanest way is to hide the navigation in the SPA, when it is loaded in an iframe. Another way is to use our no-application-header class. The iframe then has to be wrapped in a iframe-wrapper element on the host side.

RIO iframes are transparent by default, also the SPA itself. Because of that you have to add the class bg-lighter to the iframe.

**Recommended pattern**

- Wrap the iframe in an .iframe-wrapper to control sizing and overflow.
- Use utility classes to remove default iframe styling (borders, background) if needed.
- Prefer hiding parent navigation by not rendering it in the dialog context (instead of compensating with offsets).

**Common helper classes**

- unstyled: removes default iframe border styling (not necessary for widgets when using the iframe-resizer).
- full-width: makes the iframe take the full width of its container. (not necessary for widgets when using the iframe-resizer)
- full-window: makes the iframe fill (height/width) the available container space (often for dialogs).
- no-application-header: compensates for a missing header/navigation area (use carefully). It adds a -50px offset to move the SPA navigation outside the visible area.
- bg-xxx: set the iframe background color, as the default is transparent.

### Example: Example 2

Open fullscreen dialog

#### React (tsx)

```tsx
import { useState, useRef } from 'react';

import Dialog from '@rio-cloud/rio-uikit/Dialog';
import OverlayTrigger from '@rio-cloud/rio-uikit/OverlayTrigger';
import Tooltip from '@rio-cloud/rio-uikit/Tooltip';
import Button from '@rio-cloud/rio-uikit/Button';

const IFrameInDialogExample = () => {
    const [showDialog, setShowDialog] = useState(false);

    const iframeRef = useRef<HTMLIFrameElement>(null);

    const handleOpenInNewWindow = () => {
        const currentIframeUrl = iframeRef.current?.contentWindow?.location.href;
        window.open(currentIframeUrl, '_blank');
    };

    const body = (
        <div className='iframe-wrapper'>
            <iframe
                title='uikit in fullscreen dialog'
                className='unstyled bg-lighter full-window no-application-header'
                src='/#start/intro'
                ref={iframeRef}
            />
        </div>
    );

    const headerButtons = (
        <OverlayTrigger placement='bottom' overlay={<Tooltip width='auto'>Open in new window</Tooltip>}>
            <Button bsStyle='muted' iconOnly onClick={handleOpenInNewWindow}>
                <span className='rioglyph rioglyph-new-window' />
            </Button>
        </OverlayTrigger>
    );

    return (
        <>
            <Button onClick={() => setShowDialog(true)}>
                <span>Open fullscreen dialog</span>
            </Button>
            <Dialog
                show={showDialog}
                title='Service in a dialog'
                bodyClassName='padding-0'
                body={body}
                onClose={() => setShowDialog(false)}
                showCloseButton
                bsSize={Dialog.SIZE_FULL_SCREEN}
                headerButtons={headerButtons}
            />
        </>
    );
};

export default IFrameInDialogExample;
```

#### HTML (html)

```html
<div class="iframe-wrapper">
    <iframe class="unstyled full-window no-application-header bg-lighter" src="https://..." />
</div>
```

## Open a widget dialog in the parent application

When it comes to opening a modal dialog from within an iframe, there's flexibility in how it can be presented. Depending on the context, the dialog might overlay the iframe content itself or seamlessly cover the entire parent application.

A widget can request the parent application to open a dialog (for example, to present the widget in fullscreen or show another part of it). This is done by sending an event via postMessage from the widget to the parent window.

**Recommended event flow**

1. Widget sends a message to the parent requesting the dialog to open.
2. Upon receiving this event, the parent application responds by rendering a simple dialog, such as an InfoDialog, which includes another iframe pointing to a specific route within the widget. This embedded view then displays the dialog content and manages the dialog's close events.
3. When it's time to close the dialog (via a close button inside the iframe), the embedded view sends another postMessage to the parent application. This event allows the dialog view to send any necessary data back to the originating iframe.

### Example: Example 3



#### React (tsx)

```tsx
import React from 'react';
import IframeResizer from 'iframe-resizer-react';

import FrameDialog from '@rio-cloud/rio-uikit/FrameDialog';
import { PREFIX } from '../../../widgets/WidgetDemo';

const IFrameWidgetExample = React.memo(() => (
    <>
        <div className='iframe-wrapper'>
            <IframeResizer
                id='widgetDialogFrame'
                src={`${window.location.origin}${window.location.pathname}#widgetDemo`}
                className='unstyled width-100pct'
                style={{ width: '1px', minWidth: '100%' }}
            />
        </div>
        <FrameDialog
            eventPrefix={PREFIX}
            getSourceFrame={() => document.getElementById('widgetDialogFrame') as HTMLIFrameElement}
        />
    </>
));

export default IFrameWidgetExample;

// ----------------------------------------------------------
import { useState } from 'react';

import Button from '@rio-cloud/rio-uikit/Button';
import { OPEN_DIALOG_EVENT, CLOSE_DIALOG_EVENT, type OPEN_DIALOG_EVENT_TYPE } from '@rio-cloud/rio-uikit/FrameDialog';
import usePostMessage, { type RemoteAction } from '@rio-cloud/rio-uikit/usePostMessage';

export const PREFIX = 'EVENT_UIKIT_WIDGET_DEMO';

const WidgetDemo = () => {
    const [messageFromDialog, setMessageFromDialog] = useState('-');

    const { sendMessageToParent } = usePostMessage(PREFIX, (remoteAction: RemoteAction) => {
        console.log(remoteAction);

        if (remoteAction.type === `${PREFIX}${CLOSE_DIALOG_EVENT}`) {
            setMessageFromDialog(remoteAction.payload);
        }
    });

    const handleOpenDialog = () => {
        const payload: OPEN_DIALOG_EVENT_TYPE = {
            src: `${window.location.origin}${window.location.pathname}#widgetDemoDialog`,
            dialogProps: {
                bsSize: 'sm',
            },
        };

        sendMessageToParent({
            type: `${PREFIX}${OPEN_DIALOG_EVENT}`,
            payload,
        });
    };

    return (
        <div className='bg-white border rounded padding-20 height-200'>
            <div className='text-medium'>This is a widget embedded via an iframe</div>
            <div className='btn-toolbar margin-top-20'>
                <Button onClick={handleOpenDialog}>Open dialog in parent frame</Button>
            </div>
            <div className='margin-top-20'>
                <label>Closing message from dialog:</label>
                <div>{messageFromDialog}</div>
            </div>
        </div>
    );
};

export default WidgetDemo;

// ----------------------------------------------------------
import Button from '@rio-cloud/rio-uikit/Button';
import Dialog from '@rio-cloud/rio-uikit/Dialog';
import usePostMessage from '@rio-cloud/rio-uikit/usePostMessage';
import { CLOSE_DIALOG_EVENT } from '@rio-cloud/rio-uikit/FrameDialog';

import { dummyText } from '../../utils/data';
import { PREFIX } from './WidgetDemo';

const WidgetDemo = () => {
    const { sendMessageToParent } = usePostMessage(PREFIX);

    const handleCloseDialog = () => {
        sendMessageToParent({
            type: `${PREFIX}${CLOSE_DIALOG_EVENT}`,
            payload: 'hello from the dialog view',
        });
    };

    return (
        <div className='bg-white min-height-200'>
            <Dialog.Title title='Widget dialog' onCloseButtonClick={handleCloseDialog} />

            <Dialog.Body className='padding-20'>
                <div className='text-medium'>This is the widget dialog content embedded via an iframe</div>
                <p>{dummyText}</p>
            </Dialog.Body>

            <Dialog.Footer>
                <div>
                    <Button onClick={handleCloseDialog}>Close dialog in parent frame</Button>
                </div>
            </Dialog.Footer>
        </div>
    );
};

export default WidgetDemo;
```

#### HTML (html)

```html
<div class="iframe-wrapper">
    <iframe title="iframe" id="widgetDialogFrame" src="https://uikit.developers.rio.cloud/#widgetDemo" class="unstyled width-100pct" style="width: 1px; min-width: 100%; overflow: hidden; height: 200px;" scrolling="no">
    </iframe>
</div>
```