# Tauri Core Architecture

> Use when creating new Tauri 2 apps, understanding project structure, or reasoning about the component model. Prevents mixing Tauri 1.x architecture assumptions with the v2 multi-webview and capability-based model. Covers Rust backend structure, webview layer, IPC bridge model, process model, project layout, and type hierarchy. Keywords: tauri architecture, project structure, IPC bridge, webview layer, process model, Rust backend, how Tauri works, project layout, frontend backend split, getting started, what is IPC..

- Skill: `impertio-studio/tauri-core-architecture` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add impertio-studio/tauri-core-architecture`
- Raw SKILL.md: https://api.skillmd.com/api/skills/impertio-studio/tauri-core-architecture/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- License: MIT
- Author: Impertio-Studio (https://skillmd.com/u/impertio-studio)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/impertio-studio/tauri-core-architecture

---


# tauri-core-architecture

## Quick Reference

### Architecture Layers (Tauri 2.x)

| Layer | Technology | Location | Role |
|-------|-----------|----------|------|
| Rust Backend | Rust + Tokio | `src-tauri/src/` | Application logic, system access, state management |
| Webview Layer | Platform-native webview | Embedded | Renders HTML/CSS/JS frontend |
| IPC Bridge | JSON-serialized messages | Internal | Connects frontend to backend via `invoke()` and events |
| Plugin System | Rust crate + npm package | `Cargo.toml` + `package.json` | Extends capabilities (fs, dialogs, HTTP, etc.) |
| Permission System | Capabilities + permissions | `src-tauri/capabilities/` | Fine-grained access control (replaces v1 allowlist) |

### Platform Webview Engines

| Platform | Webview Engine |
|----------|---------------|
| Windows | WebView2 (Chromium-based) |
| macOS | WKWebView |
| Linux | WebKitGTK |
| iOS | WKWebView |
| Android | Android WebView |

### Key Dependencies

| Package | Type | Purpose |
|---------|------|---------|
| `@tauri-apps/cli` | devDependency (npm) | CLI tooling for dev/build |
| `@tauri-apps/api` | dependency (npm) | Frontend API for invoke, events, paths |
| `tauri` | dependency (Cargo) | Core Rust framework |
| `tauri-build` | build-dependency (Cargo) | Build script helpers |
| `serde` + `serde_json` | dependency (Cargo) | Serialization for IPC |

### Critical Warnings

**NEVER** block the main thread with synchronous I/O in commands -- ALWAYS use `async` for file, network, or long-running operations. Blocking freezes the entire UI.

**NEVER** call `.invoke_handler()` more than once on Builder -- only the LAST call takes effect. Put ALL commands in a single `generate_handler![]` macro.

**NEVER** mark command functions as `pub` when defined directly in `lib.rs` -- the glue code generation prevents it. Move commands to a separate module if they need to be public.

**NEVER** wrap managed state in `Arc` -- Tauri wraps state in `Arc` internally. Using `app.manage(Arc::new(data))` adds a redundant layer.

**NEVER** use `&str` in async command parameters -- borrowed references do not work with async command spawning. ALWAYS use `String` instead.

**NEVER** use `State<'_, T>` when you registered `Mutex<T>` -- this causes a runtime panic, not a compile error. ALWAYS match the exact registered type: `State<'_, Mutex<T>>`.

---

## Process Model

Tauri 2 uses a **two-process architecture**:

### Main Process (Rust)

- Runs the Rust backend on the main thread + Tokio async runtime
- Has full system access (filesystem, network, OS APIs)
- Manages application lifecycle, windows, menus, tray icons
- Exposes `#[tauri::command]` functions as IPC endpoints
- Manages state via `app.manage()` with `Arc`-wrapped storage

### Webview Process (Frontend)

- Runs in a platform-native webview (NOT Electron/Chromium bundle)
- Renders HTML/CSS/JS like a browser tab
- Communicates with Rust ONLY through the IPC bridge
- Has NO direct system access -- all system operations go through `invoke()`
- Event listeners enable bidirectional pub/sub messaging

### IPC Bridge

The bridge between processes uses JSON-serialized messages:

