DEV Community

Rehan Mazhar
Rehan Mazhar

Posted on

R1: A Runtime That Runs Tauri Apps in the Browser

Live demo: https://r1-todo-demo.netlify.app/
Source code: https://github.com/12errh/r1-tauriweb-runtime-v1

The Problem

Tauri is a fantastic framework for building desktop apps with a Rust backend and a web frontend. But sharing a Tauri app with someone still means sending them an installer, asking them to trust an executable, and waiting for them to download 50–100MB. For a lot of use cases — demos, quick tools, apps for non-technical users — that friction kills adoption.

What if you could just send someone a URL?

What R1 Does

R1 is a browser-native runtime for Tauri. You take your existing Tauri project, add one line to your vite.config.ts, run npm run build, and the output is a static folder. Deploy it to Vercel, Netlify, or GitHub Pages. Anyone with a browser can now run your app instantly.

No server. No backend. No installer. Just a URL.

How It Works

The browser is a sandbox — you can't run a native Rust binary inside it. R1 solves this with a layered architecture:

1. Rust → WASM
The R1 Vite plugin automatically compiles your src-tauri/ directory to WebAssembly using wasm-pack during npm run build. The .wasm binary is bundled as a static asset.

2. Kernel Worker
All WASM execution happens in a dedicated Web Worker — never the main thread. If your Rust code blocks for 2 seconds processing a file, the UI stays perfectly responsive.

3. WASI Shim
When Rust is compiled to wasm32-wasi, every std::fs call becomes a WASI syscall. R1 intercepts those syscalls and redirects them to the browser's Origin Private File System (OPFS). Your Rust code calls std::fs::write("/data/todos.json", ...) exactly as it would on a desktop — it just happens to write to OPFS instead of a real disk. Files persist across page refreshes.

4. IPC Bridge
Real Tauri injects window.__TAURI_INTERNALS__ into your app's WebView. R1 patches the same global at boot time. Your frontend calls invoke('add_todo', { text: 'Buy milk' }) exactly as it always did. R1 routes that call through the Kernel Worker to your WASM backend.

5. Event Bridge
Rust can emit events back to JS using r1::emit("progress", payload). These arrive at your listen() handlers in the frontend — the same API you use in a real Tauri app.

6. Service Worker
Asset URLs (convertFileSrc('/app/logo.png')) are intercepted by a Service Worker that reads the file from OPFS and serves it with the correct MIME type.

Current Status

  • invoke() — Tauri v1 and v2 compatible
  • std::fs read/write from Rust (via WASI → OPFS)
  • ✅ Rust → JS event bridge
  • ✅ Serde JSON bridge for complex data
  • ✅ WASM panic isolation
  • ✅ Tauri API plugins: fs, event, store, os, path, dialog, clipboard
  • ✅ Virtual Window Manager (macOS, Windows 11, Linux themes)
  • ✅ 31/31 unit tests passing
  • ✅ Live todo demo running on Netlify

Limitations (Being Honest)

  • The WASI shim covers the most common syscalls but not all of them
  • shell execute is stubbed — you can't spawn real child processes in a browser
  • Heavy native dependencies that can't compile to WASM won't work
  • Best suited for simple to medium complexity Tauri apps right now

Try It

The live demo is a real Tauri todo app — written as a standard desktop Tauri project, then compiled through R1. Add a todo, close the tab, reopen it. The data persists.

If you have a simple Tauri app, I'd love for you to try running it through R1 and see what breaks. That feedback is exactly what drives the next version.

The full architecture roadmap that guided the build is in the repository if you want to understand the design decisions.

Top comments (0)