---
name: migrating-tauri-apps
description: "Migrate Tauri apps from v1 or v2 beta to v2 stable, covering breaking changes, configuration updates, API migrations, and plugin system changes. USE WHEN upgrading a Tauri project to v2 stable, resolving v1-to-v2 breaking changes, or running the automated Tauri migrate CLI."
cluster: tauri
version: 1.0.0
---

# Tauri Migration Guide

This skill covers migrating Tauri applications to v2 stable from either v1 or v2 beta.

## Migration Paths

| Source Version | Target | Complexity |
|----------------|--------|------------|
| Tauri v1.x | v2 stable | High - significant breaking changes |
| Tauri v2 beta | v2 stable | Low - minor breaking changes |

---

## Automated Migration

Both migration paths support automated migration via the Tauri CLI:

```bash
# Install latest CLI first
npm install @tauri-apps/cli@latest

# Run migration
npm run tauri migrate
# or: yarn tauri migrate | pnpm tauri migrate | cargo tauri migrate
```

**IMPORTANT:** The migrate command automates most tasks but is NOT a complete substitute for manual review. Always verify changes after running.

---

## V1 to V2 Migration

### Configuration File Changes

#### BREAKING: Top-Level Structure Changes

**Before (v1):**
```json
{
  "package": {
    "productName": "my-app",
    "version": "1.0.0"
  },
  "tauri": {
    "bundle": { ... },
    "allowlist": { ... }
  }
}
```

**After (v2):**
```json
{
  "productName": "my-app",
  "version": "1.0.0",
  "mainBinaryName": "my-app",
  "identifier": "com.example.myapp",
  "app": { ... },
  "bundle": { ... }
}
```

#### Key Renames

| v1 Path | v2 Path |
|---------|---------|
| `package.productName` | `productName` (top-level) |
| `package.version` | `version` (top-level) |
| `tauri` | `app` |
| `tauri.bundle` | `bundle` (top-level) |
| `tauri.bundle.identifier` | `identifier` (top-level) |
| `tauri.systemTray` | `app.trayIcon` |
| `build.distDir` | `frontendDist` |
| `build.devPath` | `devUrl` |

#### BREAKING: New Required Field

Add `mainBinaryName` matching your `productName` - this is no longer automatic:

```json
{
  "productName": "My App",
  "mainBinaryName": "My App"
}
```

#### Bundle Configuration Reorganization

Platform-specific bundle configs moved under their platform key:

**Before:**
```json
{
  "tauri": {
    "bundle": {
      "dmg": { ... },
      "deb": { ... }
    }
  }
}
```

**After:**
```json
{
  "bundle": {
    "macOS": {
      "dmg": { ... }
    },
    "linux": {
      "deb": { ... }
    }
  }
}
```

#### Updater Configuration

If using the app updater, add to bundle config:

```json
{
  "bundle": {
    "createUpdaterArtifacts": "v1Compatible"
  }
}
```

Use `"v1Compatible"` for existing distributions to maintain backward compatibility.

---

### BREAKING: Allowlist Replaced with Capabilities

The v1 allowlist system is completely replaced with a capability-based ACL system.

#### Creating Capabilities

Create JSON files in `src-tauri/capabilities/`:

**src-tauri/capabilities/default.json:**
```json
{
  "identifier": "default",
  "description": "Default capabilities for the main window",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "shell:allow-open",
    "dialog:allow-open",
    "fs:allow-read-text-file"
  ]
}
```

The `tauri migrate` command auto-generates capabilities from your v1 allowlist.

---

### Cargo.toml Changes

#### Removed Features

These features no longer exist in v2:
- `reqwest-client`
- `reqwest-native-tls-vendored`
- `process-command-api`
- `shell-open-api`
- `windows7-compat`
- `updater`
- `system-tray`

#### New Features

- `linux-protocol-body` - Custom protocol request body parsing support

### BREAKING: API Module Removal

The entire `api` module is removed. Functionality moved to plugins:

