integrating-tauri-rust-frontends by dchuk/claude-code-tauri-skills
npx skills add https://github.com/dchuk/claude-code-tauri-skills --skill integrating-tauri-rust-frontends此技能涵盖将基于 Rust 的前端框架与 Tauri v2 集成,以构建使用 WASM 的桌面和移动应用程序。
| 框架 | 描述 | 打包工具 |
|---|---|---|
| Leptos | 用于构建 Web UI 的响应式 Rust 框架 | Trunk |
| Yew | 基于组件的 Rust 框架 | Trunk |
| Dioxus | 跨平台 UI 框架 | Trunk |
| Sycamore | Rust 的响应式库 | Trunk |
所有 Rust/WASM 前端都使用 Trunk 作为打包工具/开发服务器。
window.__TAURI__ 和 wasm-bindgen 访问 Tauri API。广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
ws_protocol = "ws" 以在移动开发中启用热重载。my-tauri-app/
├── src/
│ ├── main.rs # Rust 前端入口点
│ └── app.rs # 应用程序组件
├── src-tauri/
│ ├── src/
│ │ └── main.rs # Tauri 后端
│ ├── Cargo.toml # Tauri 依赖项
│ └── tauri.conf.json # Tauri 配置
├── index.html # Trunk 的 HTML 入口点
├── Cargo.toml # 前端依赖项
├── Trunk.toml # Trunk 打包工具配置
└── dist/ # 构建输出(生成)
{
"build": {
"beforeDevCommand": "trunk serve",
"devUrl": "http://localhost:1420",
"beforeBuildCommand": "trunk build",
"frontendDist": "../dist"
},
"app": {
"withGlobalTauri": true
}
}
关键设置:
beforeDevCommand: 在 Tauri 之前运行 Trunk 开发服务器devUrl: Trunk 服务前端的 URL(默认:Leptos 为 1420,普通 Trunk 为 8080)beforeBuildCommand: 在打包前构建 WASM 包frontendDist: 构建的前端资源路径withGlobalTauri: WASM 必需 - 暴露 window.__TAURI__ 以供 API 访问[build]
target = "./index.html"
dist = "./dist"
[watch]
ignore = ["./src-tauri"]
[serve]
port = 1420
open = false
[serve.ws]
ws_protocol = "ws"
关键设置:
target: 包含 Trunk 指令的 HTML 入口点ignore: 防止监视 Tauri 后端更改port: 必须与 tauri.conf.json 中的 devUrl 匹配open = false: 防止浏览器自动打开(Tauri 处理显示)ws_protocol = "ws": 移动端热重载必需[package]
name = "my-app-frontend"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
# 核心 WASM 依赖项
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
web-sys = { version = "0.3", features = ["Window", "Document"] }
# 用于 WASM 的 Tauri API 绑定
tauri-wasm = { version = "2", features = ["all"] }
# 选择你的框架:
# 对于 Leptos:
leptos = { version = "0.6", features = ["csr"] }
# 对于 Yew:
# yew = { version = "0.21", features = ["csr"] }
# 对于 Dioxus:
# dioxus = { version = "0.5", features = ["web"] }
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"
关键设置:
crate-type = ["cdylib", "rlib"]: WASM 编译必需tauri-wasm: 提供对 Tauri API 的 Rust 绑定features = ["csr"]: 框架的客户端渲染<!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 指令:
data-trunk rel="css": 包含 CSS 文件data-trunk rel="rust": 将 Rust crate 编译为 WASMdata-wasm-opt="z": 针对大小进行优化[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
use leptos::*;
mod app;
use app::App;
fn main() {
console_error_panic_hook::set_once();
mount_to_body(|| view! { <App /> });
}
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>
}
}
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>
}
}
在 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");
}
# 安装 Trunk
cargo install trunk
# 添加 WASM 目标
rustup target add wasm32-unknown-unknown
# 开发(运行 Trunk + Tauri)
cd src-tauri && cargo tauri dev
# 构建生产版本
cd src-tauri && cargo tauri build
# 仅 Trunk(用于前端调试)
trunk serve --port 1420
# 仅构建 WASM
trunk build --release
对于移动平台,需要额外的配置:
[serve]
port = 1420
open = false
address = "0.0.0.0" # 监听所有接口以供移动端访问
[serve.ws]
ws_protocol = "ws" # 移动端热重载必需
{
"build": {
"beforeDevCommand": "trunk serve --address 0.0.0.0",
"devUrl": "http://YOUR_LOCAL_IP:1420"
}
}
将 YOUR_LOCAL_IP 替换为你机器的本地 IP(例如 192.168.1.100)。
use wasm_bindgen::prelude::*;
use serde::{Serialize, Deserialize};
#[wasm_bindgen]
extern "C" {
// 核心调用
#[wasm_bindgen(js_namespace = ["window", "__TAURI__", "core"], catch)]
async fn invoke(cmd: &str, args: JsValue) -> Result<JsValue, JsValue>;
// 事件系统
#[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);
}
// 用法
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())
}
use tauri_wasm::api::{core, event, dialog, fs};
// 调用命令
let result: MyResponse = core::invoke("my_command", &my_args).await?;
// 监听事件
event::listen("my-event", |payload| {
// 处理事件
}).await;
// 触发事件
event::emit("my-event", &payload).await;
// 文件对话框
let file = dialog::open(dialog::OpenDialogOptions::default()).await?;
// 文件系统(需要权限)
let contents = fs::read_text_file("path/to/file").await?;
withGlobalTauri: truewasm32-unknown-unknown 目标ws_protocol = "ws"address = "0.0.0.0" 以供移动端访问withGlobalTauri 必须为 truewindow.__TAURI__ 是否存在opt-level = "z" 进行大小优化lto = true 的 LTOwasm-opt 后处理crate-type = ["cdylib", "rlib"]data-trunk 指令| 组件 | 版本 |
|---|---|
| Tauri | 2.x |
| Trunk | 0.17+ |
| Leptos | 0.6+ |
| wasm-bindgen | 0.2.x |
| tauri-wasm | 2.x |
始终将 tauri-wasm 版本与你的 Tauri 版本匹配。
每周安装次数
73
仓库
GitHub 星标数
12
首次出现
2026年1月24日
安全审计
安装于
opencode63
gemini-cli62
codex61
github-copilot55
cursor55
claude-code55
This skill covers integrating Rust-based frontend frameworks with Tauri v2 for building desktop and mobile applications with WASM.
| 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.
window.__TAURI__ and wasm-bindgen.ws_protocol = "ws" for hot-reload on mobile development.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)
{
"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 - Exposes window.__TAURI__ for API access[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 match devUrl in tauri.conf.jsonopen = false: Prevents browser auto-open (Tauri handles display)ws_protocol = "ws": Required for mobile hot-reload[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<!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[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
use leptos::*;
mod app;
use app::App;
fn main() {
console_error_panic_hook::set_once();
mount_to_body(|| view! { <App /> });
}
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>
}
}
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>
}
}
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");
}
# 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
For mobile platforms, additional configuration is needed:
[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
{
"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).
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())
}
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?;
withGlobalTauri: true in tauri.conf.jsonwasm32-unknown-unknown target is installedws_protocol = "ws" in Trunk.tomladdress = "0.0.0.0" for mobile accesswithGlobalTauri must be truewindow.__TAURI__ exists in browser consoleopt-level = "z" for size optimizationlto = truewasm-opt post-processingcrate-type = ["cdylib", "rlib"]data-trunk directives| 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.
Weekly Installs
73
Repository
GitHub Stars
12
First Seen
Jan 24, 2026
Security Audits
Gen Agent Trust HubFailSocketPassSnykPass
Installed on
opencode63
gemini-cli62
codex61
github-copilot55
cursor55
claude-code55
Rust并发编程指南:Send/Sync、线程与异步编程最佳实践
671 周安装