Getting Started
tauri-remote-ui has two halves that work together:
- a Rust crate (
tauri-remote-ui) that registers the Tauri plugin and runs the HTTP/WebSocket server, and - an npm package (
tauri-remote-ui) that providesinvoke/listenshims for your frontend.
| Resource | Link |
|---|---|
| Crate | crates.io/crates/tauri-remote-ui |
| API docs (Rust) | docs.rs/tauri-remote-ui |
| npm package | npmjs.com/package/tauri-remote-ui |
| Source & issues | github.com/DraviaVemal/tauri-remote-ui |
Requirements
- Tauri 2.x
- Rust 1.77.2 or newer
- A bundled frontend (the plugin serves your built assets, e.g.
frontendDist)
1. Install the packages
-
Add the Rust crate to your Tauri app (
src-tauri):Terminal window cargo add tauri-remote-ui -
Add the npm package to your frontend:
Terminal window npm install tauri-remote-uiTerminal window yarn add tauri-remote-uiTerminal window pnpm add tauri-remote-ui
2. Register the plugin
Add the plugin in your Tauri builder, then expose commands to start and stop the remote UI server from the frontend.
use tauri::AppHandle;use tauri_remote_ui::{RemoteUiConfig, RemoteUiExt};
#[tauri::command]async fn enable_server(app: AppHandle) -> Result<(), String> { app.start_remote_ui(RemoteUiConfig::default().set_port(Some(9090))) .await .map_err(|e| e.to_string())}
#[tauri::command]async fn disable_server(app: AppHandle) -> Result<(), String> { app.stop_remote_ui().await.map_err(|e| e.to_string())}
pub fn run() { tauri::Builder::default() .plugin(tauri_remote_ui::init()) .invoke_handler(tauri::generate_handler![enable_server, disable_server]) .run(tauri::generate_context!()) .expect("error while running tauri application");}3. Use the frontend shims
Replace @tauri-apps/api imports with the matching tauri-remote-ui entry
points. The shims call native Tauri IPC when inside the webview and
transparently fall back to a WebSocket transport when loaded through a browser.
import { invoke } from "tauri-remote-ui/api/core";import { listen } from "tauri-remote-ui/api/event";
await listen<{ result: number }>("counter", (event) => { console.log("counter is now", event.payload.result);});
await invoke("increment");4. Emit events from Rust
To deliver events to both the native window and any connected remote client,
import EmitterExt and call its async
emit on a WebviewWindow:
use tauri::Manager;use tauri_remote_ui::EmitterExt;
let window = app.get_webview_window("main").unwrap();window.emit("counter", serde_json::json!({ "result": 42 })).await?;5. Develop with a dev server (optional)
When running a frontend dev server (Vite, etc.), proxy the plugin's endpoints so the browser can reach the running Tauri host:
export default defineConfig({ server: { port: 3001, proxy: { "/remote_ui_info": { target: "http://localhost:9090", changeOrigin: true }, "/remote_ui_disconnect": { target: "http://localhost:9090", changeOrigin: true }, "/remote_ui_ws": { target: "http://localhost:9090", changeOrigin: true, ws: true }, }, },});What happens next
- Start your app and trigger
enable_server. - The native window shows a blocking screen listing every URL the server is reachable on (unless you keep the app UI active).
- Open one of those URLs in a browser — your app loads and is fully interactive.
- Closing the tab redirects to the disconnect screen; restart or call
disable_serverto resume normal operation.
Continue with Concepts to understand how the bridge works, or jump to Configuration and Security.
Have an idea or question? Start a thread in the Discussion Channel.