Skip to content

[FEATURE] Add iframe() Component to Embed External Web Apps in Preswald #535

@amrutha97

Description

@amrutha97

Goal

Introduce a new iframe() component that allows developers to embed external web applications, tools, dashboards, or content directly within a Preswald app layout using an <iframe>.


📌 Motivation

Many teams use Preswald alongside other tools—BI dashboards, model explainers, internal portals, notebooks, etc. Being able to embed these external apps directly inside a Preswald app via iframe unlocks:

  • Seamless multi-tool integration
  • Side-by-side app views
  • Embedding internal web apps or dashboards (Grafana, Metabase, MLflow, etc.)
  • Embedded documentation, YouTube tutorials, or interactive notebooks

This makes Preswald a composable dashboard shell.


✅ Acceptance Criteria

  • Add a new iframe() component to preswald.interfaces.components
  • Accept props:
    • url: str — required external source
    • height: Union[int, str] — default: "500px"
    • width: Union[int, str] — default: "100%"
    • title: Optional[str] — optional accessible title
  • Render as a <Card> with an embedded <iframe> in the frontend
  • Handle sandboxing / security via attribute defaults
  • Display error if URL is invalid or blocked (e.g. X-Frame-Options: deny)

🛠 Implementation Plan

1. Python API: iframe() in components.py

def iframe(url: str, height: str = "500px", width: str = "100%", title: str = ""):
    service = PreswaldService.get_instance()
    component = {
        "type": "iframe",
        "id": f"iframe-{hashlib.md5(url.encode()).hexdigest()[:6]}",
        "url": url,
        "height": height,
        "width": width,
        "title": title,
    }
    service.append_component(component)

2. Frontend Widget: IframeWidget.jsx

import React from 'react';
import { Card } from '@/components/ui/card';

const IframeWidget = ({ url, height, width, title }) => {
  return (
    <Card className="mb-4 p-2 overflow-hidden">
      <iframe
        src={url}
        title={title || "Embedded Frame"}
        style={{ width: width || "100%", height: height || "500px", border: "none" }}
        loading="lazy"
        allowFullScreen
        sandbox="allow-scripts allow-same-origin"
      />
    </Card>
  );
};

export default IframeWidget;

3. Register in DynamicComponents.jsx

import IframeWidget from '@/components/widgets/IframeWidget';

case 'iframe':
  return (
    <IframeWidget
      url={component.url}
      height={component.height}
      width={component.width}
      title={component.title}
    />
  );

🧪 Testing Plan

  • Embed:
    • YouTube video
    • Hugging Face demo
    • Internal dashboard URL
  • Test with:
    iframe("https://huggingface.co/spaces/yuntian-deng/ChatGPT", height="600px")
  • Validate:
    • Renders correctly
    • Respects sizing
    • Does not crash if blocked (show fallback message)

📚 Docs To Add

  • docs/sdk/iframe.mdx
  • Add security note: some websites block embedding via X-Frame-Options
  • Show how to embed Grafana, JupyterLite, etc.

🧩 Files Affected

  • preswald/interfaces/components.py
  • frontend/src/components/widgets/IframeWidget.jsx
  • DynamicComponents.jsx
  • docs/sdk/iframe.mdx

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions