Tauri: Small Desktop and Mobile Apps with Rust
GitHub Repo
Apache-2.0 OR MIT
November 12, 2026 at 01:35 PM
0 views

Tauri: Small Desktop and Mobile Apps with Rust

@tauri-appsProject Author

What Tauri is

Tauri is a framework for building desktop and mobile applications whose user interface is written with web technologies and whose backend is a compiled Rust binary. The README describes it as a framework for building "tiny, blazingly fast binaries for all major operating systems", where developers "can integrate any front-end framework that compiles to HTML, JS and CSS" for the UI, and the backend is "a rust-sourced binary with an API that the front-end can interact with". The current documentation widens the language list: backend logic can be written in Rust, and Swift and Kotlin are available for plugin code on Apple and Android platforms.

The defining design choice is that Tauri does not ship a browser engine. A Tauri app renders its interface in the webview the operating system already provides: Microsoft Edge WebView2 on Windows, WKWebView on macOS and iOS, WebKitGTK on Linux and the Android System WebView on Android. The docs say this lets a Tauri app contain "only the code and assets specific for that app", and they claim that "a minimal Tauri app can be less than 600KB in size". That number is the project's own claim for a minimal app, not a benchmark of real-world applications, and the same trade-off brings platform differences that are covered below.

The project frames its pitch around three points on the "What is Tauri?" page: a secure foundation built on Rust's memory, thread and type safety; smaller bundles from using the system webview; and flexibility to use virtually any frontend framework with bindings for multiple languages. The docs add that Tauri undergoes a security audit for major and minor releases, and link a Tauri 2.0 audit report by Radically Open Security.

How it works

Core process and WebView processes

The process model page says Tauri "employs a multi-process architecture similar to Electron or many modern web browsers". Each application has a Core process, which is the entry point and "the only component with full access to the operating system". The Core creates and orchestrates windows, system tray menus and notifications, and all inter-process communication is routed through it. The docs recommend keeping global state such as settings or database connections there.

The Core does not render the UI itself. It spins up WebView processes that use the operating system's webview libraries to execute HTML, CSS and JavaScript. Those libraries are dynamically linked at runtime rather than included in the executable, which the docs say makes the app "significantly smaller" but also means "you need to keep platform differences in mind, just like traditional web development".

IPC: commands and events

Communication between the two sides uses asynchronous message passing. The docs argue this is safer than shared memory or direct function access because the recipient can reject or discard any request; if the Core decides a request is malicious, it never executes the corresponding function. There are two primitives:

  • Commands are an FFI-like abstraction. The frontend calls invoke, which the docs compare to the browser's fetch, to call a Rust function, pass arguments and receive a result. A JSON-RPC-like protocol is used underneath, so all arguments and return values must be serializable to JSON. Commands can be async, can return errors, and can access managed state, the calling window or an app handle.
  • Events are fire-and-forget, one-way messages suited to lifecycle notifications and state changes. Both the frontend and the Core can emit them. The docs note that events are not type safe, are always async, cannot return values and only carry JSON payloads, and that under the hood they still use commands, gated by event permissions.

The crates behind it

The architecture document splits the repository into crates. tauri ties runtimes, macros, utilities and the API together and reads tauri.conf.json at compile time; tauri-runtime is the glue layer to webview libraries, implemented for WRY by tauri-runtime-wry; tauri-codegen and tauri-macros embed assets and generate command handlers; and the bundler crate produces platform packages. The frontend imports @tauri-apps/api to call the backend, while @tauri-apps/cli wraps the Rust CLI for npm.

Two upstream libraries maintained by the Tauri organisation do the platform work. TAO creates and manages windows; the docs describe it as a fork of winit extended with features such as menu bars and system tray. WRY is the webview rendering library that decides which system webview to use and how to talk to it. Both can be consumed directly if an application needs deeper system integration than Tauri exposes.

Key features

  • Any web frontend. The frontend guide says Tauri "acts as a static web host": you hand it a folder of HTML, CSS, JavaScript and possibly WASM. Vite is recommended for SPA frameworks such as React, Vue, Svelte and Solid, and there are guides for Next.js, Nuxt, Qwik, SvelteKit, Leptos and Trunk.
  • Scaffolding templates. create-tauri-app ships templates for vanilla HTML/CSS/JS, Vue, Svelte, React, SolidJS, Angular, Preact, Yew, Leptos and Sycamore, plus a .NET Blazor option.
  • Built-in bundler. The README lists .app, .dmg, .deb, .rpm, .AppImage, NSIS .exe installers and WiX .msi installers. The distribution docs add Snap, Flatpak, AUR, the Microsoft Store, the App Store and Google Play as targets.
  • Self-updater. The updater plugin works against a static JSON file or a dynamic update server. Update signatures are mandatory; the docs say this "cannot be disabled". The README marks the updater as desktop only.
  • Native integrations. System tray icons, native notifications and menus are part of the feature list, with more capabilities delivered as official plugins such as file system, SQL, shell, global shortcuts, NFC, biometrics and barcode scanning.
  • Capabilities and permissions. A declarative access-control layer decides which commands each window or webview may call, with optional scopes such as allowed file paths. This is covered in detail in the security section.
  • Sidecars. External binaries written in any language, such as a Python API server packaged with pyinstaller, can be bundled through bundle.externalBin and launched through the shell plugin.

