integrating-tauri-rust-frontends

Tauri Rust/WASM Frontend Integration

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "integrating-tauri-rust-frontends" with this command: npx skills add dchuk/claude-code-tauri-skills/dchuk-claude-code-tauri-skills-integrating-tauri-rust-frontends

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 and wasm-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 Tauri

  • devUrl : URL where Trunk serves the frontend (default: 1420 for Leptos, 8080 for plain Trunk)

  • beforeBuildCommand : Builds WASM bundle before packaging

  • frontendDist : Path to built frontend assets

  • withGlobalTauri : Required for WASM - Exposes window.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 directives

  • ignore : Prevents watching Tauri backend changes

  • port : Must match devUrl in tauri.conf.json

  • open = 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 compilation

  • tauri-wasm : Provides Rust bindings to Tauri APIs

  • features = ["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 files

  • data-trunk rel="rust" : Compile Rust crate to WASM

  • data-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(&#x26;args).unwrap();
        let result = invoke("greet", args_js).await;
        let greeting: String = serde_wasm_bindgen::from_value(result).unwrap();
        set_message.set(greeting);
    });
};

view! {
    &#x3C;main>
        &#x3C;h1>"Welcome to Tauri + Leptos"&#x3C;/h1>
        &#x3C;button on:click=greet>"Greet"&#x3C;/button>
        &#x3C;p>{message}&#x3C;/p>
    &#x3C;/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", &#x26;serde_json::json!({ "name": "World" }))
            .await
            .unwrap();
        set_message.set(result);
    });
};

view! {
    &#x3C;main>
        &#x3C;button on:click=greet>"Greet"&#x3C;/button>
        &#x3C;p>{message}&#x3C;/p>
    &#x3C;/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: &#x26;str, handler: &#x26;Closure&#x3C;dyn Fn(JsValue)>) -> JsValue;

#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "event"])]
async fn emit(event: &#x26;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: true in tauri.conf.json

  • Check browser console for WASM errors

  • Ensure wasm32-unknown-unknown target 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

  • withGlobalTauri must be true

  • 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-opt post-processing

Trunk build fails

  • Check Cargo.toml has crate-type = ["cdylib", "rlib"]

  • Verify index.html has correct data-trunk directives

  • 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.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

integrating-tauri-js-frontends

No summary provided by upstream source.

Repository SourceNeeds Review
-138
dchuk
Coding

configuring-tauri-permissions

No summary provided by upstream source.

Repository SourceNeeds Review
-116
dchuk
Coding

understanding-tauri-architecture

No summary provided by upstream source.

Repository SourceNeeds Review