An Electron app on my Mac uses 300MB of RAM to show a to-do list. The same app built with Tauri uses 15MB. The installer? 3MB vs Electron's 150MB. Same web tech, same functionality, 10x smaller.
What Tauri Offers
Tauri (open source, free):
- System webview — no bundled Chromium (that's why it's tiny)
- Rust backend — fast, secure, memory-safe
- Any frontend — React, Vue, Svelte, plain HTML
- Cross-platform — macOS, Windows, Linux, iOS, Android
- Auto-updater — built-in update mechanism
- System tray — native tray icons and menus
- File system access — read/write files securely
- IPC — frontend ↔ Rust communication
- 3-10 MB installers vs Electron's 100-200 MB
Quick Start
npm create tauri-app@latest
# Choose your frontend: React, Vue, Svelte, Solid, etc.
cd my-app
npm install
npm run tauri dev
Rust Backend Commands
// src-tauri/src/main.rs
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! Welcome to Tauri.", name)
}
#[tauri::command]
async fn read_file(path: String) -> Result<String, String> {
std::fs::read_to_string(&path).map_err(|e| e.to_string())
}
#[tauri::command]
async fn save_data(data: serde_json::Value) -> Result<(), String> {
let path = dirs::data_dir().unwrap().join("myapp/data.json");
std::fs::write(&path, serde_json::to_string_pretty(&data).unwrap())
.map_err(|e| e.to_string())
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet, read_file, save_data])
.run(tauri::generate_context!())
.expect("error running app");
}
Frontend (React)
import { invoke } from '@tauri-apps/api/core';
import { open, save } from '@tauri-apps/plugin-dialog';
import { readTextFile, writeTextFile } from '@tauri-apps/plugin-fs';
function App() {
const [result, setResult] = useState('');
async function handleGreet() {
const msg = await invoke('greet', { name: 'World' });
setResult(msg);
}
async function handleOpenFile() {
const path = await open({ filters: [{ name: 'Text', extensions: ['txt', 'md'] }] });
if (path) {
const content = await readTextFile(path);
setResult(content);
}
}
async function handleSaveFile() {
const path = await save({ defaultPath: 'output.txt' });
if (path) {
await writeTextFile(path, result);
}
}
return (
<div>
<button onClick={handleGreet}>Greet</button>
<button onClick={handleOpenFile}>Open File</button>
<button onClick={handleSaveFile}>Save File</button>
<pre>{result}</pre>
</div>
);
}
System Tray
use tauri::{SystemTray, SystemTrayMenu, SystemTrayMenuItem};
let tray_menu = SystemTrayMenu::new()
.add_item(CustomMenuItem::new("show", "Show Window"))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new("quit", "Quit"));
let tray = SystemTray::new().with_menu(tray_menu);
tauri::Builder::default()
.system_tray(tray)
.on_system_tray_event(|app, event| {
match event {
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
"quit" => std::process::exit(0),
"show" => app.get_window("main").unwrap().show().unwrap(),
_ => {}
},
_ => {}
}
})
.run(tauri::generate_context!())
Build & Distribute
# Build for current platform
npm run tauri build
# Creates: .dmg (macOS), .msi (Windows), .AppImage (Linux)
# Size: 3-10 MB (!)
# Cross-compile
# Use GitHub Actions for multi-platform builds
Tauri vs Electron
| Tauri | Electron |
|---|---|
| 3-10 MB installer | 100-200 MB |
| 15 MB RAM | 150-300 MB RAM |
| System webview | Bundled Chromium |
| Rust backend (fast) | Node.js backend |
| Security-first | Full Node.js access |
Need desktop data collection? Check out my web scraping actors on Apify — cloud-based scraping.
Need a desktop app? Email me at spinov001@gmail.com.
Top comments (0)