Salta ai contenuti

Tauri Cheat Sheet

Overview

Tauri is an open-source framework for building desktop applications using web technologies for the frontend and Rust for the backend. Unlike Electron, which bundles Chromium, Tauri uses the operating system’s native webview (WebView2 on Windows, WebKit on macOS/Linux), resulting in dramatically smaller binary sizes — often under 5MB compared to Electron’s 150MB+. This approach also means lower memory usage and faster startup times while still allowing developers to use familiar web frameworks like React, Vue, Svelte, or plain HTML/CSS/JS.

Tauri v2 extends beyond desktop to support mobile platforms (iOS and Android) from the same codebase. The framework provides a robust security model with fine-grained permissions, IPC (inter-process communication) between the web frontend and Rust backend, system tray support, auto-updating, and access to native OS features through a plugin system. The Rust backend enables developers to write high-performance, memory-safe code for system interactions, file operations, and native functionality.

Installation

# Prerequisites
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env

# System dependencies (Linux)
sudo apt update
sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget \
  file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev

# System dependencies (macOS) - Xcode command line tools
xcode-select --install

# System dependencies (Windows) - WebView2 and Visual Studio Build Tools
# Install from https://visualstudio.microsoft.com/visual-cpp-build-tools/

# Install Tauri CLI
cargo install tauri-cli

# Or use npm
npm install -g @tauri-apps/cli

# Create new project
cargo tauri init
# Or with create-tauri-app scaffolder
npm create tauri-app@latest my-app
cd my-app

# For mobile support (Tauri v2)
cargo tauri android init
cargo tauri ios init

Project Structure

my-tauri-app/
├── src/                    # Web frontend source
│   ├── index.html
│   ├── main.js
│   └── styles.css
├── src-tauri/              # Rust backend
│   ├── Cargo.toml          # Rust dependencies
│   ├── tauri.conf.json     # Tauri configuration
│   ├── capabilities/       # Permission capabilities
│   │   └── default.json
│   ├── icons/              # App icons
│   ├── src/
│   │   ├── main.rs         # Entry point
│   │   └── lib.rs          # Command definitions
│   └── gen/                # Generated platform code
├── package.json
└── vite.config.js          # Frontend build config

Configuration

// src-tauri/tauri.conf.json
{
  "$schema": "https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-cli/schema.json",
  "productName": "My App",
  "version": "1.0.0",
  "identifier": "com.example.myapp",
  "build": {
    "frontendDist": "../dist",
    "devUrl": "http://localhost:5173",
    "beforeDevCommand": "npm run dev",
    "beforeBuildCommand": "npm run build"
  },
  "app": {
    "windows": [
      {
        "title": "My Tauri App",
        "width": 1024,
        "height": 768,
        "resizable": true,
        "fullscreen": false,
        "minWidth": 400,
        "minHeight": 300
      }
    ],
    "security": {
      "csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost"
    }
  },
  "bundle": {
    "active": true,
    "targets": "all",
    "icon": [
      "icons/32x32.png",
      "icons/128x128.png",
      "icons/icon.icns",
      "icons/icon.ico"
    ]
  }
}

Rust Commands (Backend)

// src-tauri/src/lib.rs
use tauri::Manager;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct User {
    name: String,
    age: u32,
}

// Simple command
#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}! Welcome to Tauri.", name)
}

// Async command
#[tauri::command]
async fn fetch_data(url: String) -> Result<String, String> {
    reqwest::get(&url)
        .await
        .map_err(|e| e.to_string())?
        .text()
        .await
        .map_err(|e| e.to_string())
}

// Command with state
#[tauri::command]
fn get_count(state: tauri::State<'_, AppState>) -> u32 {
    *state.count.lock().unwrap()
}

// Command returning struct
#[tauri::command]
fn get_user() -> User {
    User {
        name: "Alice".into(),
        age: 30,
    }
}

