Concepts
This page explains the moving parts so you can reason about behaviour, security and debugging.
The big picture
Section titled “The big picture”When you call start_remote_ui, the plugin starts a small HTTP server inside
your Tauri process. That server:
- serves your built frontend assets (the same bundle the native webview loads), and
- upgrades a
/remote_ui_wsconnection to a WebSocket used as an IPC bridge.
A remote browser loads the served frontend. Because the page is now running
outside the Tauri webview, the tauri-remote-ui shims detect the missing native
IPC and route invoke / listen over the WebSocket back to the host, which
executes them against the real webview window.
┌──────────────────────────┐ ┌─────────────────────────────┐│ Remote browser / tester │ │ Tauri host application ││ │ │ ││ your frontend bundle │ HTTP │ RpcServer (hyper) ││ tauri-remote-ui shims │ <─────> │ ├─ serves assets ││ │ WS │ ├─ /remote_ui_ws bridge ││ invoke() ───────────────┼────────►│ └─ runs cmд in webview ││ listen() ◄──────────────┼─────────┤ EmitterExt::emit │└──────────────────────────┘ └─────────────────────────────┘The IPC bridge
Section titled “The IPC bridge”invoke
Section titled “invoke”tauri-remote-ui/api/core#invoke first checks for a native Tauri runtime
(window.__TAURI_INTERNALS__). When present (desktop), it delegates to the real
@tauri-apps/api invoke. Otherwise it:
- opens the WebSocket (once) and waits for it to be ready,
- sends a JSON message
{ id, cmd, args, options }with a monotonic id, - resolves the returned promise when the matching response arrives, or rejects after a 30-second timeout.
On the host, the request is executed against the primary webview window and the result (or error) is emitted back over the socket.
listen
Section titled “listen”tauri-remote-ui/api/event#listen behaves the same way: native listener on
desktop, WebSocket-backed EventTarget in the browser. To make an event reach
remote clients, emit it with EmitterExt
— its async emit sends over the socket and through Tauri’s normal event
system.
Connection lifecycle
Section titled “Connection lifecycle”- Handshake — on open, the client sends
version:<npm version>; the host replies with its crate version. A mismatch logs a warning (behaviour is undefined across mismatched releases). - Heartbeat — the client sends
pingevery 10 seconds; the host repliespong. Round-trip latency is tracked and exposed aslatencyMs; values above 200 ms log a warning. - Disconnect — when the socket closes, the browser navigates to
/remote_ui_disconnect(the disconnect screen).
Host screens
Section titled “Host screens”While a session is active, the native window shows one of two things:
- Blocking screen (default) — replaces the app UI with a notice listing
every reachable URL plus the info URL. Restart the app or stop the server to
resume. Customise it with
set_custom_blocking_ui. - Application UI kept active — if you call
enable_application_ui, the native window stays usable and navigates alongside the remote client instead of being blocked.
HTTP endpoints
Section titled “HTTP endpoints”| Path | Purpose |
|---|---|
/ (and your asset paths) | Serves the built frontend bundle. |
/remote_ui_ws | WebSocket upgrade for the IPC bridge. |
/remote_ui_info | Connection info endpoint (disable with disable_info_url). |
/remote_ui_disconnect | Disconnect screen the browser is sent to on close. |
Where assets come from
Section titled “Where assets come from”The server serves static assets from, in order of preference:
- the
bundle_pathyou set, - otherwise the Tauri-configured
frontendDist, - falling back to
../dist.