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 | shPuedes 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.