| v1 API | v2 Replacement |
|--------|----------------|
| `tauri::api::dialog` | `tauri-plugin-dialog` |
| `tauri::api::http` | `tauri-plugin-http` |
| `tauri::api::process` | `tauri-plugin-process` |
| `tauri::api::path` functions | `tauri::Manager::path` |

### BREAKING: Rust API Changes

#### Removed APIs

| v1 | v2 Alternative |
|----|----------------|
| `App::clipboard_manager` | `tauri-plugin-clipboard-manager` |
| `App::global_shortcut_manager` | `tauri-plugin-global-shortcut` |
| `App::get_cli_matches` | `tauri-plugin-cli` |
| `tauri::updater` | `tauri-plugin-updater` |

#### Renamed Types/Methods

| v1 | v2 |
|----|-----|
| `Window` | `WebviewWindow` |
| `Manager::get_window` | `Manager::get_webview_window` |

#### Menu API Changes

**Before:**
```rust
use tauri::{Menu, CustomMenuItem};
let menu = Menu::new()
    .add_item(CustomMenuItem::new("quit", "Quit"));
```

**After:**
```rust
use tauri::menu::{MenuBuilder, MenuItemBuilder};
let menu = MenuBuilder::new(app)
    .item(&MenuItemBuilder::with_id("quit", "Quit").build(app)?)
    .build()?;
```

#### Tray API Changes

**Before:**
```rust
use tauri::SystemTray;
SystemTray::new().with_menu(menu);
```

**After:**
```rust
use tauri::tray::TrayIconBuilder;
TrayIconBuilder::new()
    .menu(&menu)
    .on_menu_event(|app, event| { ... })
    .on_tray_icon_event(|tray, event| { ... })
    .build(app)?;
```

---

### BREAKING: JavaScript API Changes

#### Package Renames

| v1 | v2 |
|----|-----|
| `@tauri-apps/api/tauri` | `@tauri-apps/api/core` |
| `@tauri-apps/api/window` | `@tauri-apps/api/webviewWindow` |

#### Core API Reduction

The core `@tauri-apps/api` package now only includes:
- `core`
- `path`
- `event`
- `webviewWindow`

All other APIs require plugin packages.

---

### BREAKING: Plugin Migration

All formerly built-in APIs are now separate plugins:

| v1 Import | v2 Plugin Package |
|-----------|-------------------|
| `@tauri-apps/api/cli` | `@tauri-apps/plugin-cli` |
| `@tauri-apps/api/clipboard` | `@tauri-apps/plugin-clipboard-manager` |
| `@tauri-apps/api/dialog` | `@tauri-apps/plugin-dialog` |
| `@tauri-apps/api/fs` | `@tauri-apps/plugin-fs` |
| `@tauri-apps/api/global-shortcut` | `@tauri-apps/plugin-global-shortcut` |
| `@tauri-apps/api/http` | `@tauri-apps/plugin-http` |
| `@tauri-apps/api/notification` | `@tauri-apps/plugin-notification` |
| `@tauri-apps/api/os` | `@tauri-apps/plugin-os` |
| `@tauri-apps/api/process` | `@tauri-apps/plugin-process` |
| `@tauri-apps/api/shell` | `@tauri-apps/plugin-shell` |
| `@tauri-apps/api/updater` | `@tauri-apps/plugin-updater` |

#### Installing Plugins

```bash
# JavaScript
npm install @tauri-apps/plugin-fs

# Rust (add to Cargo.toml)
cargo add tauri-plugin-fs
```

Register plugins in your Rust code:

```rust
fn main() {
    tauri::Builder::default()
        .plugin(tauri_plugin_fs::init())
        .run(tauri::generate_context!())
        .expect("error running app");
}
```

### BREAKING: File System Plugin Changes

Function renames in `@tauri-apps/plugin-fs`:

| v1 | v2 |
|----|-----|
| `createDir` | `mkdir` |
| `readBinaryFile` | `readFile` |
| `writeBinaryFile` | `writeFile` |
| `removeDir` | `remove` |
| `removeFile` | `remove` |
| `renameFile` | `rename` |
| `Dir` enum | `BaseDirectory` |

