Tauri is a cross-platform framework that turns JavaScript or TypeScript code into a native application for virtually any operating system. While it is similar to Electron or React Native, Tauri is able to handle a wide range of front-end technologies. It combines with the front end to derive a native application for the required operating system.
Architecturally, Tauri runs the front-end application code alongside a Tauri process that generates the native application in parallel. As a result, you can develop the native application in tandem with the web application, and you can incorporate Rust calls in the native application to access operating system capability. This is a hybrid process and gives Tauri much of its power; the development process remains exactly the same no matter which stack you use. Tauri just decorates the existing process.
Tauri with SvelteKit
The process for developing a Tauri-enabled web application is to create or introduce the Rust plumbing alongside the application code, then run Tauri in web application development mode. Next, you run the Tauri connector (the IPC, or inter-process communication), launch the native application, and watch as your changes to the web application are reflected there. When you are ready to deliver the application for production, you can build a release bundle for specific platform targets.
We'll look at the process for running Tauri against a SvelteKit application. Each stack has its own requirements but the overall steps are consistent.
First, we need to have Rust installed. Tauri uses Rust for its performance, cross-platform, and security characteristics. rustc --version
should return a response on your command line. To set up Rust, you can follow the setup steps in the Tauri documents. Remember to run rustup update
and verify the install with rustc --version
.
Once Rust is available, you can scaffold a new Tauri layout with npm create tauri-app
, which requires that you have npm
installed. The Tauri quickstart describes other ways to install the Tauri infrastructure for your application, such as using a shell script or Rust cargo. We'll need npm
anyway for SvelteKit, so we’ll use that.
For our present purpose, once we have run npm create tauri-app
, we can follow the prompts, giving a name to the project (mine is iw-tauri
). Next, you’ll specify a package manager (Yarn for this example), then the framework you are using (SvelteKit). Once that is done, you can cd
into the new iw-tauri
directory and type yarn install
to install the packages, then yarn run dev
to start the development server.
Now, if you visit localhost:1420 you will see the application with a screen like the one shown in Figure 1.
Currently, the screen only shows the SvelteKit application. To see Tauri's magic at work, type npm run tauri dev
. That will launch both the SvelteKit application and the Tauri front end as realized by its Tauri Rust twin. Tauri will spawn a native window that reflects the same UI as the web interface.
Note that the first time you do this, Tauri will install its dependencies, which will take a moment. Thereafter, the startup will be quick.
As you write code and make changes, they will be reflected automatically by the native UI, provided you are using a development setup (like SvelteKit) that automatically updates the dev build.
How to create a Tauri application
Tauri works by running a Rust-native application that watches the web application and reflects code changes there. In the previous section, we used npm create tauri-app
, which is a shortcut for the manual process of creating a Tauri application. The process consists of these steps:
- Create a front-end project using a framework such as SvelteKit.
- Configure the front-end project to work with Tauri.
- Run the Tauri application.
- Configure the Tauri application to watch the front-end application.
In a manual process, we would also need to set framework-specific parameters in the front-end configuration step. In the case of SvelteKit, for example, we'd need to make sure the static adapter was used.
You can find the Tauri-specific code in the /src-tauri
directory, which contains the tauri.conf.json
file for configuring Tauri itself. You can look at the file that create tauri-app
has generated for us to get a sense of how it works and find more details here. Listing 1 contains a small portion of the Tauri configuration code.
Listing 1. A snippet of the Tauri configuration
{
"build": {
"beforeDevCommand": "yarn dev",
"beforeBuildCommand": "yarn build",
"devPath": "http://localhost:1420",
"distDir": "../build",
"withGlobalTauri": false
},
In Listing 1, you can see the config is telling Tauri what to do before a dev run, which is basically: use yarn dev
to start the web application in development mode, then tell it where the application is running (localhost on port 1420).
For a video guide to web-powered native application development with Tauri, see Tauri, a Rust-powered Electron alternative. Also see the Tauri guides for more information about manually setting up a Tauri project.
Native calls from JavaScript
Tauri lets you bind JavaScript calls to native functionality in Rust. The basic idea is, you expose a Rust function as a Tauri command (in the src-tauri/src/main.rs
file), then Tauri makes that available as a JavaScript function that you can execute in your web application. Here's a quick look from the Tauri documentation:
Listing 2. Using a native command from JavaScript
// In src-tauri/src/main.rs
#[tauri::command]
fn my_custom_command() {
println!("I was invoked from JS!");
}
// register the command:
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![my_custom_command])
.run(tauri::generate_context!())
.expect("failed to run app");
}
// In your JavaScript
import { invoke } from '@tauri-apps/api/tauri'
const invoke = window.__TAURI__.invoke
invoke('my_custom_command');
Listing 2 shows the basic outlines of using a native command from JavaScript. There is a lot more to the capability, but in general, it's quite a nice means of tying a web application into the operating system.
Tauri under the hood: Rust and WebViews
You should have a general idea now of how Tauri is laid out alongside the web application. What's genius about Tauri is the communication between the two components, which in Tauri is known as inter-process communication. Essentially, the two applications are isolated sandboxes exchanging messages. There are two types of messages: events and commands. Events are bidirectional, with a specified format that the Tauri JavaScript or TypeScript client uses to interact with the Tauri server.
The command protocol is layered on top of the event protocol. The command protocol is always initiated by the client-side application and works as a request-response interaction. It’s something like a remotely negotiated foreign-function interface, via JSON-RPC (so the data must always be JSON serializable). It isn’t a true foreign function interface in the sense that all calls into the operating system are mediated by the remote procedure call, so many of the security risks are mitigated.
Tauri uses both Rust and WebViews to generate the native user interface. WebViews provide Tauri with a standardized, well-understood basis from which to build the rest of its functionality. WebViews are native to the operating system, which makes for better performance than a secondary layer like Chromium.
Tauri uses a multi-process model for responsive UI handling. Essentially, Tauri spawns processes as needed to handle incoming events and commands from the client application. The core Tauri process creates sub-processes with the specific permissions required. Each process has a lifetime dedicated to the job at hand only.
Here's more about how WebViews work. You can also check out the Tauri project on GitHub for a look at Tauri’s custom Rust-based WebView engine. For a sense of how much goes on behind the scenes with Tauri, take a deep dive into Tauri’s architecture.
Conclusion
Tauri is a framework similar to Electron and React Native. Each has its pros and cons, but Tauri's quick uptake by developers indicates that it is doing a few things right. The decision to use native WebViews and Rust pays dividends in performance. Tauri is certainly a strong contender for new projects, as well as for existing web applications that need to be able to generate a native application.