tauri2-guide/tauri2-docs/cheatsheet.md
Z User 5972c96ff9 Comprehensive improvements across all guide documents
definitive-guide.md:
- Add missing AsyncReadExt import in streaming example
- Fix v1/v2 WindowUrl table entry (v1 used WindowUrl not WebviewUrl)
- Rename 3-Step Pattern to 4-Step Pattern for plugins

agent-prompts.md:
- Add new Section 15: Prompt for debugging build/compile errors

error-resolution-guide.md:
- Improve A3 lib naming convention explanation
- Clarify G3 multiple definition root cause
- Add actionable WebView2 install commands to H1
- Add O9-O11 mobile build errors (Android SDK, macOS, CocoaPods)

cheatsheet.md:
- Add deep linking subsection with plugin example
- Add gen/schemas/ to file hierarchy
- Fix tokio::spawn -> tauri::async_runtime::spawn in WS example
- Bump tokio-tungstenite 0.21 -> 0.24

ws_server_client_inject_guide.md:
- Add WebSocket.OPEN guard to sendToAllWindows()

tauri2-links.md:
- Expand from 6 to 16 official documentation links
2026-05-30 20:45:20 +00:00

354 lines
10 KiB
Markdown

# 🚀 Tauri 2.0 Definitive Cheat Sheet
**Tauri 2.0** features a complete architecture overhaul centered around mobile support (iOS/Android), modularized core plugins, and a strict **Access Control List (ACL)** security model. This cheat sheet covers core implementation patterns with up-to-date syntax.
---
## 🛠️ Project Setup & Structure
Tauri 2.0 splits frontend configurations and introduces modular Rust codebases via `lib.rs`.
### CLI Management
```bash
# Initialize a new app
npm create tauri-app@latest
# Run development mode (Hot-reloads UI + Rust)
npm run tauri dev
# Build production artifacts
npm run tauri build
```
### File Hierarchy
```text
my-app/
├── src/ # Frontend UI (React, Vue, Svelte, etc.)
├── src-tauri/ # Backend environment
│ ├── capabilities/ # NEW: Security capability JSON/TOML files
│ │ └── default.json # Maps app windows to permissions
│ ├── gen/
│ │ └── schemas/ # Generated JSON schemas (referenced by $schema in capabilities)
│ ├── src/
│ │ ├── main.rs # Minimal platform entry-point
│ │ └── lib.rs # Core Application setup & commands
│ ├── Cargo.toml # Rust dependencies
│ └── tauri.conf.json # Desktop/Mobile global window configurations
```
---
## 🔒 Security: The Tauri 2.0 Permission System
Tauri 2.0 enforces explicit privileges. Frontend access to system commands or plugins requires configuring a **Permission** inside a **Capability** map.
### 1. Identify Plugin Permissions
Official plugins expose granular identifier strings:
* **Core Permissions**: `core:path:allow-home`, `core:event:allow-listen`, `core:event:allow-emit`.
* **Plugin Permissions**: `<plugin-name>:allow-<command>` (e.g., `http:allow-request`, `fs:allow-read`).
### 2. Define Capabilities (`src-tauri/capabilities/default.json`)
Capabilities attach permissions to specific application windows.
```json
{
"\$schema": "../gen/schemas/capability.json",
"identifier": "main-window-capability",
"description": "Allowed permissions for the primary UI",
"windows": ["main"],
"permissions": [
"core:path:allow-home",
"core:event:allow-listen",
"core:event:allow-emit",
"fs:allow-read",
"websocket:allow-connect",
"websocket:allow-send"
]
}
```
### 3. Fine-Grained Scopes
Scopes restrict what a plugin command can touch (e.g., locking file-system access down to a explicit directory). Define these inside your capability entries:
```json
{
"identifier": "secure-fs-scope",
"windows": ["main"],
"permissions": [
{
"identifier": "fs:allow-write",
"allow": [{ "path": "\$APPDATA/logs/*" }]
}
]
}
```
---
## 📡 WebSockets in Tauri 2.0
Tauri 2.0 approaches WebSockets from two distinct perspectives depending on requirements: utilizing the client plugin to query a external endpoint, or instantiating a native high-performance Rust WebSocket Server.
### Option A: The Built-in WebSocket Client Plugin
Use this to safely initiate connections to external services from the frontend via a high-performance Rust proxy client.
#### 1. Setup Backend Setup (`src-tauri/src/lib.rs`)
```rust
// Ensure 'tauri-plugin-websocket' is added to Cargo.toml
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_websocket::init()) // Initialize client plugin
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
```
#### 2. Frontend Execution (`JavaScript / TypeScript`)
Ensure `websocket:allow-connect` and `websocket:allow-send` are added to your active capability configuration.
```typescript
import WebSocket from '@tauri-apps/plugin-websocket';
// Connect to a server
const ws = await WebSocket.connect('ws://127.0.0.1:8080');
// Handle incoming messages
ws.addListener((msg) => {
console.log('Received payload:', msg.data);
});
// Push data out
await ws.send({ type: 'Text', data: 'Hello World!' });
// Disconnect gracefully
await ws.disconnect();
```
### Option B: Built-in Custom Rust WebSocket Server
If you want your Tauri application to **act as a WebSocket Server** (e.g., exposing a local port so mobile apps or local interfaces can stream data directly to your backend), spin up a Tokio-backed server thread inside the Tauri `setup` stage.
#### 1. Dependencies (`src-tauri/Cargo.toml`)
```toml
[dependencies]
tauri = { version = "2.0", features = [] }
tokio = { version = "1", features = ["full"] }
tokio-tungstenite = "0.24" # Industry standard high-performance WS crate
futures-util = "0.3"
```
#### 2. Server Implementation (`src-tauri/src/lib.rs`)
```rust
use tauri::Manager;
use tokio::net::TcpListener;
use futures-util::{StreamExt, SinkExt};
use tokio_tungstenite::accept_async;
async fn start_ws_server(port: &str) {
let listener = TcpListener::bind(format!("127.0.0.1:{}", port))
.await
.expect("Failed to bind port");
while let Ok((stream, _)) = listener.accept().await {
tauri::async_runtime::spawn(async move {
if let Ok(mut ws_stream) = accept_async(stream).await {
println!("New connection established to Tauri WS Server!");
while let Some(Ok(msg)) = ws_stream.next().await {
if msg.is_text() || msg.is_binary() {
// Eco back received payloads
let _ = ws_stream.send(msg).await;
}
}
}
});
}
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.setup(|app| {
// Spin up native server safely using Tauri's async runtime (NOT tokio::spawn directly)
tauri::async_runtime::spawn(start_ws_server("8080"));
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
```
---
## ⚡ Inter-Process Communication (IPC) & State
### Commands (Invoking Rust from Frontend)
Pass data directly via JSON-RPC structures across the isolation barrier.
```rust
// src-tauri/src/lib.rs
use std::sync::Mutex;
use tauri::State;
pub struct AppState {
pub counter: Mutex<u32>,
}
#[tauri::command]
fn increment_counter(state: State<'_, AppState>, value: u32) -> u32 {
let mut counter = state.counter.lock().unwrap();
*counter += value;
*counter // Return to frontend
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.manage(AppState { counter: Mutex::new(0) }) // Inject State
.invoke_handler(tauri::generate_handler![increment_counter])
.run(tauri::generate_context!())
.expect("failed to run app");
}
```
```typescript
// Frontend Invoke Pattern
import { invoke } from '@tauri-apps/api/core';
const result = await invoke<number>('increment_counter', { value: 5 });
console.log(result); // Output: 5
```
### Events (Asynchronous Global Communication)
Ideal for un-prompted data streaming from Rust to UI components.
```rust
// Backend Emit
use tauri::{Emitter, AppHandle};
fn stream_system_status(app_handle: &AppHandle, load: f32) {
// Emits a global event to all active window contexts
app_handle.emit("cpu-status", load).unwrap();
}
```
```typescript
// Frontend Listen
import { listen } from '@tauri-apps/api/event';
// Make sure 'core:event:allow-listen' is in capabilities
const unlisten = await listen<number>('cpu-status', (event) => {
console.log(`CPU utilization: ${event.payload}%`);
});
// Execute unlisten() later to clean up memory hooks
```
---
## 📇 Deep Links & Windowing
### Multiple Window Spawning
```rust
// Rust implementation
use tauri::WebviewUrl;
fn create_extra_window(app: &tauri::AppHandle) {
let _window = tauri::WebviewWindowBuilder::new(
app,
"secondary_panel",
WebviewUrl::App("index.html/#/settings".into())
)
.title("Settings Panel")
.inner_size(600.0, 400.0)
.build()
.unwrap();
}
```
### Single Instance Lock
Ensures only one instance runs, focusing the existing window when a second launch is attempted.
```rust
// src-tauri/Cargo.toml: tauri-plugin-single-instance = "2"
// src-tauri/capabilities/default.json: add "single-instance:default" to permissions
// src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_single_instance::init(|app, args, cwd| {
println!("Attempted alternative launch instances: {:?}, working dir: {:?}", args, cwd);
// Focus primary window on secondary call triggers
if let Some(window) = app.get_webview_window("main") {
let _ = window.set_focus();
}
}))
.run(tauri::generate_context!())
.expect("failed execution");
}
```
### Deep Linking with `tauri-plugin-deep-link`
Registers a custom URL scheme (e.g., `my-app://open?token=xyz`) so the OS routes external links into your running app.
#### 1. Setup
```toml
# src-tauri/Cargo.toml
[dependencies]
tauri-plugin-deep-link = "2"
```
```json
// src-tauri/capabilities/default.json — add permission
{
"permissions": ["deep-link:default"]
}
```
```json
// src-tauri/tauri.conf.json — register the scheme
{
"app": {
"deepLink": {
"desktop": {
"schemes": ["my-app"]
},
"mobile": {
"scheme": "myapp"
}
}
}
}
```
#### 2. Rust Handler
```rust
// src-tauri/src/lib.rs
use tauri_plugin_deep_link::DeepLinkExt;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_deep_link::init())
.setup(|app| {
// macOS / Linux / Windows desktop handler
#[cfg(desktop)]
app.deep_link().on_open_url(|url| {
println!("Deep link opened: {}", url);
// Parse URL and route to the appropriate handler
});
Ok(())
})
.run(tauri::generate_context!())
.expect("failed execution");
}
```
#### 3. Frontend Listener (optional)
```typescript
import { listen } from '@tauri-apps/api/event';
// The deep-link plugin emits 'deep-link://new-url' events
await listen('deep-link://new-url', (event) => {
console.log('Received deep link:', event.payload);
});
```