Skip to content
Development
Skill

/iframe

**CRITICAL**: This mode is for widgets **embedded as iframes in external web pages**. This is **NOT** a native window. This is **NOT** Jupyter. The widget is sandboxed.

From plugin
pywry
9918 skills1 agent2 commands1 MCP

Claude Code

$ npx -y skills add deeleeramone/PyWry --skill iframe --agent claude-code

SKILL.md

iframe.SKILL.md

iFrame Embed Mode

> **CRITICAL**: This mode is for widgets **embedded as iframes in external web pages**. > This is **NOT** a native window. This is **NOT** Jupyter. The widget is sandboxed.

What iFrame Mode IS

You're creating widgets to be **embedded as iframes** in external pages:

  • Widget served at a URL: `http://host:port/widget/{widget_id}`
  • Embedded via `<iframe src="...">` in parent page
  • Sandboxed execution - isolated from parent
  • Cross-origin restrictions apply

What iFrame Mode is NOT

❌ **NOT a native window** - No OS integration, no native dialogs ❌ **NOT Jupyter** - No kernel, no notebook context ❌ **NOT the main page** - You're embedded, constrained by parent

Architecture

┌───────────────────────────────────────────────────────────────────┐
│                   Parent Web Page (any origin)                    │
│                                                                   │
│   <iframe src="http://pywry-server:port/widget/abc123"            │
│           width="100%" height="500">                              │
│     ┌───────────────────────────────────────────────────────┐     │
│     │              PyWry Widget (sandboxed)                 │     │
│     │                                                       │     │
│     │    ┌───────────────────────────────────────────┐      │     │
│     │    │        Your Content (HTML/CSS/JS)         │      │     │
│     │    └───────────────────────────────────────────┘      │     │
│     │                                                       │     │
│     │    [Toolbars, Charts, Forms, etc.]                    │     │
│     │                                                       │     │
│     └───────────────────────────────────────────────────────┘     │
│   </iframe>                                                       │
│                                                                   │
│   Other page content...                                           │
└───────────────────────────────────────────────────────────────────┘
                   ↕ postMessage for communication

Key Constraints

Fixed Dimensions

  • Width and height determined by parent's `<iframe>` tag
  • Widget cannot resize itself
  • Design for specific dimensions or be responsive

Sandboxed Execution

  • Cannot access parent DOM
  • Cannot read parent cookies/storage
  • Limited access to some browser APIs
  • Navigation stays within iframe (unless allowed)

Cross-Origin Restrictions

  • Widget origin ≠ parent origin (usually)
  • Communication via `postMessage` only
  • Some features require `allow` attributes

Best Practices

1. Sizing - Design for Fixed Dimensions

# The parent controls your size
# <iframe src="..." width="600" height="400">

# Design content to fit
content = Div(
    content="...",
    style="width: 100%; height: 100%; overflow: auto;",
)

2. Toolbars - Keep Minimal

# Screen real estate is precious
create_widget(
    html=content.build_html(),
    toolbars=[{
        "position": "top",
        "items": [
            # Compact: icons only or short labels
            {"type": "button", "label": "⟳", "event": "refresh"},
            {"type": "toggle", "label": "Auto", "event": "auto-update"},
        ]
    }]
)

3. Navigation - Stays in Frame

# By default, navigation stays within iframe
navigate(widget_id, url="/other-view")  # Still in iframe

# External links may be blocked by parent
# Use with caution:
navigate(widget_id, url="https://external.com", external=True)

4. Downloads - May Require Gesture

# Some browsers block downloads from iframes
# Ensure download is triggered by user action
# (button click, not automatic)

Security Considerations

Sandboxing Protects Both Sides

  • Widget can't access parent page data
  • Parent can't directly access widget DOM
  • Use this for untrusted contexts

Secret Handling

# Never expose secrets in URL or logs
# Use SecretInput handler - value stays server-side
create_widget(
    toolbars=[{
        "items": [
            {"type": "secret", "label": "API Key", "event": "api-key"},
        ]
    }]
)

Token-Based Access

# Consider adding tokens to widget URLs for access control
# http://host:port/widget/{widget_id}?token={access_token}

Recommended Components

| Component | Why | |-----------|-----| | `Button` | Compact action triggers | | `Toggle` | On/off states (saves space) | | `Select` | Dropdowns (vertical space efficient) | | `Plotly` | Self-contained interactive charts |

Communication with Parent

Widget → Parent (via events)

# Widget emits events that parent can receive
# Parent listens for postMessage events
#
# Parent page JavaScript:
# window.addEventListener('message', (e) => {
#     if (e.data.type === 'pywry:event') {
#         console.log(e.data.event_type, e.data.data);
#     }
# });

Parent → Widget (via send_event)

# Server can send events to widget
send_event(widget_id, event_type="parent:message", data={"action": "refresh"})

Embedding Pattern

Basic Embed

<iframe
  src="http://pywry-server:8001/widget/my-chart"
  width="100%"
  height="500"
  frameborder="0"
  loading="lazy">
</iframe>

With Permissions

<iframe
  src="http://pywry-server:8001/widget/my-chart"
  width="100%"
  height="500"
  frameborder="0"
  allow="clipboard-write; downloads"
  sandbox="allow-scripts allow-same-origin">
</iframe>

Code Example

from pywry import PyWry
from pywry.toolbar import Div, Button, Toggle, Toolbar

app = PyWry()

# Compact content for iframe embedding
content = Div(
    content="""
        <div style="padding: 12px;">
            <div id="status" style="margin-bottom: 12px;">Ready</div>
            <div id="chart" style="height: 350px;"></div>
        </div>
    """,
    component_id="embed-widget",
)

# Create embeddable widget
widget = app.show(
    html=conten
Read more
@deeleeramone
Ships withpywry

PyWry is a cross-platform app factory, rendering engine and UI toolkit for Python that produces native desktop, web, and notebook experiences from a single API.

9918 skills1 agent1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
99
Star history of deeleeramone/PyWry on GitHub, a chart by star-history.com

Other skills on pywry.

pywry-orientation

pywry-orientation

When to reach for PyWry MCP tools (native webview rendering, Plotly, TradingView, AgGrid, chat) instead of writing…

@deeleeramone@deeleeramoneInstall Skill
chat_agent

chat_agent

How an agent operates inside a PyWry chat widget — reading user messages, attachments, @-context, tool-call result cards,…

@deeleeramone@deeleeramoneInstall Skill
component_reference

component_reference

**STOP. THIS IS THE AUTHORITATIVE REFERENCE FOR ALL COMPONENTS.** **YOU MUST USE THE EXACT EVENT SIGNATURES DOCUMENTED HERE.**…

@deeleeramone@deeleeramoneInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills11d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc13d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react2d252k
@facebook@facebookInstall Skill

Questions about iframe.

Which coding agents can use iframe?
iframe installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.