### BREAKING: Event System Changes

| v1 | v2 |
|----|-----|
| `emit()` | Broadcasts to ALL listeners (behavior change) |
| N/A | `emit_to()` - target specific event targets |
| `listen_global` | `listen_any` |

### BREAKING: Windows Origin URL

Production Windows apps now serve from `http://tauri.localhost` instead of `https://`.

**Impact:** IndexedDB and cookies will reset unless you preserve the old behavior:

```json
{
  "app": {
    "windows": [{
      "useHttpsScheme": true
    }]
  }
}
```

### BREAKING: Environment Variables

| v1 | v2 |
|----|-----|
| `TAURI_PRIVATE_KEY` | `TAURI_SIGNING_PRIVATE_KEY` |
| `TAURI_KEY_PASSWORD` | `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` |
| `TAURI_DEV_SERVER_PORT` | `TAURI_CLI_PORT` |
| Platform variables | Now prefixed `TAURI_ENV_` |

### Mobile Support Setup

To target mobile alongside desktop:

**1. Update Cargo.toml:**
```toml
[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
```

**2. Rename src/main.rs to src/lib.rs:**
```rust
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .run(tauri::generate_context!())
        .expect("error running app");
}
```

**3. Create new src/main.rs:**
```rust
fn main() {
    app_lib::run();
}
```

---

## V2 Beta to V2 Stable Migration

### BREAKING: Core Permission Prefix

All core permissions now require the `"core:"` prefix:

**Before (beta):**
```json
{
  "permissions": [
    "path:default",
    "event:default",
    "window:default"
  ]
}
```

**After (stable):**
```json
{
  "permissions": [
    "core:path:default",
    "core:event:default",
    "core:window:default"
  ]
}
```

**Simplified alternative:** Use `"core:default"` to include all default core permissions:

```json
{
  "permissions": ["core:default"]
}
```

---

### BREAKING: Mobile Dev Server Configuration

The mobile development server no longer exposes across networks. Traffic tunnels directly from local machine to devices.

**Before (beta):**
```javascript
const mobile = !!/android|ios/.exec(process.env.TAURI_ENV_PLATFORM);
export default {
  server: {
    host: mobile ? '0.0.0.0' : false
  }
};
```

**After (stable):**
```javascript
const host = process.env.TAURI_DEV_HOST;
export default {
  server: {
    host: host || false
  }
};
```

Remove dependency on the `internal-ip` NPM package if previously used.

**iOS Device Development:** Requires additional steps. Use:
```bash
tauri ios dev --force-ip-prompt
```
Select the device's TUN address when prompted.

---

## Migration Checklist

### V1 to V2

- [ ] Run `npm run tauri migrate`
- [ ] Verify config structure changes (package -> top-level)
- [ ] Add `mainBinaryName` field
- [ ] Update bundle config structure
- [ ] Replace allowlist with capabilities
- [ ] Install required plugins
- [ ] Update Rust imports (Window -> WebviewWindow)
- [ ] Update JS imports (@tauri-apps/api/tauri -> core)
- [ ] Update FS function names if using fs plugin
- [ ] Update environment variable names
- [ ] Test event system behavior
- [ ] Verify Windows origin URL handling (IndexedDB/cookies)
- [ ] Update menu/tray code if used
- [ ] Remove deprecated Cargo features

### V2 Beta to V2 Stable

- [ ] Run `npm run tauri migrate`
- [ ] Add `core:` prefix to permission identifiers (or use `core:default`)
- [ ] Update mobile dev server configuration
- [ ] Remove `internal-ip` package dependency if present
- [ ] Test iOS device development with `--force-ip-prompt`

---

## Common Issues

| Issue | Solution |
|-------|----------|
| Permission denied errors | Check `src-tauri/capabilities/` files for required permissions |
| IndexedDB/localStorage lost (Windows) | Set `useHttpsScheme: true` in window config |
| Plugin not found | Add to Cargo.toml, register with `.plugin()`, install npm package |
| Mobile build fails | Verify `[lib]` section in Cargo.toml and `src/lib.rs` with mobile entry point |
