If this is useful, a ❤️ helps others find it.
Everything I keep looking up when building Tauri v2 apps — in one place.
Tauri Commands (Rust → Frontend)
// Define
#[tauri::command]
fn greet(name: String) -> String {
format!("Hello, {}!", name)
}
// With error handling
#[tauri::command]
fn read_file(path: String) -> Result {
std::fs::read_to_string(path).map_err(|e| e.to_string())
}
// Async
#[tauri::command]
async fn fetch_data(url: String) -> Result {
// async work here
Ok("data".to_string())
}
// Register
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet, read_file, fetch_data])
// Call from frontend
import { invoke } from '@tauri-apps/api/core';
const result = await invoke('greet', { name: 'world' });
const data = await invoke('fetch_data', { url: 'https://...' });
Events (Rust ↔ Frontend)
// Rust → Frontend
window.emit("my-event", serde_json::json!({"key": "value"})).unwrap();
// App-wide
app.emit("global-event", payload).unwrap();
// Frontend → listen
import { listen } from '@tauri-apps/api/event';
const unlisten = await listen('my-event', (event) => {
console.log(event.payload);
});
// Cleanup
unlisten();
// Frontend → Rust (via command, not events)
await invoke('handle_action', { data: 'value' });
State Management
// Define state
pub struct AppState {
pub counter: Mutex,
}
// Register
tauri::Builder::default()
.manage(AppState { counter: Mutex::new(0) })
// Use in command
#[tauri::command]
fn increment(state: tauri::State) -> u32 {
let mut counter = state.counter.lock().unwrap();
*counter += 1;
*counter
}
Permissions (v2 — capabilities/)
// src-tauri/capabilities/main.json
{
"identifier": "main-capability",
"windows": ["main"],
"permissions": [
"core:default",
"shell:allow-execute",
"shell:allow-stdin",
"fs:allow-read-files",
"fs:allow-write-files",
"fs:allow-app-cache-write",
"dialog:allow-open",
"dialog:allow-save",
"notification:default"
]
}
Window Config
// tauri.conf.json
{
"app": {
"windows": [{
"label": "main",
"title": "My App",
"width": 800,
"height": 600,
"resizable": true,
"decorations": true,
"transparent": false,
"alwaysOnTop": false,
"fullscreen": false,
"visible": true
}],
"macOS": {
"activationPolicy": "regular"
}
}
}
For menubar app: "activationPolicy": "accessory"
System Tray
use tauri::tray::{TrayIconBuilder, TrayIconEvent};
TrayIconBuilder::new()
.icon(app.default_window_icon().unwrap().clone())
.on_tray_icon_event(|tray, event| {
if let TrayIconEvent::Click { .. } = event {
let app = tray.app_handle();
if let Some(window) = app.get_webview_window("main") {
window.show().unwrap();
window.set_focus().unwrap();
}
}
})
.build(app)?;
Common Plugin Setup
# Cargo.toml
[dependencies]
tauri-plugin-store = "2"
tauri-plugin-global-shortcut = "2"
tauri-plugin-notification = "2"
tauri-plugin-dialog = "2"
tauri-plugin-fs = "2"
tauri-plugin-shell = "2"
tauri::Builder::default()
.plugin(tauri_plugin_store::Builder::new().build())
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_shell::init())
Build Commands
# Dev
cargo tauri dev
# Build for current arch
cargo tauri build
# Universal binary (Intel + Apple Silicon)
cargo tauri build --target universal-apple-darwin
# iOS (Tauri v2)
cargo tauri ios build
# Android (Tauri v2)
cargo tauri android build
Hiyoko PDF Vault → https://hiyokoko.gumroad.com/l/HiyokoPDFVault
X → @hiyoyok
Top comments (0)