Skip to content

Deep Dive: Tauri + Playwright #3

@chhot2u

Description

@chhot2u

Tauri + Playwright — Deep Dive Analysis

Overview

Tauri uses the system WebView (WebView2/WebKitGTK) for UI with a Rust backend. Playwright runs as a Node.js sidecar process for browser automation. Ultra-lightweight desktop app with powerful automation.


Architecture

┌──────────────────────────────────────┐
│          Tauri App Shell             │
│  ┌────────────────────────────────┐  │
│  │   Rust Backend (Tokio async)   │  │
│  │   - Task scheduler             │  │
│  │   - Proxy pool manager         │  │
│  │   - SQLite via rusqlite        │  │
│  │   - IPC command handlers       │  │
│  ├────────────────────────────────┤  │
│  │   Node.js Sidecar              │  │
│  │   - Playwright engine          │  │
│  │   - 100 BrowserContexts       │  │
│  │   - Communicates via stdio/IPC │  │
│  ├────────────────────────────────┤  │
│  │   System WebView (UI)          │  │
│  │   - Svelte/React dashboard     │  │
│  │   - Task monitoring            │  │
│  │   - Live preview (1 task)      │  │
│  └────────────────────────────────┘  │
└──────────────────────────────────────┘

Key Dependencies

# Cargo.toml
[dependencies]
tauri = { version = "2.x", features = ["shell-sidecar"] }
tokio = { version = "1", features = ["full"] }
rusqlite = "0.32"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
// sidecar package.json
{
  "playwright-core": "^1.49.x"
}

Proxy per Task (via Sidecar)

// Rust side: send task config to Node sidecar
#[tauri::command]
async fn create_task(proxy: ProxyConfig, steps: Vec<Step>) -> Result<TaskId, String> {
    let task_id = Uuid::new_v4().to_string();
    let msg = serde_json::to_string(&TaskRequest {
        id: task_id.clone(),
        proxy,
        steps,
    }).map_err(|e| e.to_string())?;
    
    SIDECAR.send(msg).await.map_err(|e| e.to_string())?;
    Ok(task_id)
}
// Node.js sidecar: receives task, creates context
async function handleTask(request: TaskRequest) {
  const context = await browser.newContext({
    proxy: {
      server: request.proxy.server,
      username: request.proxy.username,
      password: request.proxy.password,
    },
  });
  const page = await context.newPage();
  await executeSteps(page, request.steps);
  await context.close();
}

Concurrency Model

// Rust manages concurrency via Tokio
let semaphore = Arc::new(Semaphore::new(100));

for task in tasks {
    let permit = semaphore.clone().acquire_owned().await.unwrap();
    tokio::spawn(async move {
        sidecar.send_task(task).await;
        drop(permit);
    });
}

Strengths

  • Tiny bundle: ~10MB (no bundled browser for UI)
  • Low RAM for UI: System WebView uses minimal memory
  • Rust performance: Tokio async for task scheduling
  • Security: Rust memory safety, Tauri's security model
  • Built-in updater: Tauri auto-update system
  • Playwright features: Full API via sidecar

Weaknesses

  • Sidecar complexity: Two runtimes (Rust + Node.js) to manage
  • IPC overhead: Cross-process communication adds latency
  • Rust learning curve: Backend requires Rust knowledge
  • WebView inconsistency: Different rendering on Win/Mac/Linux
  • Still needs Playwright binary: ~100MB download on first run
  • Debugging: Harder to debug cross-runtime issues

Resource Estimates (100 tasks)

Resource Estimate
RAM ~2 GB (Tauri UI: ~50MB, Sidecar+Contexts: ~1.9GB)
CPU 4-8 cores recommended
Disk ~10MB app + ~100MB Playwright browsers
Startup <1s app, ~2s sidecar + browser

When to Choose This Stack

Bundle size is critical
✅ Team knows Rust
✅ Want native desktop feel with minimal overhead
✅ Need security-first architecture
✅ Building for resource-constrained environments

❌ Avoid if: team doesn't know Rust, need simple single-runtime setup, or need consistent cross-platform rendering


Verdict: 7.05/10

Excellent performance-to-size ratio, but sidecar complexity and Rust requirement raise the bar.

References issue #1 for full comparison

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions