Docs menu
DocsAPI reference

Webview

Open real websites in OS-level windows and embed live web pages inside your app layout.

An app runs in a sandboxed iframe, so embedding another site with <iframe src="https://…"> only works for sites that permit framing — any site sending X-Frame-Options: DENY/SAMEORIGIN or a CSP frame-ancestors renders blank, and there is no client-side way around it. To build a browser / kiosk / kid-safe app that opens real sites, declare the webview capability plus a "webviewAllowlist", then open a real top-level window.

// app.json: { "capabilities": ["webview"], "webviewAllowlist": ["wikipedia.org", "khanacademy.org"] }
const { id } = await window.chatoss.webview.open({ url: 'https://en.wikipedia.org', title: 'Wikipedia' });
// …later:
await window.chatoss.webview.close(id);

The window's navigation is locked to your webviewAllowlist at the OS level (enforced in Rust): a click or redirect to any host not on the list is cancelled before it loads — a real firewall your page JS can't widen or escape. Because it's a top-level window (not an iframe), sites that refuse framing load fine. open() rejects if the URL's host isn't on the allowlist. This is the ONLY way to enforce a navigation allowlist — a JS-only allowlist inside an iframe is not a security boundary.

Embedded web views — real web pages INSIDE your app

Same capability + allowlist, but the page renders inside your own layout instead of a separate window — this is how you build in-app browser tabs, embedded dashboards, or doc panes. The easy path is mount(element, {url}), which glues a real web view to one of your DOM elements and keeps it there as the window scrolls/resizes:

// app.json: { "capabilities": ["webview"], "webviewAllowlist": ["wikipedia.org"] }
const box = document.getElementById('viewport');          // any element you sized in your layout
const view = window.chatoss.webview.mount(box, { url: 'https://en.wikipedia.org' });
await view.ready;                                          // resolves to { id }
view.onEvent(({ url, loading }) => { /* update a URL bar / spinner / tab title */ });
await view.navigate('https://en.wikipedia.org/wiki/Cat'); // load another allowed URL
// view.close() removes it and stops tracking.

For finer control (e.g. many tabs sharing one area), use the primitives directly:

const { id } = await window.chatoss.webview.embed({ url, rect: { x, y, width, height } }); // rect = your element's box (CSS px)
await window.chatoss.webview.setBounds({ id, rect });   // call on scroll/resize to keep it glued
await window.chatoss.webview.navigate({ id, url });
await window.chatoss.webview.goBack(id);   // also goForward(id), reload(id)
window.chatoss.webview.on(id, ({ url, loading }) => { /* … */ });
await window.chatoss.webview.close(id);

🔴 The embedded view is a native layer that floats ABOVE your DOM — it does not clip to rounded corners and nothing (menus, modals) can overlay it. Reserve a clear rectangle for it, and hide it (setBounds offscreen, or close) when you show UI on top. Same OS-enforced allowlist firewall as open — an app can never point an embedded view off its webviewAllowlist.