Agent skill
integrating-tauri-rust-frontends
Guides the user through integrating Rust-based WASM frontend frameworks with Tauri v2, covering Leptos and Trunk setup, WASM compilation configuration, Cargo.toml dependencies, Trunk.toml bundler settings, and withGlobalTauri API access.
Install this agent skill to your Project
npx add-skill https://github.com/dchuk/claude-code-tauri-skills/tree/main/tauri/tauri-frontend-rust
SKILL.md
Tauri Rust/WASM Frontend Integration
This skill covers integrating Rust-based frontend frameworks with Tauri v2 for building desktop and mobile applications with WASM.
Supported Frameworks
| Framework | Description | Bundler |
|---|---|---|
| Leptos | Reactive Rust framework for building web UIs | Trunk |
| Yew | Component-based Rust framework | Trunk |
| Dioxus | Cross-platform UI framework | Trunk |
| Sycamore | Reactive library for Rust | Trunk |
All Rust/WASM frontends use Trunk as the bundler/dev server.
Critical Requirements
- Static Site Generation (SSG) Only - Tauri does not support server-based solutions (SSR). Use SSG, SPA, or MPA approaches.
- withGlobalTauri - Must be enabled for WASM frontends to access Tauri APIs via
window.__TAURI__andwasm-bindgen. - WebSocket Protocol - Configure
ws_protocol = "ws"for hot-reload on mobile development.
Project Structure
my-tauri-app/
├── src/
│ ├── main.rs # Rust frontend entry point
│ └── app.rs # Application component
├── src-tauri/
│ ├── src/
│ │ └── main.rs # Tauri backend
│ ├── Cargo.toml # Tauri dependencies
│ └── tauri.conf.json # Tauri configuration
├── index.html # HTML entry point for Trunk
├── Cargo.toml # Frontend dependencies
├── Trunk.toml # Trunk bundler configuration
└── dist/ # Build output (generated)
Configuration Files
Tauri Configuration (src-tauri/tauri.conf.json)
{
"build": {
"beforeDevCommand": "trunk serve",
"devUrl": "http://localhost:1420",
"beforeBuildCommand": "trunk build",
"frontendDist": "../dist"
},
"app": {
"withGlobalTauri": true
}
}
Key settings:
beforeDevCommand: Runs Trunk dev server before TauridevUrl: URL where Trunk serves the frontend (default: 1420 for Leptos, 8080 for plain Trunk)beforeBuildCommand: Builds WASM bundle before packagingfrontendDist: Path to built frontend assetswithGlobalTauri: Required for WASM - Exposeswindow.__TAURI__for API access
Trunk Configuration (Trunk.toml)
[build]
target = "./index.html"
dist = "./dist"
[watch]
ignore = ["./src-tauri"]
[serve]
port = 1420
open = false
[serve.ws]
ws_protocol = "ws"
Key settings:
target: HTML entry point with Trunk directivesignore: Prevents watching Tauri backend changesport: Must matchdevUrlin tauri.conf.jsonopen = false: Prevents browser auto-open (Tauri handles display)ws_protocol = "ws": Required for mobile hot-reload
Frontend Cargo.toml (Root)
[package]
name = "my-app-frontend"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
# Core WASM dependencies
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
web-sys = { version = "0.3", features = ["Window", "Document"] }
# Tauri API bindings for WASM
tauri-wasm = { version = "2", features = ["all"] }
# Choose your framework:
# For Leptos:
leptos = { version = "0.6", features = ["csr"] }
# For Yew:
# yew = { version = "0.21", features = ["csr"] }
# For Dioxus:
# dioxus = { version = "0.5", features = ["web"] }
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
Key settings:
crate-type = ["cdylib", "rlib"]: Required for WASM compilationtauri-wasm: Provides Rust bindings to Tauri APIsfeatures = ["csr"]: Client-side rendering for framework- Release profile optimized for small WASM binary size
HTML Entry Point (index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>My Tauri App</title>
<link data-trunk rel="css" href="styles.css" />
</head>
<body>
<div id="app"></div>
<link data-trunk rel="rust" href="." data-wasm-opt="z" />
</body>
</html>
Trunk directives:
data-trunk rel="css": Include CSS filesdata-trunk rel="rust": Compile Rust crate to WASMdata-wasm-opt="z": Optimize for size
Leptos Setup
Leptos-Specific Cargo.toml
[package]
name = "my-leptos-app"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
leptos = { version = "0.6", features = ["csr"] }
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
console_error_panic_hook = "0.1"
tauri-wasm = { version = "2", features = ["all"] }
[profile.release]
opt-level = "z"
lto = true
Leptos Main Entry (src/main.rs)
use leptos::*;
mod app;
use app::App;
fn main() {
console_error_panic_hook::set_once();
mount_to_body(|| view! { <App /> });
}
Leptos App Component (src/app.rs)
use leptos::*;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::spawn_local;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"])]
async fn invoke(cmd: &str, args: JsValue) -> JsValue;
}
#[component]
pub fn App() -> impl IntoView {
let (message, set_message) = create_signal(String::new());
let greet = move |_| {
spawn_local(async move {
let args = serde_json::json!({ "name": "World" });
let args_js = serde_wasm_bindgen::to_value(&args).unwrap();
let result = invoke("greet", args_js).await;
let greeting: String = serde_wasm_bindgen::from_value(result).unwrap();
set_message.set(greeting);
});
};
view! {
<main>
<h1>"Welcome to Tauri + Leptos"</h1>
<button on:click=greet>"Greet"</button>
<p>{message}</p>
</main>
}
}
Alternative: Using tauri-wasm Crate
use leptos::*;
use tauri_wasm::api::core::invoke;
#[component]
pub fn App() -> impl IntoView {
let (message, set_message) = create_signal(String::new());
let greet = move |_| {
spawn_local(async move {
let result: String = invoke("greet", &serde_json::json!({ "name": "World" }))
.await
.unwrap();
set_message.set(result);
});
};
view! {
<main>
<button on:click=greet>"Greet"</button>
<p>{message}</p>
</main>
}
}
Tauri Backend Command
In src-tauri/src/main.rs:
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Development Commands
# Install Trunk
cargo install trunk
# Add WASM target
rustup target add wasm32-unknown-unknown
# Development (runs Trunk + Tauri)
cd src-tauri && cargo tauri dev
# Build for production
cd src-tauri && cargo tauri build
# Trunk only (for frontend debugging)
trunk serve --port 1420
# Build WASM only
trunk build --release
Mobile Development
For mobile platforms, additional configuration is needed:
Trunk.toml for Mobile
[serve]
port = 1420
open = false
address = "0.0.0.0" # Listen on all interfaces for mobile
[serve.ws]
ws_protocol = "ws" # Required for mobile hot-reload
tauri.conf.json for Mobile
{
"build": {
"beforeDevCommand": "trunk serve --address 0.0.0.0",
"devUrl": "http://YOUR_LOCAL_IP:1420"
}
}
Replace YOUR_LOCAL_IP with your machine's local IP (e.g., 192.168.1.100).
Accessing Tauri APIs from WASM
Method 1: Direct wasm-bindgen (Recommended for control)
use wasm_bindgen::prelude::*;
use serde::{Serialize, Deserialize};
#[wasm_bindgen]
extern "C" {
// Core invoke
#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"], catch)]
async fn invoke(cmd: &str, args: JsValue) -> Result<JsValue, JsValue>;
// Event system
#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "event"])]
async fn listen(event: &str, handler: &Closure<dyn Fn(JsValue)>) -> JsValue;
#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "event"])]
async fn emit(event: &str, payload: JsValue);
}
// Usage
async fn call_backend() -> Result<String, String> {
let args = serde_wasm_bindgen::to_value(&serde_json::json!({
"path": "/some/path"
})).map_err(|e| e.to_string())?;
let result = invoke("read_file", args)
.await
.map_err(|e| format!("{:?}", e))?;
serde_wasm_bindgen::from_value(result)
.map_err(|e| e.to_string())
}
Method 2: Using tauri-wasm Crate
use tauri_wasm::api::{core, event, dialog, fs};
// Invoke command
let result: MyResponse = core::invoke("my_command", &my_args).await?;
// Listen to events
event::listen("my-event", |payload| {
// Handle event
}).await;
// Emit events
event::emit("my-event", &payload).await;
// File dialogs
let file = dialog::open(dialog::OpenDialogOptions::default()).await?;
// File system (requires permissions)
let contents = fs::read_text_file("path/to/file").await?;
Troubleshooting
WASM not loading
- Verify
withGlobalTauri: truein tauri.conf.json - Check browser console for WASM errors
- Ensure
wasm32-unknown-unknowntarget is installed
Hot-reload not working on mobile
- Set
ws_protocol = "ws"in Trunk.toml - Use
address = "0.0.0.0"for mobile access - Verify firewall allows connections on dev port
Tauri APIs undefined
withGlobalTaurimust betrue- Check
window.__TAURI__exists in browser console - Verify tauri-wasm version matches Tauri version
Large WASM binary size
- Enable release profile optimizations
- Use
opt-level = "z"for size optimization - Enable LTO with
lto = true - Consider
wasm-optpost-processing
Trunk build fails
- Check Cargo.toml has
crate-type = ["cdylib", "rlib"] - Verify index.html has correct
data-trunkdirectives - Ensure no server-side features enabled in framework
Version Compatibility
| Component | Version |
|---|---|
| Tauri | 2.x |
| Trunk | 0.17+ |
| Leptos | 0.6+ |
| wasm-bindgen | 0.2.x |
| tauri-wasm | 2.x |
Always match tauri-wasm version with your Tauri version.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
understanding-tauri-ipc
Teaches the assistant about Tauri IPC (Inter-Process Communication) patterns including brownfield and isolation approaches for secure message passing between frontend and Rust backend.
testing-tauri-apps
Guides developers through testing Tauri applications including unit testing with mock runtime, mocking Tauri APIs, WebDriver end-to-end testing with Selenium and WebdriverIO, and CI integration with GitHub Actions.
migrating-tauri-apps
Assists users with migrating Tauri applications from v1 to v2 stable, and from v2 beta to v2 stable, covering breaking changes, configuration updates, API migrations, and plugin system changes.
developing-tauri-plugins
Guides the user through Tauri plugin development, including creating plugin extensions, configuring permissions, and building mobile plugins for iOS and Android platforms.
managing-tauri-plugin-permissions
Guides users through configuring Tauri plugin permissions, capabilities, and security. Covers platform-specific capabilities, window-targeted permissions, using official and community plugin permissions, and writing custom plugin permissions with scopes.
optimizing-tauri-binary-size
Guides users through Tauri binary size optimization techniques to produce small, efficient desktop application bundles using Cargo profile settings and build configurations.
Didn't find tool you were looking for?