blog.dopana

Back

Tauri là framework xây desktop app với web technology — như Electron nhưng nhẹ hơn, nhanh hơn, an toàn hơn.

Electron:  Chromium (~200MB) + Node.js + HTML/CSS/JS
Tauri:     WebView (OS-native) + Rust backend + HTML/CSS/JS
plaintext

Size: Electron app ~200MB. Tauri app ~2MB.

Tại Sao Tauri?#

So Sánh Với Electron#

ElectronTauri
Bundle size~200MB~2MB
Memory~100-200MB~20-50MB
BackendNode.jsRust
SecurityThấp (Node full access)Cao (allowlist API)
Buildnpm buildnpm build + cargo build
Cross-platform✅ (macOS, Windows, Linux)
Mobile✅ (iOS, Android — Tauri v2)

Tauri v2#

Tauri v2 (2025+) hỗ trợ mobile — iOS và Android:

Tauri v1:   Desktop (macOS, Windows, Linux)
Tauri v2:   Desktop + Mobile (iOS, Android)
plaintext

Cài Đặt#

# Prerequisites
# macOS: Xcode
# Linux: WebKitGTK, libsoup
# Windows: WebView2

npm create tauri-app@latest my-app
# Select: React, Vue, Svelte, Vanilla...

cd my-app
npm install
npm run tauri dev
bash

Project Structure#

my-app/
├── src/              # Frontend (React/Vue/Svelte)
├── src-tauri/
│   ├── src/
│   │   └── main.rs   # Rust backend
│   ├── Cargo.toml
│   ├── tauri.conf.json
│   └── icons/
└── package.json
plaintext

Frontend (React + Vite)#

Rust Backend#

Tauri Commands (IPC)#

Frontend gọi Rust backend qua invoke:

// Frontend
import { invoke } from '@tauri-apps/api/core'

// Gọi command
const sum = await invoke('add', { a: 5, b: 3 })
// sum: 8

// Với error handling
try {
  const content = await invoke('read_file', { path: '/etc/hosts' })
  console.log(content)
} catch (error) {
  console.error('Failed to read file:', error)
}
typescript
// Rust — command có thể return Result
#[tauri::command]
fn get_user(id: u32) -> Result<User, String> {
    let user = database::find_user(id)
        .ok_or_else(|| format!("User {} not found", id))?;
    Ok(user)
}
rust

File System#

import { readTextFile, writeTextFile } from '@tauri-apps/plugin-fs'
import { resolve } from '@tauri-apps/api/path'

// Đọc/ghi file trong app data directory
const appDir = await resolve(appDataDir())
const configPath = await resolve(appDir, 'config.json')

// Đọc
const config = await readTextFile(configPath)

// Ghi
await writeTextFile(configPath, JSON.stringify({ theme: 'dark' }))
typescript

System Tray#

Window Management#

#[tauri::command]
async fn create_window(app: tauri::AppHandle, label: String) {
    let _window = tauri::WebviewWindowBuilder::new(
        &app,
        &label,
        tauri::WebviewUrl::App("index.html".into()),
    )
    .title("New Window")
    .inner_size(800.0, 600.0)
    .build()
    .expect("Failed to create window");
}
rust

Plugin Ecosystem#

# Tauri plugins
npm run tauri add shell # Run shell commands
npm run tauri add fs # File system
npm run tauri add dialog # Native dialogs
npm run tauri add http # HTTP client
npm run tauri add store # Persistent key-value store
npm run tauri add sql # SQLite
npm run tauri add clipboard # Clipboard access
npm run tauri add updater # Auto-update
bash
import { open } from '@tauri-apps/plugin-dialog'

// Native file dialog
const file = await open({
  multiple: false,
  filters: [{ name: 'Images', extensions: ['png', 'jpg'] }],
})
typescript
import { Store } from '@tauri-apps/plugin-store'

// Persistent store
const store = await Store.load('settings.json')
await store.set('theme', 'dark')
await store.save()

const theme = await store.get<string>('theme')
typescript

Build & Distribute#

# Development
npm run tauri dev

# Build cho production
npm run tauri build
# dist/tauri/my-app.dmg (macOS)
# dist/tauri/my-app.msi (Windows)
# dist/tauri/my-app.deb (Linux)
# ~2MB — so với Electron ~200MB
bash
// tauri.conf.json — bundle config
{
  "bundle": {
    "active": true,
    "icon": ["icons/icon.png"],
    "identifier": "com.myapp.app",
    "targets": ["dmg", "msi", "deb", "appimage"]
  },
  "security": {
    "csp": "default-src 'self'; script-src 'self'"
  }
}
json

Auto-Update#

{
  "plugins": {
    "updater": {
      "pubkey": "...",
      "endpoints": [
        "https://releases.myapp.com/{{target}}-{{arch}}/{{current_version}}"
      ]
    }
  }
}
json
import { check } from '@tauri-apps/plugin-updater'

const update = await check()
if (update?.available) {
  await update.downloadAndInstall()
}
typescript

Khi Nào Dùng Tauri?#

✅ Nên dùng#

  • Desktop app mới — muốn nhẹ, fast, native
  • Cross-platform — một codebase cho macOS, Windows, Linux
  • System tool — cần Rust performance (file processing, network)
  • Mobile + Desktop — Tauri v2 cho iOS và Android
  • Security-sensitive — cần sandbox, minimal API access

❌ Không nên#

  • Desktop app cần nhiều Node.js library — Electron ecosystem lớn hơn
  • Complex native API — Electron có nhiều native module hơn
  • Prototype nhanh — Electron quen hơn nếu team biết Node
  • Cần Chromium DevTools mạnh — WebView ít tính năng hơn

Tổng Kết#

Tauri là Electron thế hệ mới: web frontend, Rust backend, bundle 1/100 kích thước.

# Tauri app — ~2MB vs Electron ~200MB
npm create tauri-app@latest my-app
cd my-app
npm run tauri dev # Dev với hot reload
npm run tauri build # Bundle ~2MB .dmg/.msi/.deb
bash
ElectronTauri
Bundle~200MB~2MB
RAM~100-200MB~20-50MB
BackendNode.jsRust 🦀
SecurityThấpCao (allowlist)
Mobile✅ (Tauri v2)
Build time1-2 phút2-5 phút (+ Rust compile)

Tauri không thay thế Electron mọi lúc — nhưng cho 80% desktop app, bundle nhỏ hơn 100x, memory ít hơn 5x là lợi thế khó bỏ qua.

Tài liệu tham khảo#