tauri-v2

Tauri v2 Development Skill

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 "tauri-v2" with this command: npx skills add nodnarbnitram/claude-code-extensions/nodnarbnitram-claude-code-extensions-tauri-v2

Tauri v2 Development Skill

Build cross-platform desktop and mobile apps with web frontends and Rust backends.

Before You Start

This skill prevents 8+ common errors and saves ~60% tokens.

Metric Without Skill With Skill

Setup Time ~2 hours ~30 min

Common Errors 8+ 0

Token Usage High (exploration) Low (direct patterns)

Known Issues This Skill Prevents

  • Permission denied errors from missing capabilities

  • IPC failures from unregistered commands in generate_handler!

  • State management panics from type mismatches

  • Mobile build failures from missing Rust targets

  • White screen issues from misconfigured dev URLs

Quick Start

Step 1: Create a Tauri Command

// src-tauri/src/lib.rs #[tauri::command] fn greet(name: String) -> String { format!("Hello, {}!", name) }

pub fn run() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![greet]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }

Why this matters: Commands not in generate_handler![] silently fail when invoked from frontend.

Step 2: Call from Frontend

import { invoke } from '@tauri-apps/api/core';

const greeting = await invoke<string>('greet', { name: 'World' }); console.log(greeting); // "Hello, World!"

