← Zurück zum Blog
Electron hat ein Problem: Größe und Speicherverbrauch. Eine einfache Electron-App bringt locker 200 MB Installationsgröße und 80–150 MB RAM-Verbrauch mit — weil Chromium und Node.js komplett mitgeliefert werden. Tauri macht das anders: Das Framework nutzt die nativen WebView-Komponenten des Betriebssystems (WebView2 auf Windows, WKWebView auf macOS, WebKitGTK auf Linux) und einen schlanken Rust-Core statt eines eingebetteten Browsers.
Das Ergebnis: Desktop-Apps, die unter 5 MB groß sind, weniger als 30 MB RAM verbrauchen und trotzdem ein vollständiges modernes Web-Frontend (React, Vue, Svelte, Solid — alles möglich) mit leistungsfähigem System-Zugriff verbinden. Tauri 2.0 bringt dazu noch mobilen Support für iOS und Android, ein neues Permissions-System und ein stabiles Plugin-API.
Dieser Artikel zeigt, wie du mit Claude Code eine produktionsreife Tauri-Anwendung aufbaust — von der Projektstruktur über IPC-Commands und Systemzugriffe bis hin zu System-Tray, Auto-Updater und Cross-Platform-Builds mit GitHub Actions. Alle Code-Beispiele stammen aus realen Projekten und funktionieren mit Tauri 2.0.x.
Tauri 2.0
Rust
IPC
Mobile
Security
CI/CD
Claude Code
TypeScript
Cross-Platform
GitHub Actions
1. Tauri 2.0 Setup: Projektstruktur und Konfiguration
Der schnellste Weg zu einer neuen Tauri-App ist der offizielle CLI-Scaffolder. Du brauchst Rust (über rustup installiert) sowie Node.js. Claude Code kann beim Setup helfen, indem es das richtige Framework vorschlägt und Konfigurationsdateien direkt generiert.
Projekt-Erstellung
bashTerminal
# Rust installieren (falls noch nicht vorhanden)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
# System-Abhängigkeiten (Ubuntu/Debian)
sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget \
file libssl-dev libayatana-appindicator3-dev librsvg2-dev
# Neue Tauri-App erstellen
npm create tauri-app@latest meine-app -- \
--template react-ts \
--manager npm
cd meine-app
npm install
# Entwicklungsserver starten
npm run tauri dev
Projektstruktur
textProjektverzeichnis
meine-app/
├── src/ # React/TypeScript Frontend
│ ├── App.tsx
│ ├── main.tsx
│ └── components/
├── src-tauri/ # Rust Backend
│ ├── src/
│ │ ├── main.rs # Einstiegspunkt
│ │ ├── lib.rs # App-Logik & Command-Registration
│ │ └── commands/ # IPC-Command-Module
│ │ ├── mod.rs
│ │ ├── files.rs
│ │ └── system.rs
│ ├── capabilities/ # Permission-Dateien (neu in v2)
│ │ └── default.json
│ ├── icons/ # App-Icons (alle Größen)
│ ├── Cargo.toml # Rust-Dependencies
│ └── tauri.conf.json # Tauri-Hauptkonfiguration
├── package.json
└── vite.config.ts
tauri.conf.json — Hauptkonfiguration
jsonsrc-tauri/tauri.conf.json
{
"productName": "MeineApp",
"version": "1.0.0",
"identifier": "com.meinfirma.meineapp",
"build": {
"frontendDist": "../dist",
"devUrl": "http://localhost:5173",
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run build"
},
"app": {
"windows": [
{
"label": "main",
"title": "MeineApp",
"width": 1200,
"height": 800,
"minWidth": 800,
"minHeight": 600,
"resizable": true,
"center": true,
"visible": false
}
],
"security": {
"csp": "default-src 'self'; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'"
},
"withGlobalTauri": false
},
"bundle": {
"active": true,
"targets": "all",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
}
}
Cargo.toml — Rust-Dependencies
tomlsrc-tauri/Cargo.toml
[package]
name = "meine-app"
version = "1.0.0"
edition = "2021"
[lib]
name = "meine_app_lib"
crate-type = ["lib", "cdylib", "staticlib"]
[build-dependencies]
tauri-build = { version = "2", features = [] }
[dependencies]
tauri = { version = "2", features = ["tray-icon", "image-png"] }
tauri-plugin-fs = "2"
tauri-plugin-shell = "2"
tauri-plugin-dialog = "2"
tauri-plugin-notification = "2"
tauri-plugin-updater = "2"
tauri-plugin-process = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
thiserror = "1"
log = "0.4"
env_logger = "0.11"
[features]
custom-protocol = ["tauri/custom-protocol"]
[profile.release]
panic = "abort"
codegen-units = 1
lto = true
opt-level = "s"
strip = true
Claude Code Tipp: Cargo.toml Optimierung
Die [profile.release]-Einstellungen mit lto = true, opt-level = "s" und strip = true reduzieren die finale Binary-Größe um weitere 30–50 %. Claude Code generiert diese Konfiguration automatisch wenn du nach "kleinster Bundle-Größe" fragst.
2. IPC Commands: Rust-Backend kommuniziert mit dem Frontend
Das Herzstück jeder Tauri-App ist die Inter-Process Communication (IPC). Das Frontend (React/TypeScript) kommuniziert via invoke() mit Rust-Funktionen, die als Commands registriert werden. Tauri serialisiert dabei automatisch zwischen JSON und Rust-Typen.
Rust: Commands definieren
rustsrc-tauri/src/commands/system.rs
use tauri::State;
use serde::{Deserialize, Serialize};
use std::sync::Mutex;
// Geteilter App-Zustand
#[derive(Default)]
pub struct AppState {
pub counter: Mutex<u32>,
pub settings: Mutex<AppSettings>,
}
#[derive(Serialize, Deserialize, Clone, Default)]
pub struct AppSettings {
pub theme: String,
pub language: String,
pub notifications_enabled: bool,
}
// Einfacher Command ohne Argumente
#[tauri::command]
pub fn get_app_version() -> String {
env!("CARGO_PKG_VERSION").to_string()
}
// Command mit Argumenten und State
#[tauri::command]
pub fn increment_counter(
state: State<AppState>,
amount: u32,
) -> Result<u32, String> {
let mut counter = state.counter.lock()
.map_err(|e| e.to_string())?;
*counter += amount;
Ok(*counter)
}
// Async Command für I/O-Operationen
#[tauri::command]
pub async fn fetch_system_info() -> Result<SystemInfo, String> {
let info = SystemInfo {
os: std::env::consts::OS.to_string(),
arch: std::env::consts::ARCH.to_string(),
cpu_count: num_cpus::get() as u32,
};
Ok(info)
}
#[derive(Serialize)]
pub struct SystemInfo {
os: String,
arch: String,
cpu_count: u32,
}
// Settings-Command mit State-Management
#[tauri::command]
pub fn update_settings(
state: State<AppState>,
settings: AppSettings,
) -> Result<(), String> {
let mut current = state.settings.lock()
.map_err(|e| e.to_string())?;
*current = settings;
Ok(())
}
Rust: Commands registrieren (lib.rs)
rustsrc-tauri/src/lib.rs
mod commands;
use commands::system::{AppState, fetch_system_info, get_app_version,
increment_counter, update_settings};
use commands::files::{read_file, write_file, list_directory};
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.manage(AppState::default())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_process::init())
.invoke_handler(tauri::generate_handler![
get_app_version,
increment_counter,
fetch_system_info,
update_settings,
read_file,
write_file,
list_directory,
])
.setup(|app| {
// App nach erstem Render-Cycle sichtbar machen
if let Some(window) = app.get_webview_window("main") {
window.show()?;
window.set_focus()?;
}
Ok(())
})
.run(tauri::generate_context!())
.expect("Fehler beim Starten der Tauri-Anwendung");
}
TypeScript: invoke() im Frontend
typescriptsrc/hooks/useTauri.ts
import { invoke } from '@tauri-apps/api/core';
import { useState, useEffect } from 'react';
interface SystemInfo {
os: string;
arch: string;
cpu_count: number;
}
interface AppSettings {
theme: string;
language: string;
notifications_enabled: boolean;
}
// Typsichere Wrapper für alle Backend-Commands
export const tauriApi = {
getVersion: () =>
invoke<string>('get_app_version'),
incrementCounter: (amount: number) =>
invoke<number>('increment_counter', { amount }),
getSystemInfo: () =>
invoke<SystemInfo>('fetch_system_info'),
updateSettings: (settings: AppSettings) =>
invoke<void>('update_settings', { settings }),
};
// React Hook für System-Info
export function useSystemInfo() {
const [info, setInfo] = useState<SystemInfo | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
tauriApi.getSystemInfo()
.then(setInfo)
.catch((e) => setError(e as string));
}, []);
return { info, error };
}
Wichtig: Parameter-Naming zwischen Rust und TypeScript
Rust-Commands verwenden snake_case für Parameter-Namen. Tauri konvertiert beim Aufruf aus dem Frontend automatisch zwischen camelCase (TypeScript) und snake_case (Rust). Übergibst du { userId: 42 }, empfängt Rust user_id: 42. Claude Code kennt diese Konvention und generiert korrekte Wrapper automatisch.
3. Filesystem & Shell Access: Sicherer Systemzugriff
Tauri 2.0 führte ein granulares Capabilities-System ein. Statt einen globalen "Filesystem-Zugriff" zu erlauben, definierst du exakt welche Pfade gelesen oder geschrieben werden dürfen. Das macht Tauri-Apps deutlich sicherer als Electron-Apps.
Capability-Datei definieren
jsonsrc-tauri/capabilities/default.json
{
"identifier": "default",
"description": "Standard-Berechtigungen für Desktop",
"platforms": ["linux", "macos", "windows"],
"windows": ["main"],
"permissions": [
"core:default",
"fs:allow-app-data-read",
"fs:allow-app-data-write",
"fs:allow-app-log-read",
"fs:allow-download-read",
"shell:allow-open",
"dialog:allow-open",
"dialog:allow-save",
"dialog:allow-message",
"notification:default",
"process:allow-restart"
]
}
Rust: Filesystem-Commands mit Security
rustsrc-tauri/src/commands/files.rs
use tauri::{AppHandle, Manager};
use serde::Serialize;
use std::fs;
#[derive(Serialize)]
pub struct FileEntry {
name: String,
path: String,
is_dir: bool,
size: u64,
}
#[tauri::command]
pub fn read_file(app: AppHandle, filename: String) -> Result<String, String> {
let base = app.path().app_data_dir().map_err(|e| e.to_string())?;
let path = base.join(&filename);
// Path-Traversal verhindern (Security!)
if !path.starts_with(&base) {
return Err("Ungültiger Dateipfad".to_string());
}
fs::read_to_string(&path).map_err(|e| e.to_string())
}
#[tauri::command]
pub fn write_file(
app: AppHandle,
filename: String,
content: String,
) -> Result<(), String> {
let base = app.path().app_data_dir().map_err(|e| e.to_string())?;
let path = base.join(&filename);
if !path.starts_with(&base) {
return Err("Ungültiger Dateipfad".to_string());
}
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).map_err(|e| e.to_string())?;
}
fs::write(&path, content.as_bytes()).map_err(|e| e.to_string())
}
#[tauri::command]
pub fn list_directory(
app: AppHandle,
subdir: Option<String>,
) -> Result<Vec<FileEntry>, String> {
let base = app.path().app_data_dir().map_err(|e| e.to_string())?;
let target = subdir
.map(|s| base.join(s))
.unwrap_or(base.clone());
if !target.starts_with(&base) {
return Err("Ungültiger Pfad".to_string());
}
let entries = fs::read_dir(&target)
.map_err(|e| e.to_string())?
.filter_map(|entry| entry.ok())
.map(|entry| {
let meta = entry.metadata().ok();
FileEntry {
name: entry.file_name().to_string_lossy().to_string(),
path: entry.path().to_string_lossy().to_string(),
is_dir: meta.as_ref().map(|m| m.is_dir()).unwrap_or(false),
size: meta.as_ref().map(|m| m.len()).unwrap_or(0),
}
})
.collect();
Ok(entries)
}
Security-Hinweis: Pfad-Traversal verhindern
Immer den Zielpfad gegen das erlaubte Basis-Verzeichnis prüfen (path.starts_with(&base)). Ohne diese Prüfung könnten Angreifer via ../../../etc/passwd beliebige Systemdateien lesen. Claude Code fügt diese Prüfung standardmäßig bei jedem Filesystem-Command ein.
Shell-Zugriff mit Allowlist (src-tauri/capabilities/shell-access.json)
jsonsrc-tauri/capabilities/shell-access.json
{
"identifier": "shell-access",
"windows": ["main"],
"permissions": [
{
"identifier": "shell:allow-execute",
"allow": [
{
"name": "ffmpeg",
"cmd": "ffmpeg",
"args": true
},
{
"name": "git-status",
"cmd": "git",
"args": ["status", "--porcelain"]
}
]
}
]
}
4. System Tray & Notifications: Native Desktop-Integration
Ein System-Tray-Icon macht deine Tauri-App zu einem echten Desktop-Bürger. Nutzer können die App minimieren und trotzdem über das Tray-Icon schnell auf Funktionen zugreifen. Tauri 2.0 bietet dafür eine deutlich verbesserte API gegenüber Tauri 1.x.
Tray-Icon und Menü einrichten
rustsrc-tauri/src/lib.rs (Tray-Erweiterung)
use tauri::{
AppHandle, Manager, Runtime,
menu::{Menu, MenuItem, PredefinedMenuItem},
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
image::Image,
};
fn setup_tray<R: Runtime>(app: &tauri::App<R>) -> tauri::Result<()> {
let open = MenuItem::with_id(app, "open", "Öffnen", true, None::<&str>)?;
let separator = PredefinedMenuItem::separator(app)?;
let check_updates = MenuItem::with_id(
app, "check_updates", "Nach Updates suchen", true, None::<&str>
)?;
let quit = MenuItem::with_id(app, "quit", "Beenden", true, None::<&str>)?;
let menu = Menu::with_items(app, &[&open, &separator, &check_updates, &quit])?;
let icon_bytes = include_bytes!("../icons/tray-icon.png");
let icon = Image::from_bytes(icon_bytes)?;
let _tray = TrayIconBuilder::new()
.icon(icon)
.menu(&menu)
.tooltip("MeineApp")
.icon_as_template(true)
.on_menu_event(|app, event| match event.id.as_ref() {
"open" => {
if let Some(window) = app.get_webview_window("main") {
window.show().unwrap();
window.set_focus().unwrap();
}
}
"check_updates" => {
app.emit("check-for-updates", ()).unwrap();
}
"quit" => app.exit(0),
_ => {}
})
.on_tray_icon_event(|tray, event| {
if let TrayIconEvent::Click {
button: MouseButton::Left,
button_state: MouseButtonState::Up, ..
} = event {
let app = tray.app_handle();
if let Some(window) = app.get_webview_window("main") {
if window.is_visible().unwrap_or(false) {
window.hide().unwrap();
} else {
window.show().unwrap();
window.set_focus().unwrap();
}
}
}
})
.build(app)?;
Ok(())
}
Desktop-Benachrichtigungen
rustsrc-tauri/src/commands/notify.rs
use tauri_plugin_notification::NotificationExt;
use tauri::AppHandle;
#[tauri::command]
pub fn send_notification(
app: AppHandle,
title: String,
body: String,
) -> Result<(), String> {
app.notification()
.builder()
.title(&title)
.body(&body)
.show()
.map_err(|e| e.to_string())
}
typescriptsrc/lib/notify.ts
import { isPermissionGranted, requestPermission }
from '@tauri-apps/plugin-notification';
import { invoke } from '@tauri-apps/api/core';
export async function sendDesktopNotification(title: string, body: string) {
let permission = await isPermissionGranted();
if (!permission) {
permission = (await requestPermission()) === 'granted';
}
if (permission) {
await invoke('send_notification', { title, body });
}
}
5. Auto-Updater: Nahtlose Updates via GitHub Releases
Ein professioneller Auto-Updater ist für produktive Desktop-Apps unverzichtbar. Tauri 2.0 bringt tauri-plugin-updater mit, der Updates von GitHub Releases, eigenen Servern oder S3 herunterladen kann. Updates werden kryptographisch signiert und vor der Installation verifiziert.
Updater konfigurieren und Signing-Keys erstellen
jsonsrc-tauri/tauri.conf.json (Updater-Section)
{
"plugins": {
"updater": {
"active": true,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXkuLi4K...",
"endpoints": [
"https://github.com/mein-org/meine-app/releases/latest/download/latest.json"
],
"dialog": false,
"windows": {
"installerArgs": ["/passive"]
}
}
}
}
bashTerminal: Keys generieren
# Schlüsselpaar für Update-Signatur generieren
npm run tauri signer generate -- -w ~/.tauri/meine-app.key
# Öffentlichen Schlüssel anzeigen (→ in tauri.conf.json eintragen)
cat ~/.tauri/meine-app.key.pub
# Privaten Key als GitHub Secrets setzen
gh secret set TAURI_PRIVATE_KEY < ~/.tauri/meine-app.key
gh secret set TAURI_KEY_PASSWORD
Rust: Update-Logik mit Fortschrittsanzeige
rustsrc-tauri/src/commands/updater.rs
use tauri::{AppHandle, Emitter};
use tauri_plugin_updater::UpdaterExt;
use serde::Serialize;
#[derive(Clone, Serialize)]
struct UpdateInfo {
version: String,
notes: Option<String>,
pub_date: Option<String>,
}
#[tauri::command]
pub async fn check_for_updates(app: AppHandle) -> Result<Option<UpdateInfo>, String> {
let updater = app.updater_builder()
.header("User-Agent", "MeineApp-Updater/1.0")
.map_err(|e| e.to_string())?
.build()
.map_err(|e| e.to_string())?;
match updater.check().await.map_err(|e| e.to_string())? {
Some(update) => Ok(Some(UpdateInfo {
version: update.version.clone(),
notes: update.body.clone(),
pub_date: update.date.map(|d| d.to_string()),
})),
None => Ok(None),
}
}
#[tauri::command]
pub async fn install_update(app: AppHandle) -> Result<(), String> {
let updater = app.updater_builder()
.header("User-Agent", "MeineApp-Updater/1.0")
.map_err(|e| e.to_string())?
.build()
.map_err(|e| e.to_string())?;
if let Some(update) = updater.check().await.map_err(|e| e.to_string())? {
let app_clone = app.clone();
update.download_and_install(
|chunk, total| {
app_clone.emit("update-progress", serde_json::json!({
"downloaded": chunk,
"total": total
})).ok();
},
|| { app_clone.emit("update-installed", ()).ok(); },
).await.map_err(|e| e.to_string())?;
app.restart();
}
Ok(())
}
TypeScript: Update-Dialog-Komponente
typescriptsrc/components/UpdateDialog.tsx
import { useState, useEffect } from 'react';
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
interface UpdateInfo {
version: string;
notes?: string;
}
export function UpdateDialog() {
const [update, setUpdate] = useState<UpdateInfo | null>(null);
const [progress, setProgress] = useState(0);
const [installing, setInstalling] = useState(false);
useEffect(() => {
invoke<UpdateInfo | null>('check_for_updates').then(setUpdate);
const unlisten = listen<{downloaded: number; total?: number}>(
'update-progress',
({ payload }) => {
if (payload.total) {
setProgress(Math.round(payload.downloaded / payload.total * 100));
}
}
);
return () => { unlisten.then(fn => fn()); };
}, []);
if (!update) return null;
return (
<div className="update-dialog">
<h3>Update verfügbar: v{update.version}</h3>
{update.notes && <p>{update.notes}</p>}
{installing ? (
<progress value={progress} max=100>{progress}%</progress>
) : (
<button onClick={() => { setInstalling(true); invoke('install_update'); }}>
Jetzt aktualisieren
</button>
)}
</div>
);
}
6. Cross-Platform Builds: GitHub Actions Matrix für Windows, macOS und Linux
Das Bauen für alle Plattformen gleichzeitig erfordert echte Maschinen des jeweiligen Betriebssystems — Cross-Compilation für Desktop-GUIs ist in der Praxis problematisch. GitHub Actions bietet eine Matrix-Build-Strategy, die parallel auf allen drei Betriebssystemen baut und die fertigen Installer direkt in GitHub Releases hochlädt.
Was wird pro Plattform gebaut?
- Windows: .msi (NSIS Installer) + portable .exe
- macOS: .dmg + .app (Universal Binary: Intel + Apple Silicon)
- Linux: .deb + .AppImage + .rpm
GitHub Actions Workflow — Vollständig
yaml.github/workflows/release.yml
name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- platform: 'macos-latest'
args: '--target aarch64-apple-darwin'
- platform: 'macos-latest'
args: '--target x86_64-apple-darwin'
- platform: 'ubuntu-22.04'
args: ''
- platform: 'windows-latest'
args: ''
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.platform == 'macos-latest' &&
'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
- uses: swatinem/rust-cache@v2
with:
workspaces: './src-tauri -> target'
- name: Linux-Abhängigkeiten installieren
if: matrix.platform == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
libappindicator3-dev \
librsvg2-dev \
patchelf
- run: npm ci
- uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
with:
tagName: ${{ github.ref_name }}
releaseName: 'MeineApp v__VERSION__'
releaseBody: 'Changelog: https://github.com/mein-org/meine-app/blob/main/CHANGELOG.md'
releaseDraft: true
prerelease: false
args: ${{ matrix.args }}
Lokaler Build und Debugging
bashTerminal
# Alle Installer für aktuelle Plattform bauen
npm run tauri build
# Debug-Build (kein Strip/LTO, schnellere Compilation)
npm run tauri build -- --debug
# macOS: Universal Binary (Intel + ARM in einer Datei)
npm run tauri build -- --target universal-apple-darwin
# Build-Artefakte anzeigen
ls src-tauri/target/release/bundle/
# Abhängigkeiten analysieren (Cargo-Audit)
cargo audit
cargo bloat --release --crates
Tauri 2.0 vs. Electron 2026 — Direktvergleich
| Kriterium |
Tauri 2.0 |
Electron 33 |
Wails (Go) |
| Bundle-Größe |
2–5 MB |
80–200 MB |
8–15 MB |
| RAM-Verbrauch |
20–40 MB |
100–300 MB |
25–50 MB |
| Startup-Zeit |
< 200 ms |
500–1500 ms |
< 300 ms |
| Backend-Sprache |
Rust |
Node.js |
Go |
| Mobile Support (iOS/Android) |
Ja (v2.0) |
Nein |
Nein |
| Web-Renderer |
System WebView |
Chromium (gebündelt) |
System WebView |
| Sicherheitsmodell |
Capabilities-System |
Node.js-Prozess |
Go-Prozess |
| Claude Code Support |
Vollständig |
Vollständig |
Partiell |
Wann Electron bevorzugen?
Electron ist sinnvoll wenn du maximale Browser-Kompatibilität benötigst (WebKitGTK auf Linux hat Lücken), ein sehr großes Node.js-Ökosystem nutzen willst, oder wenn die Team-Expertise ausschließlich in JavaScript liegt. Für neue Projekte 2026 ist Tauri in den meisten Fällen die bessere Wahl — vor allem wenn Installationsgröße und Performance zählen.
Claude Code als Tauri-Entwicklungspartner
Claude Code kennt die Tauri 2.0 API vollständig und kann bei diesen typischen Aufgaben sofort helfen:
-
1
IPC-Design: "Erstelle einen typsicheren IPC-Command für SQLite-Operationen mit Connection-Pooling und Fehlerbehandlung via thiserror."
-
2
Performance: "Analysiere diesen Rust-Command und optimiere ihn für niedrige Latenz. Nutze async/await und rayon für CPU-intensive Operationen."
-
3
Security-Review: "Überprüfe alle Filesystem-Commands auf Pfad-Traversal und stelle sicher, dass Capabilities minimal gehalten werden."
-
4
CI/CD-Setup: "Erweitere den GitHub Actions Workflow um automatisches Code-Signing für macOS notarization und Windows SmartScreen."
-
5
Plugin-Entwicklung: "Erstelle ein Custom Tauri Plugin für lokale SQLite-Verschlüsselung mit sqlcipher und vollständiger TypeScript-API."
Bonus: Mobile Apps mit Tauri 2.0
Das gleiche Rust-Backend läuft auch auf iOS und Android. Minimales Setup:
rustup target add aarch64-linux-android aarch64-apple-ios
npm run tauri android init → npm run tauri android dev
npm run tauri ios dev (nur auf macOS)
Tauri-App mit Claude Code entwickeln
Lass Claude Code dein Tauri-Entwicklungspartner sein. Von der Projektstruktur über IPC-Commands bis hin zu Production-Builds — Claude kennt die komplette Tauri 2.0 API und schreibt sauberen, sicheren Rust-Code.
Kostenlos testen — 14 Tage Trial
Keine Kreditkarte erforderlich · Setup in 2 Minuten