src/content/docs/start/migrate/from-tauri-1.mdx
import { Tabs, TabItem } from '@astrojs/starlight/components'; import CommandTabs from '@components/CommandTabs.astro';
This guide walks you through upgrading your Tauri 1.0 application to Tauri 2.0.
The mobile interface of Tauri requires your project to output a shared library. If you are targeting mobile for your existing application, you must change your crate to produce that kind of artifact along with the desktop executable.
// src-tauri/Cargo.toml
[lib]
name = "app_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
Rename src-tauri/src/main.rs to src-tauri/src/lib.rs. This file will be shared by both desktop and mobile targets.
Rename the main function header in lib.rs to the following:
// src-tauri/src/lib.rs
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
// your code here
}
The tauri::mobile_entry_point macro prepares your function to be executed on mobile.
main.rs file calling the shared run function:// src-tauri/src/main.rs
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
app_lib::run();
}
:::danger
This command is not a substitude for this guide! Please read the whole page regardless of whether you chose to use the command.
:::
The Tauri v2 CLI includes a migrate command that automates most of the process and helps you finish the migration:
<CommandTabs npm="npm install @tauri-apps/cli@latest npm run tauri migrate" yarn="yarn upgrade @tauri-apps/cli@latest yarn tauri migrate" pnpm="pnpm update @tauri-apps/cli@latest pnpm tauri migrate" cargo='cargo install tauri-cli --version "^2.0.0" --locked cargo tauri migrate' />
Learn more about the migrate command in the Command Line Interface reference
Below is a summary of the changes from Tauri 1.0 to Tauri 2.0:
package > productName and package > version moved to top-level object.productName automatically, so you must add a mainBinaryName string to the top-level object matching productName.package removed.tauri key renamed to app.tauri > allowlist removed. Refer to Migrate Permissions.tauri > allowlist > protocol > assetScope moved to app > security > assetProtocol > scope.tauri > cli moved to plugins > cli.tauri > windows > fileDropEnabled renamed to app > windows > dragDropEnabled.tauri > updater > active removed.tauri > updater > dialog removed.tauri > updater moved to plugins > updater.bundle > createUpdaterArtifacts added, must be set when using the app updater.
v1Compatible when upgrading from v1 apps that were already distributed. See the updater guide for more information.tauri > systemTray renamed to app > trayIcon.tauri > pattern moved to app > security > pattern.tauri > bundle moved top-level.tauri > bundle > identifier moved to top-level object.tauri > bundle > dmg moved to bundle > macOS > dmgtauri > bundle > deb moved to bundle > linux > debtauri > bundle > appimage moved to bundle > linux > appimagetauri > bundle > macOS > license removed, use bundle > licenseFile instead.tauri > bundle > windows > wix > license removed, use bundle > licenseFile instead.tauri > bundle > windows > nsis > license removed, use bundle > licenseFile instead.tauri > bundle > windows > webviewFixedRuntimePath removed, use bundle > windows > webviewInstallMode instead.build > withGlobalTauri moved to app > withGlobalTauri.build > distDir renamed to frontendDist.build > devPath renamed to devUrl.Tauri 2.0 Configuration API reference
native-tls-vendored instead.shell plugin instead (see instructions in the following section).shell plugin instead (see instructions in the following section).notification plugin.tray-icon.api module removed. Each API module can be found in a Tauri plugin.api::dialog module removed. Use tauri-plugin-dialog instead. Migrationapi::file module removed. Use Rust's std::fs instead.api::http module removed. Use tauri-plugin-http instead. Migrationapi::ip module rewritten and moved to tauri::ipc. Check out the new APIs, specially tauri::ipc::Channel.api::path module functions and tauri::PathResolved moved to tauri::Manager::path. Migrationapi::process::Command, tauri::api::shell and tauri::Manager::shell_scope APIs removed. Use tauri-plugin-shell instead. Migrationapi::process::current_binary and tauri::api::process::restart moved to tauri::process.api::version module has been removed. Use the semver crate instead.App::clipboard_manager and AppHandle::clipboard_manager removed. Use tauri-plugin-clipboard instead. MigrationApp::get_cli_matches removed. Use tauri-plugin-cli instead. MigrationApp::global_shortcut_manager and AppHandle::global_shortcut_manager removed. Use tauri-plugin-global-shortcut instead. MigrationManager::fs_scope removed. The file system scope can be accessed via tauri_plugin_fs::FsExt.Plugin::PluginApi now receives a plugin configuration as a second argument.Plugin::setup_with_config removed. Use the updated tauri::Plugin::PluginApi instead.scope::ipc::RemoteDomainAccessScope::enable_tauri_api and scope::ipc::RemoteDomainAccessScope::enables_tauri_api removed. Enable each core plugin individually via scope::ipc::RemoteDomainAccessScope::add_plugin instead.scope::IpcScope removed, use scope::ipc::Scope instead.scope::FsScope, scope::GlobPattern and scope::FsScopeEvent removed, use scope::fs::Scope, scope::fs::Pattern and scope::fs::Event respectively.updater module removed. Use tauri-plugin-updater instead. MigrationEnv.args field has been removed, use Env.args_os field instead.Menu, MenuEvent, CustomMenuItem, Submenu, WindowMenuEvent, MenuItem and Builder::on_menu_event APIs removed. MigrationSystemTray, SystemTrayHandle, SystemTrayMenu, SystemTrayMenuItemHandle, SystemTraySubmenu, MenuEntry and SystemTrayMenuItem APIs removed. MigrationThe @tauri-apps/api package no longer provides non-core modules. Only the previous tauri (now core), path, event and window modules are exported. All others have been moved to plugins.
@tauri-apps/api/tauri module renamed to @tauri-apps/api/core. Migration@tauri-apps/api/cli module removed. Use @tauri-apps/plugin-cli instead. Migration@tauri-apps/api/clipboard module removed. Use @tauri-apps/plugin-clipboard instead. Migration@tauri-apps/api/dialog module removed. Use @tauri-apps/plugin-dialog instead. Migration@tauri-apps/api/fs module removed. Use @tauri-apps/plugin-fs instead. Migration@tauri-apps/api/global-shortcut module removed. Use @tauri-apps/plugin-global-shortcut instead. Migration@tauri-apps/api/http module removed. Use @tauri-apps/plugin-http instead. Migration@tauri-apps/api/os module removed. Use @tauri-apps/plugin-os instead. Migration@tauri-apps/api/notification module removed. Use @tauri-apps/plugin-notification instead. Migration@tauri-apps/api/process module removed. Use @tauri-apps/plugin-process instead. Migration@tauri-apps/api/shell module removed. Use @tauri-apps/plugin-shell instead. Migration@tauri-apps/api/updater module removed. Use @tauri-apps/plugin-updater instead Migration@tauri-apps/api/window module renamed to @tauri-apps/api/webviewWindow. MigrationThe v1 plugins are now published as @tauri-apps/plugin-<plugin-name>. Previously they were available from git as tauri-plugin-<plugin-name>-api.
Most of the environment variables read and written by the Tauri CLI were renamed for consistency and prevention of mistakes:
TAURI_PRIVATE_KEY -> TAURI_SIGNING_PRIVATE_KEYTAURI_KEY_PASSWORD -> TAURI_SIGNING_PRIVATE_KEY_PASSWORDTAURI_SKIP_DEVSERVER_CHECK -> TAURI_CLI_NO_DEV_SERVER_WAITTAURI_DEV_SERVER_PORT -> TAURI_CLI_PORTTAURI_PATH_DEPTH -> TAURI_CLI_CONFIG_DEPTHTAURI_FIPS_COMPLIANT -> TAURI_BUNDLER_WIX_FIPS_COMPLIANTTAURI_DEV_WATCHER_IGNORE_FILE -> TAURI_CLI_WATCHER_IGNORE_FILENAMETAURI_TRAY -> TAURI_LINUX_AYATANA_APPINDICATORTAURI_APPLE_DEVELOPMENT_TEAM -> APPLE_DEVELOPMENT_TEAMTAURI_PLATFORM -> TAURI_ENV_PLATFORMTAURI_ARCH -> TAURI_ENV_ARCHTAURI_FAMILY -> TAURI_ENV_FAMILYTAURI_PLATFORM_VERSION -> TAURI_ENV_PLATFORM_VERSIONTAURI_PLATFORM_TYPE -> TAURI_ENV_PLATFORM_TYPETAURI_DEBUG -> TAURI_ENV_DEBUGThe event system was redesigned to be easier to use. Instead of relying on the source of the event, it now has a simpler implementation that relies on event targets.
emit function now emits the event to all event listeners.emit_to/emitTo function to trigger an event to a specific target.emit_filter now filters based on EventTarget instead of a window.listen_global to listen_any. It now listens to all events regardless of their filters and targets.event.listen() behaves similar to listen_any. It now listens to all events regardless of their filters and targets, unless a target is set in the Options.WebviewWindow.listen etc. only listen to events emitted to the respective EventTarget.Tauri v2 introduces multiwebview support currently behind an unstable feature flag.
In order to support it, we renamed the Rust Window type to WebviewWindow and the Manager get_window function to get_webview_window.
The WebviewWindow JS API type is now re-exported from @tauri-apps/api/webviewWindow instead of @tauri-apps/api/window.
On Windows the frontend files in production apps are now hosted on http://tauri.localhost instead of https://tauri.localhost. Because of this IndexedDB, LocalStorage and Cookies will be reset unless dangerousUseHttpScheme was used in v1. To prevent this you can set app > windows > useHttpsScheme to true or use WebviewWindowBuilder::use_https_scheme to keep using the https scheme.
Common scenarios you may encounter when migrating your Tauri 1.0 app to Tauri 2.0.
The @tauri-apps/api/tauri module was renamed to @tauri-apps/api/core.
Simply rename the module import:
- import { invoke } from "@tauri-apps/api/tauri"
+ import { invoke } from "@tauri-apps/api/core"
The Rust App::get_cli_matches JavaScript @tauri-apps/api/cli APIs have been removed. Use the @tauri-apps/plugin-cli plugin instead:
# Cargo.toml
[dependencies]
tauri-plugin-cli = "2"
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_cli::init())
}
// package.json
{
"dependencies": {
"@tauri-apps/plugin-cli": "^2.0.0"
}
}
import { getMatches } from '@tauri-apps/plugin-cli';
const matches = await getMatches();
fn main() {
use tauri_plugin_cli::CliExt;
tauri::Builder::default()
.plugin(tauri_plugin_cli::init())
.setup(|app| {
let cli_matches = app.cli().matches()?;
Ok(())
})
}
The Rust App::clipboard_manager and AppHandle::clipboard_manager and JavaScript @tauri-apps/api/clipboard APIs have been removed. Use the @tauri-apps/plugin-clipboard-manager plugin instead:
[dependencies]
tauri-plugin-clipboard-manager = "2"
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_clipboard_manager::init())
}
// package.json
{
"dependencies": {
"@tauri-apps/plugin-clipboard-manager": "^2.0.0"
}
}
import { writeText, readText } from '@tauri-apps/plugin-clipboard-manager';
await writeText('Tauri is awesome!');
assert(await readText(), 'Tauri is awesome!');
use tauri_plugin_clipboard::{ClipboardExt, ClipKind};
tauri::Builder::default()
.plugin(tauri_plugin_clipboard::init())
.setup(|app| {
app.clipboard().write(ClipKind::PlainText {
label: None,
text: "Tauri is awesome!".into(),
})?;
Ok(())
})
The Rust tauri::api::dialog JavaScript @tauri-apps/api/dialog APIs have been removed. Use the @tauri-apps/plugin-dialog plugin instead:
# Cargo.toml
[dependencies]
tauri-plugin-dialog = "2"
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
}
// package.json
{
"dependencies": {
"@tauri-apps/plugin-dialog": "^2.0.0"
}
}
import { save } from '@tauri-apps/plugin-dialog';
const filePath = await save({
filters: [
{
name: 'Image',
extensions: ['png', 'jpeg'],
},
],
});
use tauri_plugin_dialog::DialogExt;
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
.setup(|app| {
app.dialog().file().pick_file(|file_path| {
// do something with the optional file path here
// the file path is `None` if the user closed the dialog
});
app.dialog().message("Tauri is Awesome!").show();
Ok(())
})
The Rust App::get_cli_matches JavaScript @tauri-apps/api/fs APIs have been removed. Use the std::fs for Rust and @tauri-apps/plugin-fs plugin for JavaScript instead:
# Cargo.toml
[dependencies]
tauri-plugin-fs = "2"
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_fs::init())
}
// package.json
{
"dependencies": {
"@tauri-apps/plugin-fs": "^2.0.0"
}
}
import { mkdir, BaseDirectory } from '@tauri-apps/plugin-fs';
await mkdir('db', { baseDir: BaseDirectory.AppLocalData });
Some functions and types have been renamed or removed:
Dir enum alias removed, use BaseDirectory.FileEntry, FsBinaryFileOption, FsDirOptions, FsOptions, FsTextFileOption and BinaryFileContents interfaces and type aliases have been removed and replaced with new interfaces suited for each function.createDir renamed to mkdir.readBinaryFile renamed to readFile.removeDir removed and replaced with remove.removeFile removed and replaced with remove.renameFile removed and replaced with rename.writeBinaryFile renamed to writeFile.Use the Rust std::fs functions.
The Rust App::global_shortcut_manager and AppHandle::global_shortcut_manager and JavaScript @tauri-apps/api/global-shortcut APIs have been removed. Use the @tauri-apps/plugin-global-shortcut plugin instead:
# Cargo.toml
[dependencies]
[target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies]
tauri-plugin-global-shortcut = "2"
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_global_shortcut::Builder::default().build())
}
// package.json
{
"dependencies": {
"@tauri-apps/plugin-global-shortcut": "^2.0.0"
}
}
import { register } from '@tauri-apps/plugin-global-shortcut';
await register('CommandOrControl+Shift+C', () => {
console.log('Shortcut triggered');
});
use tauri_plugin_global_shortcut::GlobalShortcutExt;
tauri::Builder::default()
.plugin(
tauri_plugin_global_shortcut::Builder::new().with_handler(|app, shortcut| {
println!("Shortcut triggered: {:?}", shortcut);
})
.build(),
)
.setup(|app| {
// register a global shortcut
// on macOS, the Cmd key is used
// on Windows and Linux, the Ctrl key is used
app.global_shortcut().register("CmdOrCtrl+Y")?;
Ok(())
})
The Rust tauri::api::http JavaScript @tauri-apps/api/http APIs have been removed. Use the @tauri-apps/plugin-http plugin instead:
# Cargo.toml
[dependencies]
tauri-plugin-http = "2"
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_http::init())
}
// package.json
{
"dependencies": {
"@tauri-apps/plugin-http": "^2.0.0"
}
}
import { fetch } from '@tauri-apps/plugin-http';
const response = await fetch(
'https://raw.githubusercontent.com/tauri-apps/tauri/dev/package.json'
);
use tauri_plugin_http::reqwest;
tauri::Builder::default()
.plugin(tauri_plugin_http::init())
.setup(|app| {
let response_data = tauri::async_runtime::block_on(async {
let response = reqwest::get(
"https://raw.githubusercontent.com/tauri-apps/tauri/dev/package.json",
)
.await
.unwrap();
response.text().await
})?;
Ok(())
})
The HTTP plugin re-exports reqwest so you can check out their documentation for more information.
</TabItem> </Tabs>The Rust tauri::api::notification JavaScript @tauri-apps/api/notification APIs have been removed. Use the @tauri-apps/plugin-notification plugin instead:
# Cargo.toml
[dependencies]
tauri-plugin-notification = "2"
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_notification::init())
}
// package.json
{
"dependencies": {
"@tauri-apps/plugin-notification": "^2.0.0"
}
}
import { sendNotification } from '@tauri-apps/plugin-notification';
sendNotification('Tauri is awesome!');
use tauri_plugin_notification::NotificationExt;
use tauri::plugin::PermissionState;
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_notification::init())
.setup(|app| {
if app.notification().permission_state()? == PermissionState::Unknown {
app.notification().request_permission()?;
}
if app.notification().permission_state()? == PermissionState::Granted {
app.notification()
.builder()
.body("Tauri is awesome!")
.show()?;
}
Ok(())
})
}
The Rust Menu APIs were moved to the tauri::menu module and refactored to use the muda crate.
tauri::menu::MenuBuilderUse tauri::menu::MenuBuilder instead of tauri::Menu. Note that its constructor takes a Manager instance (one of App, AppHandle or WebviewWindow) as an argument:
use tauri::menu::MenuBuilder;
tauri::Builder::default()
.setup(|app| {
let menu = MenuBuilder::new(app)
.copy()
.paste()
.separator()
.undo()
.redo()
.text("open-url", "Open URL")
.check("toggle", "Toggle")
.icon("show-app", "Show App", app.default_window_icon().cloned().unwrap())
.build()?;
app.set_menu(menu);
Ok(())
})
tauri::menu::PredefinedMenuItemUse tauri::menu::PredefinedMenuItem instead of tauri::MenuItem:
use tauri::menu::{MenuBuilder, PredefinedMenuItem};
tauri::Builder::default()
.setup(|app| {
let menu = MenuBuilder::new(app).item(&PredefinedMenuItem::copy(app)?).build()?;
Ok(())
})
:::tip
The menu builder has dedicated methods to add each predefined menu item so you can call .copy() instead of .item(&PredefinedMenuItem::copy(app, None)?).
:::
tauri::menu::MenuItemBuilderUse tauri::menu::MenuItemBuilder instead of tauri::CustomMenuItem:
use tauri::menu::MenuItemBuilder;
tauri::Builder::default()
.setup(|app| {
let toggle = MenuItemBuilder::new("Toggle").accelerator("Ctrl+Shift+T").build(app)?;
Ok(())
})
tauri::menu::SubmenuBuilderUse tauri::menu::SubmenuBuilder instead of tauri::Submenu:
use tauri::menu::{MenuBuilder, SubmenuBuilder};
tauri::Builder::default()
.setup(|app| {
let submenu = SubmenuBuilder::new(app, "Sub")
.text("Tauri")
.separator()
.check("Is Awesome")
.build()?;
let menu = MenuBuilder::new(app).item(&submenu).build()?;
Ok(())
})
tauri::Builder::menu now takes a closure because the menu needs a Manager instance to be built. See the documentation for more information.
The Rust tauri::Builder::on_menu_event API was removed. Use tauri::App::on_menu_event or tauri::AppHandle::on_menu_event instead:
use tauri::menu::{CheckMenuItemBuilder, MenuBuilder, MenuItemBuilder};
tauri::Builder::default()
.setup(|app| {
let toggle = MenuItemBuilder::with_id("toggle", "Toggle").build(app)?;
let check = CheckMenuItemBuilder::new("Mark").build(app)?;
let menu = MenuBuilder::new(app).items(&[&toggle, &check]).build()?;
app.set_menu(menu)?;
app.on_menu_event(move |app, event| {
if event.id() == check.id() {
println!("`check` triggered, do something! is checked? {}", check.is_checked().unwrap());
} else if event.id() == "toggle" {
println!("toggle triggered!");
}
});
Ok(())
})
Note that there are two ways to check which menu item was selected: move the item to the event handler closure and compare IDs, or define a custom ID for the item through the with_id constructor and use that ID string to compare.
:::tip
Menu items can be shared across menus, and the menu event is bound to a menu item instead of a menu or window.
If you don't want all listeners to be triggered when a menu item is selected, do not share menu items and use dedicated instances instead, that you could move into tauri::WebviewWindow/WebviewWindowBuilder::on_menu_event closure.
:::
The Rust tauri::api::os JavaScript @tauri-apps/api/os APIs have been removed. Use the @tauri-apps/plugin-os plugin instead:
# Cargo.toml
[dependencies]
tauri-plugin-os = "2"
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_os::init())
}
// package.json
{
"dependencies": {
"@tauri-apps/plugin-os": "^2.0.0"
}
}
import { arch } from '@tauri-apps/plugin-os';
const architecture = await arch();
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_os::init())
.setup(|app| {
let os_arch = tauri_plugin_os::arch();
Ok(())
})
}
The Rust tauri::api::process JavaScript @tauri-apps/api/process APIs have been removed. Use the @tauri-apps/plugin-process plugin instead:
# Cargo.toml
[dependencies]
tauri-plugin-process = "2"
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_process::init())
}
// package.json
{
"dependencies": {
"@tauri-apps/plugin-process": "^2.0.0"
}
}
import { exit, relaunch } from '@tauri-apps/plugin-process';
await exit(0);
await relaunch();
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_process::init())
.setup(|app| {
// exit the app with a status code
app.handle().exit(1);
// restart the app
app.handle().restart();
Ok(())
})
}
The Rust tauri::api::shell JavaScript @tauri-apps/api/shell APIs have been removed. Use the @tauri-apps/plugin-shell plugin instead:
# Cargo.toml
[dependencies]
tauri-plugin-shell = "2"
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
}
// package.json
{
"dependencies": {
"@tauri-apps/plugin-shell": "^2.0.0"
}
}
import { Command, open } from '@tauri-apps/plugin-shell';
const output = await Command.create('echo', 'message').execute();
await open('https://github.com/tauri-apps/tauri');
use tauri_plugin_shell::ShellExt;
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.setup(|app| {
app.shell().open("https://github.com/tauri-apps/tauri", None)?;
Ok(())
})
}
use tauri_plugin_shell::ShellExt;
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.setup(|app| {
let status = tauri::async_runtime::block_on(async move { app.shell().command("which").args(["ls"]).status().await.unwrap() });
println!("`which` finished with status: {:?}", status.code());
Ok(())
})
}
use tauri_plugin_shell::ShellExt;
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.setup(|app| {
let output = tauri::async_runtime::block_on(async move { app.shell().command("echo").args(["TAURI"]).output().await.unwrap() });
assert!(output.status.success());
assert_eq!(String::from_utf8(output.stdout).unwrap(), "TAURI");
Ok(())
})
}
use tauri_plugin_shell::{ShellExt, process::CommandEvent};
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.setup(|app| {
let handle = app.handle().clone();
tauri::async_runtime::spawn(async move {
let (mut rx, mut child) = handle.shell().command("cargo")
.args(["tauri", "dev"])
.spawn()
.expect("Failed to spawn cargo");
let mut i = 0;
while let Some(event) = rx.recv().await {
if let CommandEvent::Stdout(line) = event {
println!("got: {}", String::from_utf8(line).unwrap());
i += 1;
if i == 4 {
child.write("message from Rust\n".as_bytes()).unwrap();
i = 0;
}
}
}
});
Ok(())
})
}
The Rust SystemTray APIs were renamed to TrayIcon for consistency. The new APIs can be found in the Rust tray module.
tauri::tray::TrayIconBuilderUse tauri::tray::TrayIconBuilder instead of tauri::SystemTray:
let tray = tauri::tray::TrayIconBuilder::with_id("my-tray").build(app)?;
See TrayIconBuilder for more information.
Use tauri::menu::Menu instead of tauri::SystemTrayMenu, tauri::menu::Submenu instead of tauri::SystemTraySubmenu and tauri::menu::PredefinedMenuItem instead of tauri::SystemTrayMenuItem.
tauri::SystemTray::on_event have been split into tauri::tray::TrayIconBuilder::on_menu_event and tauri::tray::TrayIconBuilder::on_tray_icon_event:
use tauri::{
menu::{MenuBuilder, MenuItemBuilder},
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
};
tauri::Builder::default()
.setup(|app| {
let toggle = MenuItemBuilder::with_id("toggle", "Toggle").build(app)?;
let menu = MenuBuilder::new(app).items(&[&toggle]).build()?;
let tray = TrayIconBuilder::new()
.menu(&menu)
.on_menu_event(move |app, event| match event.id().as_ref() {
"toggle" => {
println!("toggle clicked");
}
_ => (),
})
.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(webview_window) = app.get_webview_window("main") {
let _ = webview_window.unminimize();
let _ = webview_window.show();
let _ = webview_window.set_focus();
}
}
})
.build(app)?;
Ok(())
})
:::caution[Change of default behavior]
The built-in dialog with an automatic update check was removed, use the Rust and JS APIs to check for and install updates instead. Failing to do so will prevent your users from getting further updates!
:::
The Rust tauri::updater and JavaScript @tauri-apps/api-updater APIs have been removed. To set a custom updater target with the @tauri-apps/plugin-updater:
[dependencies]
tauri-plugin-updater = "2"
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_updater::Builder::new().build())
}
// package.json
{
"dependencies": {
"@tauri-apps/plugin-updater": "^2.0.0"
}
}
import { check } from '@tauri-apps/plugin-updater';
import { relaunch } from '@tauri-apps/plugin-process';
const update = await check();
if (update?.available) {
console.log(`Update to ${update.version} available! Date: ${update.date}`);
console.log(`Release notes: ${update.body}`);
await update.downloadAndInstall();
// requires the `process` plugin
await relaunch();
}
To check for updates:
use tauri_plugin_updater::UpdaterExt;
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_updater::Builder::new().build())
.setup(|app| {
let handle = app.handle();
tauri::async_runtime::spawn(async move {
let response = handle.updater().check().await;
});
Ok(())
})
}
To set a custom updater target:
fn main() {
let mut updater = tauri_plugin_updater::Builder::new();
#[cfg(target_os = "macos")]
{
updater = updater.target("darwin-universal");
}
tauri::Builder::default()
.plugin(updater.build())
}
The Rust tauri::api::path module functions and tauri::PathResolver have been moved to tauri::Manager::path:
use tauri::{path::BaseDirectory, Manager};
tauri::Builder::default()
.setup(|app| {
let home_dir_path = app.path().home_dir().expect("failed to get home dir");
let path = app.path().resolve("path/to/something", BaseDirectory::Config)?;
Ok(())
})
On the Rust side, Window was renamed to WebviewWindow, its builder WindowBuilder is now named WebviewWindowBuilder and WindowUrl is now named WebviewUrl.
Additionally, the Manager::get_window function was renamed to get_webview_window and
the window's parent_window API was renamed to parent_raw to support a high level window parent API.
On the JavaScript side, the WebviewWindow class is now exported in the @tauri-apps/api/webviewWindow path.
The onMenuClicked function was removed, you can intercept menu events when creating a menu in JavaScript instead.
On the JavaScript side, make sure you Migrate to File System Plugin. Additionally, note the changes made to the v1 allowlist in Migrate Permissions.
On the Rust side, make sure you Migrate Path to Tauri Manager.
In Tauri v1, the external binaries and their arguments were defined in the allowlist. In v2, use the new permissions system. Read Migrate Permissions for more information.
On the JavaScript side, make sure you Migrate to Shell Plugin.
On the Rust side, tauri::api::process API has been removed. Use tauri_plugin_shell::ShellExt and tauri_plugin_shell::process::CommandEvent APIs instead. Read the Embedding External Binaries guide to see how.
The "process-command-api" features flag has been removed in v2. So running the external binaries does not require this feature to be defined in the Tauri config anymore.
The v1 allowlist have been rewritten to a completely new system for permissions that works for individual plugins and is much more configurable for multiwindow and remote URL support. This new system works like an access control list (ACL) where you can allow or deny commands, allocate permissions to a specific set of windows and domains, and define access scopes.
To enable permissions for your app, you must create capability files inside the src-tauri/capabilities folder, and Tauri will automatically configure everything else for you.
The migrate CLI command automatically parses your v1 allowlist and generates the associated capability file.
To learn more about permissions and capabilities, see the security documentation.