Why this matters: Use @tauri-apps/api/core (not @tauri-apps/api/tauri

  • that's v1 API).

Step 3: Add Required Permissions

// src-tauri/capabilities/default.json { "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", "windows": ["main"], "permissions": ["core:default"] }

Why this matters: Tauri v2 denies everything by default - explicit permissions required for all operations.

Critical Rules

Always Do

  • Register every command in tauri::generate_handler![cmd1, cmd2, ...]

  • Return Result<T, E> from commands for proper error handling

  • Use Mutex<T> for shared state accessed from multiple commands

  • Add capabilities before using any plugin features

  • Use lib.rs for shared code (required for mobile builds)

Never Do

  • Never use borrowed types (&str ) in async commands - use owned types

  • Never block the main thread - use async for I/O operations

  • Never hardcode paths - use Tauri path APIs (app.path() )

  • Never skip capability setup - even "safe" operations need permissions

Common Mistakes

Wrong - Borrowed type in async:

#[tauri::command] async fn bad(name: &str) -> String { // Compile error! name.to_string() }

Correct - Owned type:

#[tauri::command] async fn good(name: String) -> String { name }

Why: Async commands cannot borrow data across await points; Tauri requires owned types for async command parameters.

Known Issues Prevention

Issue Root Cause Solution

"Command not found" Missing from generate_handler!

Add command to handler macro

"Permission denied" Missing capability Add to capabilities/default.json

State panic on access Type mismatch in State<T>

Use exact type from .manage()

White screen on launch Frontend not building Check beforeDevCommand in config

IPC timeout Blocking async command Remove blocking code or use spawn

Mobile build fails Missing Rust targets Run rustup target add <target>

Configuration Reference

tauri.conf.json

{ "$schema": "./gen/schemas/desktop-schema.json", "productName": "my-app", "version": "1.0.0", "identifier": "com.example.myapp", "build": { "devUrl": "http://localhost:5173", "frontendDist": "../dist", "beforeDevCommand": "npm run dev", "beforeBuildCommand": "npm run build" }, "app": { "windows": [{ "label": "main", "title": "My App", "width": 800, "height": 600 }], "security": { "csp": "default-src 'self'; img-src 'self' data:", "capabilities": ["default"] } }, "bundle": { "active": true, "targets": "all", "icon": ["icons/icon.icns", "icons/icon.ico", "icons/icon.png"] } }

Key settings:

  • build.devUrl : Must match your frontend dev server port

  • app.security.capabilities : Array of capability file identifiers

Cargo.toml

[package] name = "app" version = "0.1.0" edition = "2021"

[lib] name = "app_lib" crate-type = ["staticlib", "cdylib", "rlib"]

[build-dependencies] tauri-build = { version = "2", features = [] }

[dependencies] tauri = { version = "2", features = [] } serde = { version = "1", features = ["derive"] } serde_json = "1"

Key settings:

  • [lib] section: Required for mobile builds

  • crate-type : Must include all three types for cross-platform

Common Patterns

Error Handling Pattern

use thiserror::Error;

#[derive(Debug, 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::ser::Serializer { serializer.serialize_str(self.to_string().as_ref()) } }

#[tauri::command] fn risky_operation() -> Result<String, AppError> { Ok("success".into()) }

State Management Pattern

use std::sync::Mutex; use tauri::State;

struct AppState { counter: u32, }

#[tauri::command] fn increment(state: State<'_, Mutex<AppState>>) -> u32 { let mut s = state.lock().unwrap(); s.counter += 1; s.counter }

// In builder: tauri::Builder::default() .manage(Mutex::new(AppState { counter: 0 }))

Event Emission Pattern

use tauri::Emitter;

#[tauri::command] fn start_task(app: tauri::AppHandle) { std::thread::spawn(move || { app.emit("task-progress", 50).unwrap(); app.emit("task-complete", "done").unwrap(); }); }

import { listen } from '@tauri-apps/api/event';

const unlisten = await listen('task-progress', (e) => { console.log('Progress:', e.payload); }); // Call unlisten() when done

Channel Streaming Pattern

use tauri::ipc::Channel;

#[derive(Clone, serde::Serialize)] #[serde(tag = "event", content = "data")] enum DownloadEvent { Progress { percent: u32 }, Complete { path: String }, }

#[tauri::command] async fn download(url: String, on_event: Channel<DownloadEvent>) { for i in 0..=100 { on_event.send(DownloadEvent::Progress { percent: i }).unwrap(); } on_event.send(DownloadEvent::Complete { path: "/downloads/file".into() }).unwrap(); }

import { invoke, Channel } from '@tauri-apps/api/core';

const channel = new Channel<DownloadEvent>(); channel.onmessage = (msg) => console.log(msg.event, msg.data); await invoke('download', { url: 'https://...', onEvent: channel });

Bundled Resources

References

Located in references/ :

  • capabilities-reference.md

  • Permission patterns and examples

  • ipc-patterns.md

  • Complete IPC examples

Note: For deep dives on specific topics, see the reference files above.

Dependencies

Required

Package Version Purpose

@tauri-apps/cli

^2.0.0 CLI tooling

@tauri-apps/api

^2.0.0 Frontend APIs

tauri

^2.0.0 Rust core

tauri-build

^2.0.0 Build scripts

Optional (Plugins)

Package Version Purpose

tauri-plugin-fs

^2.0.0 File system access

tauri-plugin-dialog

^2.0.0 Native dialogs

tauri-plugin-shell

^2.0.0 Shell commands, open URLs

tauri-plugin-http

^2.0.0 HTTP client

tauri-plugin-store

^2.0.0 Key-value storage

Official Documentation

  • Tauri v2 Documentation

  • Commands Reference

  • Capabilities & Permissions

  • Configuration Reference

Troubleshooting

White Screen on Launch

Symptoms: App launches but shows blank white screen

Solution:

  • Verify devUrl matches your frontend dev server port

  • Check beforeDevCommand runs your dev server

  • Open DevTools (Cmd+Option+I / Ctrl+Shift+I) to check for errors

Command Returns Undefined

Symptoms: invoke() returns undefined instead of expected value

Solution:

  • Verify command is in generate_handler![]

  • Check Rust command actually returns a value

  • Ensure argument names match (camelCase in JS, snake_case in Rust by default)

Mobile Build Failures

Symptoms: Android/iOS build fails with missing target

Solution:

Android targets

rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android

iOS targets (macOS only)

rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim

Setup Checklist

Before using this skill, verify:

  • npx tauri info shows correct Tauri v2 versions

  • src-tauri/capabilities/default.json exists with at least core:default

  • All commands registered in generate_handler![]

  • lib.rs contains shared code (for mobile support)

  • Required Rust targets installed for target platforms

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

ha-automation

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

kubernetes-operations

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

esphome-config-helper

No summary provided by upstream source.

Repository SourceNeeds Review