Using Popup Windows in io.Connect Desktop

There are two common ways to leverage pop-up windows in io.Connect Desktop:

  • For io.Connect Desktop 10+, create the popup dynamically with io.windows.createPopup().
  • For older versions or existing apps you don’t want to rewrite, reuse a hidden frameless app/window and show it with showPopup().

A) Available since Desktop 10+: createPopup()

Starting with io.Connect Desktop 10.0, you can create a popup with io.windows.createPopup().

createPopup() creates a hidden frameless popup window for you. You don’t need a separate app definition, an auto-started hidden app, or a preconfigured popup-window. You can create a simple popup content directly with JavaScript by using the returned browserWindow.document. or keepthe popup UI in a separate HTML file which is more structured and usually easier to maintain.

1. Create the launcher app

The launcher app contains the button that will show the popup.

<button type="button" id="button" class="btn btn-primary">
    Show Dynamic Popup
</button>

<span id="status">Popup not created yet.</span>

<script src="index.js"></script>

2. Create the popup HTML

Create a separate popup.html file for the popup UI.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Quick Actions</title>
    <style>
        html,
        body {
            box-sizing: border-box;
            width: 100%;
            height: 100%;
            margin: 0;
            overflow: hidden;
            font-family: Arial, sans-serif;
            background: #1f2933;
            color: #f5f7fa;
        }

        *,
        *::before,
        *::after {
            box-sizing: inherit;
        }

        .popup {
            height: 100%;
            padding: 16px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            gap: 10px;
        }

        h3 {
            margin: 0;
            font-size: 18px;
        }

        p {
            margin: 0;
            color: #cbd5e1;
            font-size: 13px;
        }

        .actions {
            display: flex;
            gap: 8px;
        }

        button {
            padding: 6px 10px;
            border-radius: 3px;
            font: inherit;
            font-size: 13px;
            cursor: pointer;
        }

        .primary {
            border: 0;
            background: #4f8cff;
            color: white;
        }

        .secondary {
            border: 1px solid #64748b;
            background: transparent;
            color: #f5f7fa;
        }
    </style>
</head>
<body>
    <div class="popup">
        <h3>Quick Actions</h3>
        <p>This frameless popup was created dynamically with io.windows.createPopup().</p>
        <div class="actions">
            <button class="primary">Open Details</button>
            <button class="secondary">Copy ID</button>
        </div>
    </div>
</body>
</html>

3. Create and show the popup

In the launcher app, create the popup with createPopup(), load the HTML page into it, and show it relative to the button.

window.addEventListener("DOMContentLoaded", initializeApp);

async function initializeApp() {
    const io = await IODesktop();

    const button = document.getElementById("button");
    const status = document.getElementById("status");
    const myWindow = io.windows.my();

    const popup = await io.windows.createPopup({
        width: 320,
        height: 160
    });

    popup.browserWindow.location.href =
        "http://localhost:4242/popup/create-popup-app/popup.html";

    status.textContent = "Popup created.";

    button.addEventListener("click", async () => {
        await showPopup(popup, myWindow, button);
        status.textContent = "Popup shown.";
    });
}

function getButtonBounds(button) {
    return {
        left: Math.round(button.getBoundingClientRect().left),
        top: Math.round(button.getBoundingClientRect().top),
        width: Math.round(button.getBoundingClientRect().width),
        height: Math.round(button.getBoundingClientRect().height)
    };
}

async function showPopup(popup, myWindow, button) {
    const popupOptions = {
        windowId: popup.ioConnectWindow.id,
        targetBounds: getButtonBounds(button),
        size: {
            width: 320,
            height: 160
        },
        targetLocation: "bottom",
        verticalOffset: 12
    };

    await myWindow.showPopup(popupOptions);
}

The important pieces are:

  • createPopup() creates the hidden frameless popup window.
  • browserWindow.location.href loads the HTML page into that popup.
  • ioConnectWindow.id is passed as windowId to showPopup().
  • targetBounds tells io.Connect Desktop where the popup should appear.
  • targetLocation and verticalOffset control the popup placement.

You can also create the content directly with JavaScript, as shown in the official docs:

const popup = await io.windows.createPopup();

popup.browserWindow.document.body.innerText = "My Popup";

This is useful for quick examples or simple messages, but a separate HTML file is easier to read, style, test, and maintain.

B) Pre-10 Versions and Existing Apps: showPopup()

If you already have a hidden app or hidden window that you use as a popup, you don’t have to rewrite it immediately. You can still show that window with showPopup().

This is the pattern used by older examples:

  1. Define a visible app that triggers the popup.
  2. Define a hidden app that contains the popup UI.
  3. Find the hidden popup window by name.
  4. Pass its window ID to showPopup().

For io.Connect Desktop 10+, the hidden popup window must be frameless. Register the apps.

{
        "name":"popup-demo",
        "title":"Popup Demo",
        "type": "window",
        "ignoreSavedLayout": true,
        "details":{
            "url":"http://localhost:4242/popup/popup-app/index.html",
            "mode": "html",
            "top": 200,
            "left": 200
        }
},
{
    "name": "popup-window",
    "title": "Popup",
    "type": "window",
    "ignoreSavedLayout": true,
    "hidden": true,
    "autoStart": true,
    "details": {
        "url": "http://localhost:4242/popup/popup-window/index.html",
        "mode": "frameless",
        "hidden": true
    }
}

Then from the visible app:

async function initializeApp() {
    // Initialize the `@interopio/desktop` library.
    await initializeIOConnect().catch(console.error);

    // Reference to the window which will be used as a popup.
    const popup = io.windows.find("popup-window");
    if (!popup) {
        console.error("Popup window wasn't found. Make sure the popup-window app is registered and running.");
        return;
    }

    // Reference to the button which will trigger the popup when clicked.
    const button = document.getElementById("button");

    // Reference to this window.
    const myWindow = io.windows.my();

    button.addEventListener("click", () => {
        activatePopup(popup, myWindow, button);
    });
};

And show the popup button:

const popup = io.windows.find("popup-window");
const myWindow = io.windows.my();

await myWindow.showPopup({
    windowId: popup.id,
    targetBounds: getButtonBounds(button),
    size: {
        width: 320,
        height: 160
    },
    targetLocation: "bottom",
    verticalOffset: 12
});

Use this approach when the popup already exists as a separate app with its own configuration, lifecycle, or compatibility requirements.

This will work in io.Connect 10+, but only with a frameless window. If you see this error:

Method 'showAsPopup' can not be called for window with id ... and mode "html"

then the window you’re trying to show as a popup was created as an HTML window, not a frameless one.

For the hidden-window approach, check the app definition loaded by io.Connect Desktop and make sure it contains:

"mode": "frameless"

If the config is already fixed but the error still appears, restart io.Connect Desktop or close the already-running hidden popup instance. Existing windows keep the mode they were created with, so a running "html" instance must be recreated.

References