Getting started

Prerequisites

Tauri needs system dependencies, Rust and, if you use a JavaScript frontend, Node.js. On Debian and Ubuntu the prerequisites page lists these packages:

sudo apt update
sudo apt install libwebkit2gtk-4.1-dev \
  build-essential \
  curl \
  wget \
  file \
  libxdo-dev \
  libssl-dev \
  libayatana-appindicator3-dev \
  librsvg2-dev

Equivalent lists are given for Arch, Fedora, Gentoo, OSTree, openSUSE and Alpine, with NixOS pointed at the NixOS wiki. On macOS you need Xcode, or only the Xcode Command Line Tools if you are not targeting iOS. On Windows you need the Microsoft C++ Build Tools with the "Desktop development with C++" option, plus WebView2, which the docs say is already present on Windows 10 version 1803 and later. Rust is installed via rustup on Linux and macOS:

curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

On Windows the docs ask you to make sure the MSVC toolchain is the default host triple. The workspace manifest on the dev branch sets rust-version = "1.90" and the 2024 edition.

Create and run a project

The quickest path, from the README, is the scaffolding tool:

npm create tauri-app@latest

It prompts for a project name, a bundle identifier, the frontend language, package manager and UI template. Then start the development server:

cd tauri-app
npm install
npm run tauri dev

The develop guide warns that the first run may take several minutes while Cargo downloads and builds dependencies; later builds are cached.

A first command

Rust functions become callable from the frontend with the #[tauri::command] attribute and registration on the builder. From the calling-Rust guide:

#[tauri::command]
fn my_custom_command() {
	println!("I was invoked from JavaScript!");
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
	tauri::Builder::default()
		.invoke_handler(tauri::generate_handler![my_custom_command])
		.run(tauri::generate_context!())
		.expect("error while running tauri application");
}
import { invoke } from '@tauri-apps/api/core';
invoke('my_custom_command');

Arguments are passed as a JSON object with camelCase keys, return values must implement serde::Serialize, and errors must be serializable too. Two gotchas from the guide: command names must be unique across modules, and commands defined in lib.rs cannot be marked pub because of a limitation in the generated glue code. Commands without async run on the main thread unless marked otherwise, so the docs prefer async commands for heavy work.

Building and mobile

Release builds and bundles come from one command:

npm run tauri build

For Android, install Android Studio, set JAVA_HOME, ANDROID_HOME and NDK_HOME, and add the Rust targets:

rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android

iOS requires full Xcode on macOS, the iOS Rust targets and CocoaPods. Mobile development then runs through tauri android dev or tauri ios dev. On a physical iOS device the dev server must listen on the address in TAURI_DEV_HOST; projects made with create-tauri-app are configured for this out of the box.

The security model

The security overview draws a trust boundary between Rust code in the application core and frontend code running in the webview. Code in the core and in plugins "has full access to all available system resources and is not constrained". Code in the webview can reach system resources only through the IPC layer, and access is controlled by three concepts:

  • Permissions describe explicit privileges for commands. They can allow or deny commands, define scopes such as permitted paths, or both, and can be grouped into permission sets. Plugins ship pre-defined permissions; identifiers follow <name>:default and <name>:<command-name>. Application permissions can only be written in TOML.
  • Capabilities grant or deny permissions to specific windows or webviews by label. They live as JSON or TOML files in src-tauri/capabilities, can be restricted to particular platforms, and are all enabled by default unless tauri.conf.json lists specific ones.
  • Scopes constrain what an allowed command may touch, enforced by the command implementation itself.

A capability that lets the main window use core defaults plus one extra API looks like this, taken from the capabilities guide:

{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "main-capability",
  "description": "Capability for the main window",
  "windows": ["main"],
  "permissions": [
    "core:path:default",
    "core:event:default",
    "core:window:default",
    "core:app:default",
    "core:resources:default",
    "core:menu:default",
    "core:tray:default",
    "core:window:allow-set-title"
  ]
}

The docs are explicit about the limits. Capabilities help minimise the impact of a frontend compromise and reduce privilege escalation from frontend to system. They do not protect against malicious or insecure Rust code, scopes that are too lax, incorrect scope checks in a command, 0-days or unpatched 1-days in the system webview, or supply-chain attacks. Windows that belong to more than one capability effectively merge the permissions of all of them, and boundaries depend on window labels, not titles. By default, every command registered with invoke_handler is callable from every window until you restrict it through the app manifest.

The CSP is only enabled if you set it in the configuration, and the docs advise avoiding remote scripts served from a CDN. Remote URLs can be granted API access through a capability's remote field, but the docs caution that on Linux and Android Tauri cannot distinguish requests from an embedded <iframe> and the window itself.

Use cases

  • Turning an existing SPA into an installable app. A React, Vue or Svelte app built with Vite can be wrapped with tauri init and shipped as installers for all three desktop systems, provided it is built as SSG, SPA or MPA output rather than relying on server-side rendering.
  • Desktop front ends for local tools. The sidecar mechanism is aimed at bundling things like a Python CLI or API server so users do not need to install Node.js or Python themselves, with a web UI on top.
  • Rust-first applications. Teams already writing Rust can keep business logic, state and data access in the Core process and use Yew, Leptos or Sycamore for the UI.
  • One codebase for desktop and mobile. Tauri 2 targets Android and iOS from the same project, with platform-specific capabilities for mobile-only plugins such as NFC, biometrics or barcode scanning.

How it compares

The documentation names Electron only once, when it says Tauri's multi-process architecture is "similar to Electron or many modern web browsers". The comparison the docs actually make is structural rather than a feature matrix: Tauri does not bundle a browser engine and instead links the system webview at runtime, and its backend is a compiled Rust binary rather than a bundled runtime. The architecture page states that Tauri apps "do not ship a runtime since the final binary is compiled from Rust". The trade-off is smaller downloads and OS-delivered webview patches against testing on several rendering engines.

Size claims deserve care when comparing. The project's own figure is that a minimal app can be under 600KB, while the AppImage guide notes that an AppImage, which bundles its dependencies, grows "from the 2-6 MB range to 70+ MB", and on Windows the fixed WebView2 runtime option adds around 180MB to the installer. The size advantage therefore depends heavily on the packaging format you choose.

Things to know before adopting

  • Different webviews on each platform. Your UI runs on Chromium-based webviews on Windows (WebView2) and Android, and on WebKit through WKWebView on macOS and iOS and WebKitGTK on Linux. The webview versions page notes that WebView2 updates itself, macOS WebKit only updates with the OS so unsupported macOS versions "do not receive WebKit updates", Android depends on the device's selected WebView provider, and Linux versions vary by distribution. Feature support has to be checked per engine.
  • Linux build baseline and dependencies. Tauri 2 requires webkit2gtk 4.1, for example Ubuntu 22.04, and the README notes that Tauri 1 used webkit2gtk 4.0. Because glibc breaks compatibility with older systems, the Debian and AppImage guides say you must build on the oldest base system you intend to support that still provides WebKitGTK 4.1; Ubuntu 22.04 and Debian 12 are given as suitable baselines.
  • No server-side rendering. The frontend checklist states that Tauri "does not natively support server based alternatives (such as SSR)".
  • Code signing and notarization. The distribution overview says signing is required on most platforms, and macOS distribution outside the App Store also requires notarization. Signing cross-compiled Windows installers needs an external signing tool.
  • Updater keys are unforgiving. Update signing cannot be turned off, and the updater docs warn that if you lose the private key you will not be able to publish new updates to existing installs. The key must be supplied through environment variables; .env files do not work.
  • Windows installer details. .msi installers can only be built on Windows because WiX only runs there, and cross-compiling NSIS installers from Linux or macOS is described as "not tested as much". Tauri officially supports only the MSVC Windows target.
  • Mobile maturity. The README's platform table lists Android 7 and above, then adds "currently 8 and above", and iOS 9 and above. iOS development is only possible on macOS. Some features are desktop only, including the updater, and the architecture page still says the bundler supports macOS, Windows and Linux "but in the near future will support mobile platforms as well".
  • The permission system is extra work and not a sandbox for Rust. Capabilities protect against frontend compromise only. Anything written in the Rust core, including plugins, has full access to system resources, and broad scopes or missed checks in your own commands are on you.
  • Dev server exposure. The lifecycle threats page says the built-in development server is neither encrypted nor authenticated, exposes the frontend to the local network, and "should not be used on untrusted networks".
  • Licensing. The code is dual licensed: the workspace Cargo.toml and the npm packages declare Apache-2.0 OR MIT, the repository ships LICENSE-MIT and LICENSE-APACHE-2.0, and the README says "MIT or MIT/Apache 2.0 where applicable". The GitHub API reports only Apache-2.0. The Tauri logo is separately licensed CC-BY-NC-ND.

Project activity

As of November 2026 the repository has about 112,800 stars and 4,200 forks. It was created on 13 July 2019, is written primarily in Rust, and its most recent push was on 12 November 2026. Development happens on the dev branch, where the tauri crate manifest is at version 2.12.5 and the CLI and JavaScript API packages at 2.12.4. The code is available under Apache-2.0 OR MIT.

The project is organised as a Programme within the Commons Conservancy and is funded through Open Collective, with CrabNebula listed as a partner in the README.

Enjoying this project?

Discover more amazing open-source projects on TechLogHub. We curate the best developer tools and projects.

Project
tauri
Created
November 12
Last Updated
November 12, 2026 at 01:35 PM

Maintain Tauri: Small Desktop and Mobile Apps with Rust? Add the badge to your README

Show that the project is featured on TechLogHub and send readers to this write-up.

Featured on TechLogHub
[![Featured on TechLogHub](https://techloghub.com/badge-neutral.svg)](https://techloghub.com/open-source/tauri?utm_source=techloghub_badge&utm_medium=referral&utm_campaign=readme)

Find more projects like this

One email a week: new and trending developer tools, fresh comparisons, and what shipped. Unsubscribe in one click.