Tauri es un framework para construir aplicaciones de escritorio pequeñas, rápidas y seguras usando tecnologías web modernas. Combina Rust en el backend con cualquier framework frontend como React, Vue o Svelte.
Antes de comenzar, necesitas instalar:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Puedes crear un proyecto Tauri usando create-tauri-app:
O configurarlo manualmente:
Un proyecto Tauri típico tiene esta estructura:
Tauri es agnóstico al framework frontend. Puedes usar React, Vue, Svelte o incluso HTML plano.
Tauri proporciona varias formas de comunicación:
import { invoke } from '@tauri-apps/api/tauri'; function App() { const [greeting, setGreeting] = React.useState(''); async function greet(name) { // Llama al comando "greet" definido en Rust const result = await invoke('greet', { name }); setGreeting(result); } return ( <div> <button onClick={() => greet('World')}> Saludar </button> <p>{greeting}</p> </div> ); }
#[tauri::command] fn greet(name: &str) -> String { format!("Hola, {}!", name) } fn main() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![greet]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
Tauri proporciona APIs listas para usar:
import { readDir, BaseDirectory } from '@tauri-apps/api/fs'; async function listFiles() { const entries = await readDir('Documents', { dir: BaseDirectory.Home }); entries.forEach(entry => { console.log(entry.name); }); }
Rust proporciona el poder del backend en Tauri. Veamos algunas características avanzadas.
use serde::Deserialize; use tauri::command; #[derive(Debug, Deserialize)] struct User { name: String, age: u8, } #[command] fn create_user(user: User) -> Result<String, String> { if user.age < 18 { return Err("El usuario debe ser mayor de edad".into()); } Ok(format!("Usuario {} creado con edad {}", user.name, user.age)) } fn main() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![create_user]) .run(tauri::generate_context!()) .expect("error running tauri application"); }
use std::fs; use tauri::command; #[command] fn read_file(path: String) -> Result<String, String> { fs::read_to_string(&path) .map_err(|e| format!("Error leyendo archivo: {}", e)) } #[command] fn write_file(path: String, contents: String) -> Result<(), String> { fs::write(&path, contents) .map_err(|e| format!("Error escribiendo archivo: {}", e)) }
use tauri::{Manager, RunEvent}; fn main() { tauri::Builder::default() .setup(|app| { let main_window = app.get_window("main").unwrap(); // Escuchar evento desde el frontend main_window.listen("frontend-event", |event| { println!("Evento recibido: {:?}", event.payload()); }); Ok(()) }) .build(tauri::generate_context!()) .expect("error building tauri application") .run(|app_handle, e| { if let RunEvent::ExitRequested { .. } = e { // Enviar evento al frontend antes de salir app_handle.emit_all("backend-event", "Aplicación cerrando").unwrap(); } }); }
use tauri::{Menu, MenuItem, Submenu, CustomMenuItem}; fn main() { let menu = Menu::new() .add_submenu(Submenu::new( "Archivo", Menu::new() .add_item(CustomMenuItem::new("new", "Nuevo")) .add_item(CustomMenuItem::new("open", "Abrir")) .add_native_item(MenuItem::Separator) .add_item(CustomMenuItem::new("quit", "Salir")), )) .add_submenu(Submenu::new( "Editar", Menu::new() .add_native_item(MenuItem::Undo) .add_native_item(MenuItem::Redo) .add_native_item(MenuItem::Separator) .add_native_item(MenuItem::Cut) .add_native_item(MenuItem::Copy) .add_native_item(MenuItem::Paste), )); tauri::Builder::default() .menu(menu) .on_menu_event(|event| { match event.menu_item_id() { "quit" => std::process::exit(0), _ => println!("Clicked on {}", event.menu_item_id()), } }) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
use tauri::{SystemTray, SystemTrayMenu, CustomMenuItem}; fn main() { let tray_menu = SystemTrayMenu::new() .add_item(CustomMenuItem::new("show", "Mostrar")) .add_item(CustomMenuItem::new("hide", "Ocultar")) .add_native_item(SystemTrayMenuItem::Separator) .add_item(CustomMenuItem::new("quit", "Salir")); let system_tray = SystemTray::new().with_menu(tray_menu); tauri::Builder::default() .system_tray(system_tray) .on_system_tray_event(|app, event| { match event { SystemTrayEvent::MenuItemClick { id, .. } => { match id.as_str() { "quit" => std::process::exit(0), "hide" => app.get_window("main").unwrap().hide().unwrap(), "show" => app.get_window("main").unwrap().show().unwrap(), _ => {} } } _ => {} } }) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
import { sendNotification } from '@tauri-apps/api/notification'; function App() { const showNotification = () => { sendNotification({ title: 'Tauri App', body: 'Esta es una notificación del sistema!', icon: 'path/to/icon.png' }); }; return ( <button onClick={showNotification}> Mostrar notificación </button> ); }
Para crear un ejecutable:
Esto generará instaladores para tu plataforma:
Puedes personalizar el build en src-tauri/tauri.conf.json
:
{ "build": { "distDir": "../dist", "devPath": "http://localhost:3000", "beforeBuildCommand": "npm run build" }, "package": { "productName": "MiApp", "version": "1.0.0" }, "tauri": { "bundle": { "identifier": "com.example.miapp", "icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png"], "resources": [], "targets": "all", "windows": { "certificateThumbprint": null, "digestAlgorithm": "sha256", "timestampUrl": "" } }, "allowlist": { "all": false, "fs": { "scope": ["$HOME/Documents"] } } } }
Tauri soporta actualizaciones automáticas:
use tauri::updater::Builder; fn main() { tauri::Builder::default() .setup(|app| { let handle = app.handle(); tauri::async_runtime::spawn(async move { let builder = Builder::new(handle.config()); match builder.build().await { Ok(updater) => { if let Ok(Some(update)) = updater.check().await { if update.download_and_install().await.is_ok() { std::process::exit(0); } } } Err(e) => println!("Error en actualizador: {}", e), } }); Ok(()) }) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
Tauri es una excelente alternativa moderna para desarrollar aplicaciones de escritorio con tecnologías web. Combina lo mejor de Rust para el backend con la flexibilidad de cualquier framework frontend.