🚧 Glyx is pre-release software. APIs may change before v1.0. Get started →
Documentation
Guides
WebView & Browser Embedding

WebView & Browser Embedding

Glyx uses a purpose-built GPU renderer with no browser engine — fast startup, small binaries, consistent cross-platform rendering. That also means libraries that depend on a real browser (maps, PDF viewers, rich text editors built on contenteditable) don't work out of the box.

<WebView> embeds the native OS browser control (WebView2 on Windows, WKWebView on macOS, WebKitGTK on Linux, via wry (opens in a new tab)) as a real child window, position-tracked to the component's layout rect — the same escape-hatch role Electron's <webview> tag or a Tauri system-webview child window plays.

Requires webview: true in glyx.config.json capabilities (or it's included automatically if capabilities are unspecified). The webview runs as a separate glyx-cap-webview native module, loaded dynamically — not statically linked into glyx-core — matching how audio/camera/AI capabilities are structured.


What needs WebView

Use caseWhy it needs a browser
Interactive maps (Leaflet, Mapbox GL, Google Maps)Depend on WebGL or HTML canvas inside a browser compositor
PDF viewerpdf.js renders into a browser canvas
Portal & intranet pagesHTML pages you or your company own, embedded as-is
Third-party web dashboardsGrafana, Metabase, or any iframe-based embed
Rich text editors (Slate, TipTap, Quill)Built on contenteditable and DOM selection APIs
<video> with DRMRequires browser CDM integration

If the content is your own web app, consider whether it can be rewritten as a native Glyx screen instead — better performance, tighter OS integration. WebView is the right call for content you don't control, or where porting would be impractical.

For simple map-like UIs, Canvas 2D can cover it without a browser at all — static tile grids (ctx.drawImage over fetched PNG tiles), route/heatmap overlays via the path API, or pin/label markers with ctx.fillText. Good for dashboards showing fixed-background data; not a substitute for interactive street maps with full pan/zoom/labels.


Basic usage

import { WebView } from '@glyx-dev/react'
 
<WebView src="https://example.com" style={{ width: 800, height: 600 }} />

Raw HTML, no network required:

<WebView html="<h1>hi</h1>" style={{ flex: 1 }} />

Local files bundled with your app, served over glyx-asset://:

<WebView src="glyx-asset://index.html" assetsRoot="C:/app/assets" style={{ flex: 1 }} />

Props

PropTypeDescription
srcstringURL or glyx-asset:// path to load. Ignored if html is set.
htmlstringRaw HTML to load directly — no network, no navigation.
sandboxboolean (default true)Devtools off, strict navigation. Set false to relax.
allowedOriginsstring[]Navigation allowlist. Omitted/empty = only the initial URL's own origin is navigable — clicking an external link inside the page won't leave that origin.
assetsRootstringEnables glyx-asset://<path> to serve files from this directory.
onMessage(msg: string) => voidCalled when the page sends a message via window.ipc.postMessage(str).
stylelayout styleSame layout system as every other Glyx component — the webview is position-tracked to this rect every frame.

Two-way messaging

Frames/content never cross the JS bridge — only postMessage strings do.

Page → JS: inside the embedded page's own script, call window.ipc.postMessage(str) (injected automatically by wry). It arrives as the onMessage prop's argument.

JS → page: use a ref to call postMessage on the component; the page receives it via the standard window.addEventListener('message', ...).

import { useRef } from 'react'
import { WebView } from '@glyx-dev/react'
 
const PAGE_HTML = `<!doctype html><html><body>
  <button onclick="window.ipc.postMessage('hello from page')">Send</button>
  <script>
    window.addEventListener('message', e => console.log('from JS:', e.data))
  </script>
</body></html>`
 
function App() {
  const ref = useRef(null)
  return (
    <WebView
      ref={ref}
      html={PAGE_HTML}
      onMessage={(msg) => {
        console.log('from page:', msg)
        ref.current.postMessage('hello from JS')
      }}
      style={{ flex: 1 }}
    />
  )
}

Limitations

  • Only postMessage strings cross the bridge — no shared DOM, no direct JS interop between the host app and the embedded page.
  • The webview is a real OS child window composited by the platform, not part of Glyx's own GPU render tree — it always renders on top of Glyx content at its screen position, same constraint every framework with a native webview embed has.
  • allowedOrigins is a navigation allowlist, not a content sandbox — it restricts which origins the page can navigate to, not what the loaded page's own script can do within its origin.