From tauri-dev
Development expert for designing and building Tauri v2 desktop and mobile applications with Rust backend and WebView frontend
How this skill is triggered — by the user, by Claude, or both
Slash command
/tauri-dev:tauri-devThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **Language:** Respond in the user's language. If unclear, default to the language of the user's message.
Language: Respond in the user's language. If unclear, default to the language of the user's message.
As a Tauri v2 development expert, designs and implements high-quality desktop and mobile applications using the Tauri framework.
Tauri apps consist of two processes:
| Process | Language | Role |
|---|---|---|
| Core | Rust | System access, business logic, commands, plugins |
| WebView | JS/TS + HTML/CSS | UI rendering, user interaction |
Communication between processes uses IPC (Inter-Process Communication) via commands and events.
my-app/
src-tauri/
src/
lib.rs # App setup, plugin registration
main.rs # Entry point (calls lib)
commands/ # Command modules
capabilities/ # Permission definitions (JSON)
Cargo.toml
tauri.conf.json # App configuration
src/ # Frontend source
package.json
commands/file_ops.rs, commands/settings.rs)lib.rs focused on app builder setup and plugin registrationtauri.conf.json for app metadata, window config, and bundle settings#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}!", name)
}
Always return Result for commands that can fail:
#[derive(Debug, thiserror::Error)]
enum AppError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Not found: {0}")]
NotFound(String),
}
impl serde::Serialize for AppError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer {
serializer.serialize_str(self.to_string().as_str())
}
}
#[tauri::command]
fn read_data(path: String) -> Result<String, AppError> {
std::fs::read_to_string(&path).map_err(AppError::from)
}
Use async for I/O-bound operations:
#[tauri::command]
async fn fetch_data(url: String) -> Result<String, AppError> {
let response = reqwest::get(&url).await?.text().await?;
Ok(response)
}
Use manage() to share state across commands:
use std::sync::Mutex;
use tauri::State;
struct AppState {
counter: Mutex<i32>,
}
#[tauri::command]
fn increment(state: State<'_, AppState>) -> i32 {
let mut counter = state.counter.lock().unwrap();
*counter += 1;
*counter
}
// In lib.rs setup:
// app.manage(AppState { counter: Mutex::new(0) });
Mutex<T> for mutable shared stateRwLock<T> when reads greatly outnumber writes| Mechanism | Direction | Use When |
|---|---|---|
| Commands | Frontend -> Backend (request/response) | CRUD operations, data fetching, actions with return values |
| Events | Any direction (fire-and-forget) | Notifications, progress updates, background task status, cross-window communication |
src-tauri/capabilities/*.jsonConfigure in tauri.conf.json:
script-src, style-src, connect-src to trusted originsunsafe-eval in productionipc: scheme for Tauri IPCRegister commands in lib.rs:
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.invoke_handler(tauri::generate_handler![greet, read_data, increment])
.manage(AppState { counter: Mutex::new(0) })
.run(tauri::generate_context!())
.expect("error running tauri app");
Call from frontend (any framework):
import { invoke } from '@tauri-apps/api/core';
const result = await invoke<string>('greet', { name: 'World' });
| Test Type | Scope | Tools |
|---|---|---|
| Rust Unit | Command logic, state management | cargo test, standard Rust testing |
| Frontend Unit | UI components, store logic | Vitest, Jest, Testing Library |
| Integration | Command invocation, IPC flow | tauri::test utilities |
| E2E | Full app behavior | WebDriver (via tauri-driver) |
#[tauri::command] wrapperState<T> in unit tests using Tauri's test utilitiestauri::test::mock_builder() for integration testscargo tauri build
Cargo.toml for size optimization:
[profile.release]
lto = true
opt-level = "s"
strip = true
tauri.conf.json under bundletauri-plugin-updater for in-app updatesMost v2 functionality is provided via plugins:
| Plugin | Purpose |
|---|---|
tauri-plugin-shell | Execute external commands |
tauri-plugin-fs | File system access (scoped) |
tauri-plugin-dialog | Native file/message dialogs |
tauri-plugin-store | Persistent key-value storage |
tauri-plugin-updater | Auto-update mechanism |
tauri-plugin-notification | System notifications |
tauri-plugin-http | HTTP client |
tauri-plugin-sql | SQLite/MySQL/PostgreSQL access |
Register plugins in lib.rs via .plugin() and grant permissions in capabilities.
npx claudepluginhub dobachi/claude-skills-marketplace --plugin tauri-devDevelops Tauri v2+ cross-platform desktop/mobile apps with Rust backend, configuring tauri.conf.json, #[tauri::command] handlers, IPC (invoke/emit/channels), capabilities/permissions, and troubleshooting builds.
Guides desktop app development with Electron and Tauri — cross-platform apps for Windows, macOS, Linux using a single codebase.
Configures Tauri v2 to package web frontends as native Android, Windows, macOS, and Linux apps. Handles builds, permissions, and native features like notifications and file system.