Tauri allows building desktop (and now mobile in v2.0) apps using web technologies. It differs from Electron by using the OS Native Webview (WebView2 on Windows, WebKit on macOS/Linux).
When to Use
- Lightweight Cross-Platform Desktop Apps: Building native macOS, Windows, and Linux apps using web frontends and Rust backends.
- Tauri v2 Mobile & Desktop Support: Compiling unified codebases to iOS, Android, and desktop.
- Memory-Efficient Desktop Software: Tiny binary size (< 10MB) and low memory usage utilizing the system webview.
- High-Security Desktop Environments: Strict IPC permissions, cryptographic signature verification, and zero Node runtime.
Quick Start
// 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"); }
```javascript
// frontend app.js
import { invoke } from "@tauri-apps/api/core";
const greeting = await invoke("greet", { name: "World" });
Core Concepts
Rust Command Handlers with #[tauri::command]
Exposing high-performance native Rust functions to the webview:
// src-tauri/src/lib.rs
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct SystemStats {
cpu_cores: usize,
os_name: String,
}
#[tauri::command]
fn get_system_stats() -> Result<SystemStats, String> {
Ok(SystemStats {
cpu_cores: num_cpus::get(),
os_name: std::env::consts::OS.to_string(),
})
}
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![get_system_stats])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Invoking Rust Commands from Frontend
Type-safe frontend IPC invocation:
// src/services/system.ts
import { invoke } from "@tauri-apps/api/core";
interface SystemStats {
cpu_cores: number;
os_name: string;
}
export async function fetchSystemMetrics(): Promise<SystemStats> {
try {
const stats = await invoke<SystemStats>("get_system_stats");
console.log(`Running on ${stats.os_name} with ${stats.cpu_cores} cores`);
return stats;
} catch (error) {
console.error("Failed to invoke Tauri command:", error);
throw error;
}
}
Tauri v2 Permissions & Capabilities System
Configuring granular security rules for plugins and commands:
// src-tauri/capabilities/main.json
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"core:default",
"fs:allow-read-text-file",
"dialog:allow-open",
"notification:allow-notify"
]
}
Common Patterns
Typed IPC Invocation with Serde Structs
Problem: Passing structured parameters and receiving typed errors across the Rust-to-JS bridge.
Solution: Serialize payloads with serde:
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct LoginRequest {
username: String,
auth_token: String,
}
#[derive(Serialize)]
struct UserSession {
user_id: u64,
display_name: String,
}
#[tauri::command]
async fn authenticate(req: LoginRequest) -> Result<UserSession, String> {
if req.auth_token == "valid" {
Ok(UserSession { user_id: 1, display_name: req.username })
} else {
Err("Authentication failed".into())
}
}
Best Practices
Do:
- Target Tauri v2 with explicit capability and permission manifests for least privilege access.
- Handle heavy computation and filesystem operations in Rust, keeping the webview UI smooth and responsive.
- Configure code signing and automated updates using Tauri's built-in updater plugin.
- Use
@tauri-apps/api/corefor modern Tauri v2 frontend bindings.
Don't:
- Pass large binary files across the IPC bridge as base64 strings; use custom protocol streaming.
- Grant wildcard permissions (
fs:allow-all) in capability files. - Block the main Tauri thread; use async Rust commands for I/O operations.
Troubleshooting
| Error | Cause | Solution |
|---|---|---|
Command ... not found in invoke handler |
Command function omitted from tauri::generate_handler![...]. |
Register the command name in generate_handler![my_cmd]. |
Tauri build fails: WebView2 / WebKit2GTK missing |
Missing system webview libraries on Linux or Windows. | Install libwebkit2gtk-4.1-dev (Linux) or WebView2 runtime (Windows). |
Permission denied calling plugin API in Tauri v2 |
Capabilities permission file not granting access to plugin. | Add plugin permissions in src-tauri/capabilities/default.json. |