Tauri 2: apps desktop com Rust e frontend web sem o peso do Electron
1. O que é Tauri e por que ele desafia o Electron?
Tauri é um framework para construção de aplicações desktop multiplataforma que combina um backend em Rust com um frontend web tradicional (HTML, CSS, JavaScript/TypeScript). Diferentemente do Electron, que empacota um navegador Chromium completo, Tauri utiliza o renderizador nativo do sistema operacional (WebView) — no Windows é o WebView2 baseado no Edge, no macOS o WKWebView, e no Linux o WebKitGTK.
O resultado é impressionante: enquanto um app "Hello World" em Electron consome cerca de 100-150 MB de RAM e gera binários de 150-200 MB, a mesma aplicação em Tauri roda com 10-20 MB de memória e produz executáveis de apenas 3-8 MB. O tempo de inicialização também é drasticamente menor — tipicamente abaixo de 1 segundo contra 3-5 segundos do Electron.
A motivação original do projeto (lançado em 2020) era justamente oferecer uma alternativa mais leve, segura e performática para desenvolvedores web que desejam criar aplicativos desktop sem abrir mão da produtividade. Com a versão 2 (estável desde outubro de 2023), o framework amadureceu significativamente, adicionando suporte a plugins oficiais, sistema de permissões granular e melhorias no IPC.
2. Primeiros passos com Tauri 2: configuração e scaffolding
Para começar, você precisa do Rust (via rustup) e Node.js (v16+). Instale a CLI do Tauri:
npm install -g @tauri-apps/cli@latest
Crie um novo projeto:
npm create tauri-app@latest
Durante a configuração interativa, escolha um nome, o gerenciador de pacotes (npm, yarn, pnpm) e o template de frontend (React, Vue, Svelte ou Vanilla). A estrutura gerada será:
meu-app/
├── src/ # Frontend (React, Vue, etc.)
├── src-tauri/ # Backend Rust
│ ├── src/
│ │ └── main.rs # Código principal do Rust
│ ├── Cargo.toml # Dependências Rust
│ └── tauri.conf.json # Configurações do Tauri
├── package.json
└── index.html
O arquivo tauri.conf.json centraliza as configurações:
{
"build": {
"frontendDist": "../dist",
"devUrl": "http://localhost:5173",
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run build"
},
"app": {
"windows": [
{
"title": "Meu App Tauri",
"width": 1024,
"height": 768,
"resizable": true
}
]
}
}
Para desenvolvimento, execute npm run tauri dev. O comando inicia o servidor Vite (ou outro bundler) e abre a janela nativa com a WebView.
3. Comunicação entre Rust e frontend: IPC e comandos
O coração da comunicação é o sistema de comandos (commands). No lado Rust, você expõe funções com o atributo #[tauri::command]:
// src-tauri/src/main.rs
use tauri::Manager;
#[tauri::command]
fn greet(name: &str) -> String {
format!("Olá, {}! Bem-vindo ao Tauri 2.", name)
}
#[tauri::command]
fn calculate_fibonacci(n: u32) -> Result<Vec<u64>, String> {
if n > 90 {
return Err("Número muito grande".to_string());
}
let mut sequence = vec![0, 1];
for i in 2..=n as usize {
sequence.push(sequence[i-1] + sequence[i-2]);
}
Ok(sequence[..=n as usize].to_vec())
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet, calculate_fibonacci])
.run(tauri::generate_context!())
.expect("Erro ao executar aplicação");
}
No frontend, a invocação usa invoke:
import { invoke } from '@tauri-apps/api/core';
// Comando simples
async function saudacao() {
const mensagem = await invoke('greet', { name: 'Maria' });
console.log(mensagem); // "Olá, Maria! Bem-vindo ao Tauri 2."
}
// Comando com retorno complexo e tratamento de erro
async function fibonacci() {
try {
const sequencia = await invoke('calculate_fibonacci', { n: 10 });
console.log(sequencia); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
} catch (erro) {
console.error('Erro:', erro);
}
}
Para eventos bidirecionais (úteis para notificações em tempo real):
// Rust: emitindo evento
app_handle.emit("progress", 75).unwrap();
// Frontend: escutando evento
import { listen } from '@tauri-apps/api/event';
const unlisten = await listen('progress', (event) => {
console.log(`Progresso: ${event.payload}%`);
});
4. Sistema de plugins e extensibilidade
O Tauri 2 possui um ecossistema robusto de plugins oficiais. Para usar um, adicione-o ao Cargo.toml e configure no tauri.conf.json:
// Cargo.toml
[dependencies]
tauri-plugin-dialog = "2"
tauri-plugin-fs = "2"
tauri-plugin-shell = "2"
// tauri.conf.json
{
"plugins": {
"dialog": {},
"fs": {
"scope": ["$APPDATA/*", "$HOME/Documents/*"]
},
"shell": {
"open": true
}
}
}
No frontend, a API do plugin fica disponível:
import { open } from '@tauri-apps/plugin-dialog';
import { readTextFile } from '@tauri-apps/plugin-fs';
async function abrirArquivo() {
const arquivo = await open({
multiple: false,
filters: [{ name: 'Textos', extensions: ['txt', 'md'] }]
});
if (arquivo) {
const conteudo = await readTextFile(arquivo);
console.log(conteudo);
}
}
Para criar um plugin customizado, você encapsula comandos Rust em um crate separado:
// src-tauri/plugins/meu-plugin/src/lib.rs
use tauri::{plugin::Plugin, Runtime};
pub struct MeuPlugin;
impl<R: Runtime> Plugin<R> for MeuPlugin {
fn name(&self) -> &'static str {
"meu-plugin"
}
fn initialize(&mut self, app: &tauri::AppHandle<R>, _config: serde_json::Value) -> Result<(), Box<dyn std::error::Error>> {
// Configuração inicial
Ok(())
}
}
5. Segurança e permissões no Tauri 2
O modelo de segurança do Tauri 2 é baseado em capabilities — você declara explicitamente quais comandos e plugins podem ser acessados pelo frontend. As permissões são configuradas no tauri.conf.json:
{
"app": {
"security": {
"csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
}
},
"capabilities": [
{
"identifier": "default",
"description": "Permissões padrão do app",
"windows": ["main"],
"permissions": [
"core:default",
"dialog:default",
"fs:read",
"fs:write",
{
"identifier": "shell:allow-open",
"allow": [{ "url": "https://*" }]
}
]
}
]
}
Boas práticas incluem:
- Validar todas as entradas no Rust (nunca confiar no frontend)
- Usar escopo mínimo de permissões (ex: fs:read ao invés de fs:all)
- Habilitar CSP restritivo
- Para operações sensíveis, exigir confirmação do usuário via diálogos nativos
6. Build, distribuição e atualizações automáticas
Para build de produção:
npm run tauri build
Isso gera:
- Windows: src-tauri/target/release/bundle/msi/ ou nsis/
- macOS: src-tauri/target/release/bundle/dmg/
- Linux: src-tauri/target/release/bundle/appimage/ ou deb/
Para atualizações automáticas, configure o plugin updater:
// Cargo.toml
tauri-plugin-updater = "2"
// tauri.conf.json
{
"plugins": {
"updater": {
"endpoints": ["https://meuservidor.com/updates/{{target}}/{{current_version}}"],
"pubkey": "seu-public-key-aqui"
}
}
}
No servidor, hospede um manifesto JSON:
{
"version": "1.2.0",
"notes": "Correção de bugs e melhorias de performance",
"pub_date": "2024-06-15",
"platforms": {
"windows-x86_64": { "url": "https://meuservidor.com/releases/app_1.2.0_x64.msi", "signature": "" },
"darwin-x86_64": { "url": "https://meuservidor.com/releases/app_1.2.0_x64.dmg", "signature": "" },
"linux-x86_64": { "url": "https://meuservidor.com/releases/app_1.2.0_amd64.deb", "signature": "" }
}
}
7. Casos de uso reais e comparação com Electron
Empresas como a Linear (ferramenta de gerenciamento de projetos) migraram partes de sua stack de Electron para Tauri, reportando redução de 70% no uso de memória. O Discord também testou Tauri para seu aplicativo de desktop, com resultados promissores em performance.
Limitações atuais do Tauri 2:
- Suporte limitado a WebGL (funciona, mas sem aceleração total em alguns cenários)
- APIs nativas avançadas (bluetooth, USB) ainda dependem de plugins comunitários
- Ecossistema de plugins menor que o do Electron
Quando escolher Tauri:
- Apps que priorizam leveza e performance
- Projetos onde o tamanho do binário importa (ex: ferramentas de linha de comando com GUI)
- Equipes já familiarizadas com Rust ou que desejam segurança de memória no backend
Quando preferir Electron:
- Apps que dependem fortemente de Chromium (ex: ferramentas de desenvolvimento web)
- Necessidade de suporte a APIs experimentais do navegador
- Ecossistema maduro de plugins (Electron tem 10+ anos de comunidade)
Alternativas como Neutralinojs (backend em C++) e NW.js (também baseado em Chromium) ocupam nichos específicos, mas Tauri se destaca pelo equilíbrio entre produtividade web e performance nativa.
Referências
- Documentação oficial do Tauri 2 — Guia completo de instalação, configuração e API de todos os plugins oficiais
- Tauri vs Electron: Benchmark de performance — Artigo técnico comparando consumo de memória, tamanho de binário e tempo de inicialização
- Tauri 2 Plugin System Guide — Tutorial oficial sobre criação e uso de plugins customizados
- Tauri Security Model: Capabilities and Permissions — Documentação detalhada sobre o sistema de permissões e boas práticas de segurança
- Migrating from Electron to Tauri: A Case Study — Relato de uma equipe que migrou um app real de Electron para Tauri, com métricas de ganho
- Tauri Updater Plugin Documentation — Guia oficial para implementar atualizações automáticas com servidor de manifestos
- Rust and WebAssembly Book — Recurso complementar para quem deseja aprofundar no backend Rust do Tauri