// Register commands
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .manage(AppState::default())
        .invoke_handler(tauri::generate_handler![
            greet,
            fetch_data,
            get_count,
            get_user
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

Frontend (JavaScript/TypeScript)

import { invoke } from '@tauri-apps/api/core';
import { open, save } from '@tauri-apps/plugin-dialog';
import { readTextFile, writeTextFile } from '@tauri-apps/plugin-fs';

// Call Rust commands
async function sayHello() {
    const response = await invoke('greet', { name: 'World' });
    console.log(response); // "Hello, World! Welcome to Tauri."
}

// Call with complex parameters
const user = await invoke('get_user');
console.log(user.name, user.age);

// File dialog
const filePath = await open({
    multiple: false,
    filters: [{ name: 'Text', extensions: ['txt', 'md'] }]
});

if (filePath) {
    const contents = await readTextFile(filePath);
    console.log(contents);
}

// Save file
const savePath = await save({
    filters: [{ name: 'JSON', extensions: ['json'] }]
});
if (savePath) {
    await writeTextFile(savePath, JSON.stringify(data));
}

Events and IPC

// Emit event from Rust
#[tauri::command]
fn start_process(app: tauri::AppHandle) {
    std::thread::spawn(move || {
        for i in 0..100 {
            app.emit("progress", i).unwrap();
            std::thread::sleep(std::time::Duration::from_millis(50));
        }
        app.emit("complete", "Done!").unwrap();
    });
}
import { listen } from '@tauri-apps/api/event';

// Listen for events from Rust
const unlisten = await listen('progress', (event) => {
    console.log(`Progress: ${event.payload}%`);
    updateProgressBar(event.payload);
});

await listen('complete', (event) => {
    console.log(event.payload);
});

// Emit event to Rust
import { emit } from '@tauri-apps/api/event';
await emit('user-action', { type: 'click', target: 'button' });

// Clean up listener
unlisten();

Plugins

# Add plugins via cargo
cargo add tauri-plugin-dialog
cargo add tauri-plugin-fs
cargo add tauri-plugin-shell
cargo add tauri-plugin-notification
cargo add tauri-plugin-clipboard-manager
cargo add tauri-plugin-updater
cargo add tauri-plugin-store
cargo add tauri-plugin-sql

# Add corresponding npm packages
npm install @tauri-apps/plugin-dialog
npm install @tauri-apps/plugin-fs
npm install @tauri-apps/plugin-shell
npm install @tauri-apps/plugin-notification
// Register plugins in lib.rs
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_dialog::init())
        .plugin(tauri_plugin_fs::init())
        .plugin(tauri_plugin_shell::init())
        .plugin(tauri_plugin_notification::init())
        .plugin(tauri_plugin_store::Builder::default().build())
        .invoke_handler(tauri::generate_handler![/* commands */])
        .run(tauri::generate_context!())
        .expect("error running app");
}

Build and Deploy

# Development
cargo tauri dev

# Build for production
cargo tauri build

# Build specific targets
cargo tauri build --target x86_64-unknown-linux-gnu
cargo tauri build --target x86_64-apple-darwin
cargo tauri build --target x86_64-pc-windows-msvc

# Mobile builds
cargo tauri android build
cargo tauri ios build

# Debug build
cargo tauri build --debug

# Generate app icons from a source image
cargo tauri icon path/to/icon.png

# Output locations:
# Linux: src-tauri/target/release/bundle/deb/, appimage/
# macOS: src-tauri/target/release/bundle/dmg/, macos/
# Windows: src-tauri/target/release/bundle/msi/, nsis/

Advanced Usage

// System tray
use tauri::{
    menu::{Menu, MenuItem},
    tray::TrayIconBuilder,
};

pub fn run() {
    tauri::Builder::default()
        .setup(|app| {
            let quit = MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?;
            let show = MenuItem::with_id(app, "show", "Show Window", true, None::<&str>)?;
            let menu = Menu::with_items(app, &[&show, &quit])?;
            
            TrayIconBuilder::new()
                .icon(app.default_window_icon().unwrap().clone())
                .menu(&menu)
                .on_menu_event(|app, event| match event.id.as_ref() {
                    "quit" => app.exit(0),
                    "show" => {
                        if let Some(window) = app.get_webview_window("main") {
                            window.show().unwrap();
                            window.set_focus().unwrap();
                        }
                    }
                    _ => {}
                })
                .build(app)?;
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("error running app");
}

// Multi-window
#[tauri::command]
async fn open_settings(app: tauri::AppHandle) {
    tauri::WebviewWindowBuilder::new(
        &app,
        "settings",
        tauri::WebviewUrl::App("settings.html".into()),
    )
    .title("Settings")
    .inner_size(600.0, 400.0)
    .build()
    .unwrap();
}

Troubleshooting

IssueSolution
WebView not renderingInstall WebView2 (Windows) or webkit2gtk (Linux)
cargo tauri dev slowUse --release only for final builds; dev mode is faster
Command not found from JSEnsure command is registered in generate_handler![]
Permission denied errorsAdd required permissions in capabilities/default.json
Build fails on LinuxInstall all required system dependencies listed above
Binary too largeEnable LTO and strip symbols in Cargo.toml release profile
Hot reload not workingEnsure devUrl matches your frontend dev server port
iOS/Android build failsRun cargo tauri [ios/android] init first
CSP blocking resourcesUpdate security.csp in tauri.conf.json
Window blank on startupCheck frontend build output path matches frontendDist config