npx skills add ...
npx skills add nodnarbnitram/claude-code-extensions --skill tauri-v2
Tauri v2+ cross-platform app development with Rust backend. Use when configuring tauri.conf.json, implementing Rust commands (#[tauri::command]), setting up IPC patterns (invoke, emit, channels), configuring permissions/capabilities, troubleshooting build issues, or deploying desktop/mobile apps. Triggers on Tauri, src-tauri, invoke, emit, capabilities.json.
npx skills add nodnarbnitram/claude-code-extensions --skill tauri-v2
Build cross-platform desktop and mobile apps with web frontends and Rust backends.
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) |
generate_handler!Why this matters: Commands not in generate_handler![] silently fail when invoked from frontend.
main.rsstays thin:src-tauri/src/main.rsshould only be a thin passthrough — all application logic lives inlib.rs:This split is required for mobile builds — Tauri replaces
main()withmobile_entry_pointon mobile targets.
Why this matters: Use @tauri-apps/api/core (not @tauri-apps/api/tauri - that's v1 API).
Why this matters: Tauri v2 denies everything by default - explicit permissions required for all operations.
tauri::generate_handler![cmd1, cmd2, ...]Result<T, E> from commands for proper error handlingMutex<T> for shared state accessed from multiple commandslib.rs for shared code (required for mobile builds)#[cfg_attr(mobile, tauri::mobile_entry_point)] on pub fn run() in lib.rs for mobile compatibility&str) in async commands - use owned typesapp.path())Wrong - Borrowed type in async:
Correct - Owned type:
Why: Async commands cannot borrow data across await points; Tauri requires owned types for async command parameters.
| 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 |
| Plugin feature silently fails | Plugin installed but permission not in capability | Add plugin permission string to capabilities/default.json |
| Updater fails in production | Unsigned artifacts or HTTP endpoint | Generate keys with cargo tauri signer generate, use HTTPS endpoint only |
| Sidecar not found | externalBin not in tauri.conf.json or missing executable | Add path to bundle.externalBin, ensure binary is bundled |
| Feature works on desktop, breaks on mobile | Desktop-only API used | Check if API has mobile support — some plugins are desktop-only |
| 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> |
references/capabilities-reference.mdreferences/ipc-patterns.mdreferences/plugin-reference.mdreferences/updater-distribution-reference.mdreferences/advanced-runtime-reference.mdKey settings:
build.devUrl: Must match your frontend dev server portapp.security.capabilities: Array of capability file identifiersPlugin configuration — Some plugins require additional tauri.conf.json blocks (e.g., store, updater). Always check the specific plugin docs at v2.tauri.app/plugin/<plugin-name>/ for required config keys.
Why lib.rs owns all logic: Tauri replaces main() with #[cfg_attr(mobile, tauri::mobile_entry_point)] on mobile. All commands, state, and builder setup must live in lib.rs::run().
Key settings:
[lib] section: Required for mobile buildscrate-type: Must include all three types for cross-platformUse Result<T, E> and thiserror for type-safe error propagation across the IPC boundary. See references/ipc-patterns.md for full implementation details.
All command arguments must implement serde::Deserialize, and return types must implement serde::Serialize. This is how Tauri bridges JSON over the IPC boundary.
Common serde pitfalls:
Option<T> maps to optional JS arguments (can be undefined or null)#[serde(tag = "type")] or similar to be JSON-safeSerialize (see Error Handling Pattern above)Tauri state manages application data across commands. See references/ipc-patterns.md for more complex state patterns.
Events are fire-and-forget notifications. See references/ipc-patterns.md for bidirectional examples.
Channels provide high-frequency, typed streaming from Rust to Frontend. See references/ipc-patterns.md for full implementation details.
Tauri v2 uses WebviewWindow for unified window and webview management.
Why this matters: Use tauri::WebviewWindow and app.get_webview_window("label") in v2 — the v1 app.get_window() API is removed in v2.
Located in references/:
capabilities-reference.md - Permission patterns and examplesipc-patterns.md - Complete IPC examplesplugin-reference.md - Official plugin install, registration, and permission stringsupdater-distribution-reference.md - Signing, HTTPS requirements, and bundle shippingadvanced-runtime-reference.md - TrayIconBuilder, sidecars, deep links, and asset protocolsNote: For deep dives on specific topics, see the reference files above.
| Package | Version | Purpose |
|---|---|---|
@tauri-apps/cli | ^2 (v2+) | CLI tooling |
@tauri-apps/api | ^2 (v2+) | Frontend APIs |
tauri | ^2 (v2+) | Rust core |
tauri-build | ^2 (v2+) | Build scripts |
*Last verified: 2026-04-02. Always check official changelog for feature timing.
| Package | Version | Purpose | Key Permission |
|---|---|---|---|
tauri-plugin-fs | ^2 (v2+) | File system access | fs:default |
tauri-plugin-dialog | ^2 (v2+) | Native dialogs | dialog:default |
tauri-plugin-shell | ^2 (v2+) | Shell commands, open URLs | shell:default |
tauri-plugin-http | ^2 (v2+) | HTTP client | http:default |
tauri-plugin-store | ^2 (v2+) | Key-value storage | store:default |
Plugin permissions are mandatory. Installing a plugin without adding its permission string to a capability file causes silent runtime failures. See
references/plugin-reference.mdfor full install + permission details for all official plugins.
Symptoms: App launches but shows blank white screen
Solution:
devUrl matches your frontend dev server portbeforeDevCommand runs your dev serverSymptoms: invoke() returns undefined instead of expected value
Solution:
generate_handler![]Symptoms: Android/iOS build fails with missing target
Solution:
Not all Tauri APIs and plugins support mobile (iOS/Android). Before using any plugin or API in a mobile build:
v2.tauri.app/plugin/<name>/ for platform support matrixTrayIconBuilder), window labels/multi-window, some shell plugin featurestauri::AppHandle is mobile-safe#[cfg(desktop)] / #[cfg(mobile)] for platform-specific Rust logicBefore using this skill, verify:
npx tauri info shows correct Tauri v2 versionssrc-tauri/capabilities/default.json exists with at least core:defaultgenerate_handler![]lib.rs contains shared code (for mobile support)