This chapter explains how a Tauri app works: the relationship between the Rust core and the WebView, commands that let the frontend call Rust, events for two-way notifications, the permission-based security model, and plugins that add features.
A Rust core and the system WebView
A Tauri app consists of a core process written in Rust plus the WebViews that render the UI. Instead of bundling a browser engine, Tauri uses the one provided by the OS: WebView2 (based on Microsoft Edge) on Windows, WKWebView on macOS and iOS, WebKitGTK on Linux and the system WebView on Android. This keeps installers small, but since the rendering engine differs per platform, you should test the UI on every OS you ship to.
Windowing is handled by TAO and WebView integration by WRY, both maintained by the Tauri team. The core can reach windows, menus, the system tray and OS APIs, while code inside the WebView sends requests to the core over IPC (inter-process communication). Checking permissions at that boundary is the heart of Tauri's security.
Commands: calling Rust from the frontend
Annotate a Rust function with #[tauri::command] and register it with generate_handler!, and the frontend can call it by name. Call invoke_handler only once and list every command there; a second call replaces the first.
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {name}!")
}
#[tauri::command]
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
return Err("cannot divide by zero".into());
}
Ok(a / b)
}
#[tauri::command]
async fn count_lines(path: String) -> Result<usize, String> {
let text = std::fs::read_to_string(&path).map_err(|e| e.to_string())?;
Ok(text.lines().count())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet, divide, count_lines])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Arguments and return values travel as JSON, so they must be serializable with serde. On the JavaScript side, pass arguments with camelCase keys; they are matched to the snake_case parameter names in Rust automatically. An Err value becomes a rejected Promise in the frontend. Commands without async run on the main thread, so make long-running work an async command to keep the UI responsive.
import { invoke } from "@tauri-apps/api/core";
const message = await invoke<string>("greet", { name: "Tauri" });
try {
const result = await invoke<number>("divide", { a: 10, b: 0 });
console.log(result);
} catch (error) {
console.error(error); // "cannot divide by zero"
}
Events: notifications in both directions
Commands are request and response; events are fire-and-forget messages that either side can send. Use them when Rust needs to speak first, for progress updates or state changes. Rust emits through the trait, and the frontend subscribes with .
0 comentarios
Iniciar sesión · Inicia sesión para dejar un comentario.