From aaf25b75b7fc067187a83cc6250751de77a7f13a Mon Sep 17 00:00:00 2001
From: DrMelone <27028174+Classic298@users.noreply.github.com>
Date: Sat, 14 Feb 2026 19:32:10 +0100
Subject: [PATCH] Rich UI Docs
---
.../plugin/development/events.mdx | 67 ++++-
.../plugin/development/rich-ui.mdx | 281 ++++++++++++++++--
2 files changed, 328 insertions(+), 20 deletions(-)
diff --git a/docs/features/extensibility/plugin/development/events.mdx b/docs/features/extensibility/plugin/development/events.mdx
index 2fa22a6ff..14de486c4 100644
--- a/docs/features/extensibility/plugin/development/events.mdx
+++ b/docs/features/extensibility/plugin/development/events.mdx
@@ -379,14 +379,14 @@ await __event_emitter__(
### `execute` (**requires** `__event_call__`)
-**Run code dynamically on the user's side:**
+**Run JavaScript directly in the user's browser:**
```python
result = await __event_call__(
{
"type": "execute",
"data": {
- "code": "print(40 + 2);",
+ "code": "return document.title;",
}
}
)
@@ -396,12 +396,73 @@ await __event_emitter__(
"type": "notification",
"data": {
"type": "info",
- "content": f"Code executed, result: {result}"
+ "content": f"Page title: {result}"
}
}
)
```
+#### How It Works
+
+The `execute` event runs JavaScript **directly in the main page context** using `new Function()`. This means:
+
+- It runs with **full access** to the page's DOM, cookies, localStorage, and session
+- It is **not sandboxed** — there are no iframe restrictions
+- It can manipulate the Open WebUI interface directly (show/hide elements, read form data, trigger downloads)
+- The code runs as an async function, so you can use `await` and `return` a value back to the backend
+
+#### Example: Display a Custom Form
+
+```python
+result = await __event_call__(
+ {
+ "type": "execute",
+ "data": {
+ "code": """
+ return new Promise((resolve) => {
+ const overlay = document.createElement('div');
+ overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:9999';
+ overlay.innerHTML = `
+
+
Enter Details
+
+
+
+
+ `;
+ document.body.appendChild(overlay);
+ document.getElementById('exec-submit').onclick = () => {
+ const name = document.getElementById('exec-name').value;
+ const email = document.getElementById('exec-email').value;
+ overlay.remove();
+ resolve({ name, email });
+ };
+ });
+ """
+ }
+ }
+)
+# result will be {"name": "...", "email": "..."}
+```
+
+#### Execute vs Rich UI Embeds
+
+The `execute` event and [Rich UI Embeds](/features/extensibility/plugin/development/rich-ui) are complementary ways to create interactive experiences:
+
+| | `execute` Event | Rich UI Embed |
+|---|---|---|
+| **Runs in** | Main page context (no sandbox) | Sandboxed iframe |
+| **Persistence** | Ephemeral — gone on reload/navigate | Persistent — saved in chat history |
+| **Page access** | Full (DOM, cookies, localStorage) | Isolated from parent by default |
+| **Forms** | Always works (no sandbox) | Requires `allowForms` setting enabled |
+| **Best for** | Transient interactions, side effects, downloads, DOM manipulation | Persistent visual content, dashboards, charts |
+
+Use `execute` for transient interactions (confirmations, custom dialogs, triggering downloads, reading page state) and Rich UI embeds for persistent visual content you want to stay in the conversation.
+
+:::warning
+Because `execute` runs unsandboxed JavaScript in the user's browser session, it has full access to the Open WebUI page context. Only use this in trusted functions — never execute untrusted or user-provided code through this event.
+:::
+
---
## 🏗️ When & Where to Use Events
diff --git a/docs/features/extensibility/plugin/development/rich-ui.mdx b/docs/features/extensibility/plugin/development/rich-ui.mdx
index 719d6775e..f7168410e 100644
--- a/docs/features/extensibility/plugin/development/rich-ui.mdx
+++ b/docs/features/extensibility/plugin/development/rich-ui.mdx
@@ -68,32 +68,279 @@ async def action(self, body, __event_emitter__=None):
return (html, {"Content-Disposition": "inline", "Content-Type": "text/html"})
```
-## Advanced Features
+## Iframe Height and Auto-Sizing
-The embedded iframes support auto-resizing and include configurable security settings. The system automatically handles:
+Rich UI embeds are rendered inside a sandboxed iframe. The iframe needs to know how tall its content is in order to display without scrollbars. There are two mechanisms for this:
-- **Auto-resizing**: Embedded content automatically adjusts height based on its content
-- **Cross-origin communication**: Safe message passing between the iframe and parent window
-- **Security sandbox**: Configurable security restrictions for embedded content
+### postMessage Height Reporting (Recommended)
-## Security Considerations
+When `allowSameOrigin` is **off** (the default), the parent page cannot read the iframe's content height directly. Your HTML must report its own height by posting a message to the parent window:
-When embedding external content, several security options can be configured through the UI settings:
+```html
+
+```
+
+Add this script to the end of your `` in every Rich UI embed. Without it, the iframe will stay at a small default height and your content will be cut off with a scrollbar.
+
+### Same-Origin Auto-Resize
+
+When `allowSameOrigin` is **on** (via the user setting `iframeSandboxAllowSameOrigin`), the parent page can directly measure the iframe's content height and resize it automatically — no script needed in your HTML. However, this comes with security trade-offs (see below).
+
+## Sandbox and Security
+
+Embedded iframes run inside a [sandbox](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/iframe#sandbox). The following sandbox flags are always enabled by default:
+
+- `allow-scripts` — JavaScript execution
+- `allow-popups` — Popups (e.g. window.open)
+- `allow-downloads` — File downloads
+
+Two additional flags can be toggled by the user in **Settings → Interface**:
+
+| Setting | Default | Description |
+|---|---|---|
+| Allow Iframe Same-Origin Access | ❌ Off | Allows the iframe to access parent page context |
+| Allow Iframe Form Submissions | ❌ Off | Allows form submissions within embedded content |
+
+### allowSameOrigin
+
+This is the most important flag to be aware of. It is **off by default** for security reasons.
+
+**When off (default):**
+- The iframe is fully isolated from the parent page
+- It **cannot** read cookies, localStorage, or DOM of the parent
+- The parent **cannot** read the iframe's content height (so you must use the postMessage pattern above)
+- This is the safest option and recommended for most use cases
+
+**When on:**
+- The iframe can interact with the parent page's context
+- Auto-resizing works without any script in your HTML
+- Chart.js and Alpine.js dependencies are automatically injected if detected
+- ⚠️ **Use with caution** — only enable this when you trust the embedded content
-- `iframeSandboxAllowForms`: Allow form submissions within embedded content
-- `iframeSandboxAllowSameOrigin`: Allow same-origin requests (use with caution)
+Users can toggle this setting in **Settings → Interface → Iframe Same-Origin Access**.
+
+## Rendering Position
+
+- **Tool embeds** inside a tool call result render **inline** at the tool call indicator (the "View Result from..." line)
+- **Action embeds** and message-level embeds render **below** the message text content
+
+## Advanced Communication
+
+The iframe and parent window can communicate beyond just height reporting. The following patterns are available:
+
+### Payload Requests
+
+The iframe can request a data payload from the parent. This is useful for passing dynamic data into the embed after it loads:
+
+```html
+
+```
+
+The parent responds with `{ type: 'payload', requestId: ..., payload: ... }` containing the configured payload data.
+
+### Tool Args Injection (Tools Only)
+
+When a **Tool** returns a Rich UI embed, the tool call arguments (the parameters the model passed to the tool) are automatically injected into the iframe's `window.args`. This allows your embedded HTML to access the tool's input:
+
+```html
+
+```
+
+:::note
+This only works for Tool embeds rendered via the tool call display. Action embeds do not have `window.args` since they are triggered by the user, not the model.
+:::
+
+### Auto-Injected Libraries
+
+When `allowSameOrigin` is enabled, the iframe component auto-detects usage of certain libraries in your HTML and injects them automatically — no CDN `
+```
+
+## Rich UI Embeds vs Execute Event
+
+Rich UI embeds and the [`execute` event](/features/extensibility/plugin/development/events#execute-requires-__event_call__) are complementary ways to create interactive experiences. Choose based on your needs:
+
+| | Rich UI Embed | `execute` Event |
+|---|---|---|
+| **Runs in** | Sandboxed iframe | Main page context (no sandbox) |
+| **Persistence** | Persistent — saved in chat history | Ephemeral — gone on reload/navigate |
+| **Page access** | Isolated from parent by default | Full (DOM, cookies, localStorage) |
+| **Forms** | Requires `allowForms` setting enabled | Always works (no sandbox) |
+| **Best for** | Persistent visual content, dashboards, charts | Transient interactions, side effects, downloads, DOM manipulation |
+
+Use Rich UI embeds for persistent visual content you want to stay in the conversation. Use `execute` for transient interactions like custom dialogs, triggering downloads, or reading page state.
## Use Cases
Rich UI embedding is perfect for:
-- **Interactive dashboards**: Real-time data visualization and controls
-- **Form interfaces**: Complex input forms with validation and dynamic behavior
-- **Charts and graphs**: Interactive plotting with libraries like Plotly, D3.js, or Chart.js
-- **Media players**: Video, audio, or interactive media content
-- **Custom widgets**: Specialized UI components for specific tool functionality
-- **External integrations**: Embedding content from external services or APIs
-- **Human-triggered visualizations**: Actions that display results when a user clicks a button, e.g. generating a report or triggering a download
+- **Interactive dashboards** — Real-time data visualization and controls
+- **Charts and graphs** — Interactive plotting with libraries like Plotly, D3.js, or Chart.js
+- **Form interfaces** — Complex input forms with validation and dynamic behavior
+- **Media players** — Video, audio, or interactive media content
+- **Download triggers** — Especially useful for iOS PWA where native download links are blocked
+- **Custom widgets** — Specialized UI components for specific tool functionality
+- **External integrations** — Embedding content from external services or APIs
+- **Human-triggered visualizations** — Actions that display results when a user clicks a button, e.g. generating a report or triggering a download
+
+## Full Sample Action
+
+
+Complete working Sample Action with Rich UI embed
+
+This Action returns a styled card with stats, including the recommended height-reporting script:
+
+```python
+"""
+title: Rich UI Demo Action
+author: open-webui
+version: 0.1.0
+description: Demonstrates Rich UI embedding from an Action function.
+"""
+
+from pydantic import BaseModel, Field
+
+
+class Action:
+ class Valves(BaseModel):
+ pass
+
+ def __init__(self):
+ self.valves = self.Valves()
+
+ async def action(self, body: dict, __user__=None, __event_emitter__=None) -> None:
+ from fastapi.responses import HTMLResponse
+
+ html = """
+
+
+
+
+
+
+
+
Rich UI Embed Demo
+
This embed renders below the message text.
+ Action Embed
+
+
+
42
+
Answers
+
+
+
99%
+
Accuracy
+
+
+
0ms
+
Latency
+
+
+
+
+
+
+ """
+
+ return HTMLResponse(content=html, headers={"Content-Disposition": "inline"})
+```
+
+
## External Tool Example
@@ -123,7 +370,7 @@ async def create_dashboard():
)
```
-The embedded content automatically inherits responsive design and integrates seamlessly with the chat interface, providing a native-feeling experience for users interacting with your tools.
+The embedded content automatically inherits responsive design and integrates seamlessly with the chat interface, providing a native-feeling experience for users interacting with your tools.
## CORS and Direct Tools