skills/rust-tauri-development/SKILL.md
Expert Tauri v2 developer for building desktop apps with Rust backend and web frontend. Activate on: Tauri app, Tauri v2, Rust desktop app, IPC commands, tauri::command, tauri.conf.json, Tauri plugin, WebviewWindow, system tray Tauri, Tauri multi-window. NOT for: Electron apps (use cross-platform-desktop), code signing/distribution (use rust-app-distribution), pure Rust CLI tools (use rust-expert).
npx skillsauth add curiositech/windags-skills rust-tauri-developmentInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Expert Tauri v2 development for building desktop applications with Rust backend and web frontend.
Data Exchange Pattern:
├─ Request/Response needed?
│ ├─ YES: Use #[tauri::command]
│ │ ├─ Sync operation? → fn command() -> Result<T, String>
│ │ └─ I/O operation? → async fn command() -> Result<T, String>
│ └─ NO: Fire-and-forget?
│ ├─ YES: Use events (emit/listen)
│ └─ Large binary data? → Use raw payload channels
State Management Pattern:
├─ Simple shared data?
│ ├─ Read-only → Arc<T>
│ ├─ Mutable + sync access → Arc<Mutex<T>>
│ └─ Mutable + async access → Arc<RwLock<T>>
├─ Database needed?
│ ├─ Simple KV → tauri-plugin-store
│ └─ Relational → tauri-plugin-sql
└─ Cross-process state? → Database or file-based
Window Count Decision:
├─ Single window app?
│ └─ Use default main window only
├─ Settings/preferences needed?
│ └─ Create secondary window with restricted capabilities
├─ Background processing?
│ ├─ System tray → TrayIconBuilder + hidden main window
│ └─ No tray → Keep main window, emit progress events
└─ Multi-document interface?
└─ Create window per document with shared state
Functionality needed:
├─ File system access?
│ ├─ Basic read/write → cargo tauri add fs
│ └─ Complex file ops → Custom commands + std::fs
├─ HTTP requests?
│ ├─ Simple → reqwest in custom commands
│ └─ Complex proxy/auth → Custom plugin
├─ Database?
│ ├─ SQLite/MySQL → cargo tauri add sql
│ └─ Custom storage → Custom plugin
└─ Platform integration?
├─ Notifications → cargo tauri add notification
└─ Custom system APIs → Custom plugin
Detection Rule: If UI freezes during Rust command execution Symptoms:
// BAD: Blocks the main thread
#[tauri::command]
fn heavy_computation() -> String {
std::thread::sleep(Duration::from_secs(5)); // UI freezes
"done".to_string()
}
// GOOD: Async command
#[tauri::command]
async fn heavy_computation() -> String {
tokio::time::sleep(Duration::from_secs(5)).await;
"done".to_string()
}
Detection Rule: If app crashes with "failed to serialize" during invoke() Symptoms:
// BAD: Missing derives
struct MyData {
field: String,
}
// GOOD: Proper derives
#[derive(Serialize, Deserialize)]
struct MyData {
field: String,
}
Detection Rule: If commands fail with "not allowed" or capability errors Symptoms:
{
"permissions": [
"fs:allow-read",
"fs:scope-app-data" // Add specific scopes
]
}
Detection Rule: If shared state shows inconsistent values between windows Symptoms:
// BAD: No synchronization
static mut COUNTER: i32 = 0;
// GOOD: Proper state management
struct AppState {
counter: Arc<Mutex<i32>>,
}
Detection Rule: If layout/behavior differs dramatically between macOS and Windows Symptoms:
Scenario: Create a desktop file manager with folder tree, file operations, and progress tracking.
Step 1: Architecture Decision
Step 2: Setup Capabilities
{
"permissions": [
"core:default",
"fs:allow-read",
"fs:allow-write",
"fs:allow-create",
"fs:scope-downloads",
"fs:scope-documents"
]
}
Step 3: Implement Backend Commands
#[tauri::command]
async fn list_directory(path: String, app: AppHandle) -> Result<Vec<FileEntry>, String> {
let entries = std::fs::read_dir(&path)
.map_err(|e| e.to_string())?
.collect::<Result<Vec<_>, _>>()
.map_err(|e| e.to_string())?;
let mut files = Vec::new();
for (i, entry) in entries.iter().enumerate() {
// Emit progress for large directories
if i % 100 == 0 {
app.emit("scan_progress", i).unwrap();
}
files.push(FileEntry::from_dir_entry(entry)?);
}
app.emit("scan_complete", files.len()).unwrap();
Ok(files)
}
#[tauri::command]
async fn copy_file(src: String, dest: String, app: AppHandle) -> Result<(), String> {
let src_path = Path::new(&src);
let dest_path = Path::new(&dest);
// Use async file operations for large files
let mut src_file = tokio::fs::File::open(&src_path).await
.map_err(|e| e.to_string())?;
let mut dest_file = tokio::fs::File::create(&dest_path).await
.map_err(|e| e.to_string())?;
// Stream copy with progress
let file_size = src_file.metadata().await
.map_err(|e| e.to_string())?.len();
let mut copied = 0u64;
while copied < file_size {
let chunk_size = tokio::io::copy(&mut src_file, &mut dest_file).await
.map_err(|e| e.to_string())?;
copied += chunk_size;
app.emit("copy_progress", (copied * 100) / file_size).unwrap();
}
Ok(())
}
What a novice would miss: Using blocking std::fs operations (freezes UI), forgetting progress events, not handling permission errors.
What an expert catches: Async file operations, progress tracking, proper error propagation, scoped capabilities.
cargo tauri icon for all required sizescargo tauri build)Do NOT use for:
Delegate to:
data-ai
license: Apache-2.0 NOT for unrelated tasks outside this domain.
development
Use when designing caching strategies (cache-aside, write-through, write-behind), implementing distributed locks, building rate limiters, leaderboards, real-time streams (XADD/consumer groups), pub/sub, or tuning eviction policies. Triggers: thundering-herd on cache miss, dogpile on key expiry, Redlock vs SET-NX-PX choice, sliding-window rate limiter, hot-key on a single cluster slot, big-key blowup, MULTI/EXEC across slots, KEYS in production. NOT for Redis Cluster operations/admin (different domain), embedded KV (SQLite, leveldb), in-process LRU caches, or Memcached.
tools
Drawing the `'use client'` boundary correctly in React Server Components apps (Next.js App Router, RSC frameworks) — leaf-pushing, slot composition, serialization rules, and environment poisoning prevention. Grounded in react.dev and Next.js 16 docs.
development
Use when designing rate limiting for an API, choosing between token bucket / sliding window / leaky bucket / fixed window, implementing it in Redis, deciding edge (Cloudflare/Upstash) vs origin enforcement, sizing per-user vs per-IP vs per-endpoint quotas, returning the right 429 response with Retry-After, or fixing the boundary-burst bug in fixed-window limiters. Triggers: 429 too many requests, INCR + EXPIRE, ZADD + ZREMRANGEBYSCORE + ZCARD, X-RateLimit-Remaining header, Cloudflare WAF rate limiting rules, Upstash @upstash/ratelimit, leaky bucket shaping vs policing, distributed rate limiter consistency. NOT for DDoS mitigation specifically (different scale), CAPTCHA / bot management, full WAF design, or per-user quota billing.