```
Frontend                          Rust Backend
   |                                   |
   |--- invoke('cmd', {args}) -------->|  (JSON request)
   |                                   |--- execute command
   |<-- Result<T, E> (JSON) ----------|  (JSON response)
   |                                   |
   |--- emit('event', payload) ------->|  (event pub/sub)
   |<-- emit('event', payload) --------|  (event pub/sub)
   |                                   |
   |<-- Channel.send(data) -----------|  (streaming)
```

- `invoke()` sends a JSON object with camelCase keys; Rust receives snake_case parameters
- Events use `Serialize + Clone` payloads
- Channels enable streaming multiple messages from Rust to JS
- Binary data can bypass JSON via `tauri::ipc::Response`

---

## Project Structure

```
my-tauri-app/
├── src/                        # Frontend source (React/Vue/Svelte/vanilla)
│   ├── index.html
│   ├── main.ts
│   └── styles.css
├── src-tauri/                  # Rust backend
│   ├── src/
│   │   ├── lib.rs              # Main app logic (mobile entry point)
│   │   └── main.rs             # Desktop entry point (calls lib.rs)
│   ├── capabilities/           # Permission capability files (JSON/TOML)
│   │   └── default.json        # Default capabilities
│   ├── permissions/            # Custom command permissions (TOML only)
│   ├── icons/                  # Application icons (all required sizes)
│   ├── gen/                    # Generated files (DO NOT edit manually)
│   │   └── schemas/
│   │       ├── desktop-schema.json
│   │       ├── mobile-schema.json
│   │       └── remote-schema.json
│   ├── Cargo.toml              # Rust dependencies
│   ├── Cargo.lock              # Deterministic build lock
│   ├── tauri.conf.json         # Main configuration
│   ├── build.rs                # Cargo build script
│   └── .taurignore             # Exclude files from dev watcher
├── package.json                # Frontend dependencies
├── tsconfig.json               # TypeScript config (if applicable)
└── vite.config.ts              # Bundler config (if Vite)
```

### Source Control Rules

- **ALWAYS commit**: `src-tauri/Cargo.lock` (deterministic builds)
- **ALWAYS ignore**: `src-tauri/target/` (build artifacts)
- **NEVER edit**: `src-tauri/gen/` (auto-generated schemas)

### Entry Point Pattern (Tauri 2.x)

Desktop and mobile share logic through `lib.rs`:

```rust
// src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![/* commands */])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

// src-tauri/src/main.rs (desktop only)
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
    app_lib::run();
}
```

**ALWAYS** use the `lib.rs` + `main.rs` split pattern for Tauri 2 -- this is required for mobile support and is the standard project layout.

---

## Type Hierarchy

### Core Types

| Type | Description | Traits |
|------|------------|--------|
| `App` | Full application instance (only in `setup()`) | `Manager`, `Emitter`, `Listener` |
| `AppHandle` | Lightweight clone-safe handle to the app | `Manager`, `Emitter`, `Listener`, `Clone + Send + Sync` |
| `WebviewWindow` | Combined window + webview (most common) | `Manager`, `Emitter`, `Listener` |
| `Window` | OS-level window (without webview) | `Manager`, `Emitter`, `Listener` |
| `Webview` | Webview inside a window | `Manager`, `Emitter`, `Listener` |
| `State<'_, T>` | Managed state accessor in commands | -- |
| `Builder` | Application configuration builder | -- |

### The Manager Trait

The `Manager` trait is the unifying interface. It is implemented by `App`, `AppHandle`, `Webview`, `WebviewWindow`, and `Window`. Key methods:

| Method | Return Type | Purpose |
|--------|------------|---------|
| `app_handle()` | `&AppHandle<R>` | Get the AppHandle |
| `config()` | `&Config` | Access tauri.conf.json |
| `state::<T>()` | `State<'_, T>` | Access managed state |
| `try_state::<T>()` | `Option<State<'_, T>>` | Access state without panic |
| `manage(state)` | `bool` | Register new state |
| `get_webview_window(label)` | `Option<WebviewWindow<R>>` | Get window by label |
| `webview_windows()` | `HashMap<String, WebviewWindow<R>>` | Get all windows |
| `get_window(label)` | `Option<Window<R>>` | Get OS window by label |
| `path()` | `&PathResolver<R>` | Access path resolver |
| `package_info()` | `&PackageInfo` | Get package metadata |

### The Emitter Trait

| Method | Purpose |
|--------|---------|
| `emit(event, payload)` | Broadcast to ALL targets |
| `emit_to(target, event, payload)` | Emit to a specific target |
| `emit_filter(event, payload, filter_fn)` | Emit to targets matching a filter |
| `emit_str(event, json_string)` | Emit with pre-serialized JSON |

### The Listener Trait

| Method | Purpose |
|--------|---------|
| `listen(event, handler)` | Listen for events (returns EventId) |
| `once(event, handler)` | Listen once, auto-remove after first event |
| `unlisten(id)` | Remove a listener |
| `listen_any(event, handler)` | Listen from any source |

### Runtime Generic

Most types are generic over `R: Runtime`. With the default `wry` feature, `R` resolves to `Wry`. Use the generic form in plugins or for test mocking:

```rust
#[tauri::command]
async fn my_command<R: Runtime>(
    app: AppHandle<R>,
    window: WebviewWindow<R>,
) -> Result<(), String> {
    Ok(())
}
```

---

## Builder Pattern

The `Builder` is the single entry point for configuring a Tauri application:

```rust
tauri::Builder::default()
    .manage(MyState::default())                              // state
    .plugin(tauri_plugin_shell::init())                      // plugins
    .invoke_handler(tauri::generate_handler![cmd1, cmd2])    // commands
    .setup(|app| {                                           // init hook
        let handle = app.handle().clone();
        // app is &mut App -- full access, runs once
        Ok(())
    })
    .on_window_event(|window, event| { /* ... */ })          // window events
    .on_menu_event(|app, event| { /* ... */ })               // menu events
    .menu(|app| { /* build menu */ })                        // app menu
    .run(tauri::generate_context!())
    .expect("error running app");
```

### Builder Method Reference

| Method | Purpose |
|--------|---------|
| `manage(T)` | Register managed state |
| `plugin(P)` | Register a plugin |
| `invoke_handler(F)` | Register ALL command handlers |
| `setup(F)` | App initialization hook |
| `menu(F)` | Set the application menu |
| `on_menu_event(F)` | Menu event handler |
| `on_window_event(F)` | Window event handler |
| `on_webview_event(F)` | Webview event handler |
| `on_page_load(F)` | Page load handler |
| `on_tray_icon_event(F)` | Tray icon event handler |
| `build(Context)` | Build without running (returns `App`) |
| `run(Context)` | Build and run the app |
| `any_thread(self)` | Allow running on any thread |

---

## Frontend API Overview

| API Area | Key Functions | Import Path |
|----------|--------------|-------------|
| Commands | `invoke<T>()`, `Channel<T>` | `@tauri-apps/api/core` |
| Events | `listen()`, `emit()`, `emitTo()`, `once()` | `@tauri-apps/api/event` |
| Windows | `getCurrentWindow()`, `getAllWindows()` | `@tauri-apps/api/window` |
| Webviews | `getCurrentWebview()` | `@tauri-apps/api/webview` |
| Paths | `appDataDir()`, `join()`, `BaseDirectory` | `@tauri-apps/api/path` |
| Utilities | `convertFileSrc()`, `isTauri()` | `@tauri-apps/api/core` |
| Testing | `mockIPC()`, `mockWindows()`, `clearMocks()` | `@tauri-apps/api/mocks` |

---

## Reference Links

- [references/methods.md](references/methods.md) -- API signatures for App, AppHandle, Manager, Runtime, Webview, WebviewWindow
- [references/examples.md](references/examples.md) -- Working code examples verified against Tauri 2.x documentation
- [references/anti-patterns.md](references/anti-patterns.md) -- What NOT to do, with WHY explanations

### Official Sources

- https://v2.tauri.app/develop/
- https://docs.rs/tauri/latest/tauri/
- https://v2.tauri.app/reference/javascript/api/
- https://v2.tauri.app/start/create-project/

