Rust, SolidJS, and Tauri: Create a cross-platform desktop app
Tags: rust, solidjs
Published: 2022-07-19 15:14:44

**Written by [Alex Merced](https://blog.logrocket.com/author/alexmerced/)✏️**

On December 15, 2022 GitHub will [calling it a day for the ATOM IDE](https://github.blog/2022-06-08-sunsetting-atom/), which holds a special place in software history as the first real Electron application. 

Electron allowed developers to use standard web technologies that they were already familiar with to build cross-platform desktop applications for Windows, Mac, and Linux. This is all great for applications like Atom, VSCode, and Postman, but it is not always ideal. For example, one common criticism of Electron apps is that they often use up a lot of memory relative to what a lower-level language, like C, C++, Rust, or Go would use. 

In this article, we’ll explore [Tauri](https://tauri.app), a new framework for building binaries for most major desktop platforms. Let’s get started! To follow along with our simple example, you can [access the full code](https://github.com/AlexMercedCoder/my-first-tauri-application) here:

*   [What is Tauri?](#what-tauri)
*   [Summary of Tauri’s architecture](#summary-tauri-architecture)
*   [Taking Tauri for a test drive](#taking-tauri-test-drive)
*   [The folder structure](#folder-structure)
*   [Creating a basic command](#creating-basic-command)

## What is Tauri? <a name="what-tauri">

[Tauri](https://tauri.studio/) is a new framework that offers what people like most about Electron but fixes many of the security and performance concerns. Tauri offers the ability to design your UI using web technologies like HTML, CSS, and JavaScript but allows you to use lower-level languages to write the application and backend logic. 

At the time of writing, Tauri primarily supports Rust as the backend language, but its API can be implemented across multiple languages. Over time, we can expect C, C++, Go, Ruby, Python, and even JavaScript backend bindings as users decide to implement them.

## Summary of Tauri's architecture <a name="summary-tauri-architecture">

Like Electron, Tauri uses a multi-process approach. [The Core process](https://tauri.app/v1/guides/architecture/process-model/#the-core-process) is essentially the backend of the application with all the data models and application logic written in Rust. [The WebView process](https://tauri.app/v1/guides/architecture/process-model/#the-webview-process) is the application UI that is built in the JavaScript framework of your choice. You can even use non-JavaScript options like Rust using Dominator or ClojureScript. 

The two processes communicate either through events or commands, which are RPC calls. Either process can emit events that the other process can listen for to trigger logic in one process when the other process does a particular action. 

The WebView process can issue commands to trigger pre-defined functions in the core process. These are essential [RPC Calls](https://main.grokoverflow.com/posts/2022/02-understanding-rpc-node-walkthrough).

## Taking Tauri for a test drive <a name="taking-tauri-test-drive">

To get everything installed, I recommend following [this guide](https://tauri.studio/v1/guides/getting-started/prerequisites) from the official docs. You’ll need Node.js and Rust installed. Once everything is set up, you can create an app by running the following command:

```bash
npx create-tauri-app
```

You'll be given several options of what framework or language to use for the WebView. For this tutorial, select **SolidJS** and then the **[`ts-sass`](https://github.com/solidjs/templates/tree/master/ts-sass)** Solid template. 

`cd` into the new folder and run `npm install`. Then, run `npm run tauri dev`, which turns on the Core and WebView processes so you can preview it at `localhost:3000`. The page essentially just displays a number that is counting each second. 

Pay attention to the terminal output because there may be libraries and environmental variables you need to install to get the app to build successfully. I’m working from a POP OS, and I had to install several libraries before it had all the dependencies.

## The folder structure <a name="folder-structure">

Our app, which I’ve called `/Your-Tauri-App`, will use the following folder structure:

```javascript
/Your-Tauri-App
|
|-- /node_modules //All the NPM Libraries
|
|-- /src // Your frontend UI Code for the WebView Process
|
|-- /src-tauri // Your backend code for the Core process in Rust
|
|-- .gitignore // files to be ignored by git
|-- index.html // just the HTML file the WebView mounts to
|-- package-lock.json // lockfile for npm
|-- package.json // config file for npm
|-- pnpm-lock.yaml // lock file for pnpm
|-- readme.md // template readme
|-- tsconfig.json // Typescript Configurations
|-- vite.config.js // Vite configurations
```

## Creating a basic command <a name="creating-basic-command">

You can think of commands like writing your routes in the backend of a standard web application. However, instead of using `fetch` to make a request to a route, we’ll use `invoke` to call one of these commands from our frontend. 

Let's create a very basic command in `/src-tauri/src/main.rs`:

```javascript
#![cfg_attr(
  all(not(debug_assertions), target_os = "windows"),
  windows_subsystem = "windows"
)]

// Our Tauri Command
#[tauri::command]
fn return_string(word: String) -> String{
    return word
}

fn main() {
  tauri::Builder::default()
    .invoke_handler(tauri::generate_handler![return_string])
    .run(tauri::generate_context!())
    .expect("error while running tauri application");
}
```

The `#[tauri::command]` macro turns our function into a command, and we register our command with the app with the `invoke_handler` method in the Tauri builder. 

Then, using the `invoke` method, we call this command from Solid. Let's update our `App.js` file:

```javascript
import { invoke } from '@tauri-apps/api/tauri';
import { Component, createSignal } from 'solid-js';
import './App.scss';
import Counter from './Counter';

const App: Component = () => {
  const [counter, setCounter] = createSignal(0);
  setInterval(setCounter, 1000, (c: number) => c + 1);

  async function handleClick(event){

    // Invoke the command from our backend using the invoke function
    const result = await invoke("return_string", {
      word: "This is the argument"
    })

    // print the result to an alert
    alert(result)
  }

  return (
    <>
      <div>
        <h1 class="header">{counter()}</h1>
        <button onClick={handleClick}>Click Me</button>
      </div>
      <Counter />
    </>
  );
};

export default App;
```

In the code above, we import the invoke method `import { invoke } from '@tauri-apps/api/tauri';`. We call `invoke` and pass it two arguments, a string with the name of the command to invoke and an object with any argument by name. 

Now, when we click on the new **`Click Me`** button, the text we sent will be sent back, confirming that we successfully invoked the method from our backend. That's it! You can create commands using Rust as well as any Rust libraries to handle data management needs for your application.

## Building the application

At the time of writing, Tauri doesn't support cross-compilation, so it'll compile to whatever operating system you are currently on, in my case, Linux. 

To enable this, all you have to do is run `npm run tauri build`. Just make sure to update the identifier property in the `tauri.config.json` to whatever you want. 

For me, after the build completed, there were `AppImage` and `Deb` files available in `src-tauri/target/bundle`.

## Conclusion

Tauri opens up desktop apps using web technologies and your favorite frontend framework to better performance and smaller bundles. Tauri will certainly be tempting many Electron developers into making the switch for apps that need improved performance. 

Although we just scratched the surface of what Tauri is capable of, I recommend reading up on the following cool features:

*   [Triggering code with events](https://tauri.studio/v1/guides/features/events)
*   [Creating a Window Menu](https://tauri.studio/v1/guides/features/menu)
*   [Customizing the Window](https://tauri.studio/v1/guides/features/window-customization)

Happy coding!

---

## LogRocket: Full visibility into production Rust apps

Debugging Rust applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking performance of your Rust apps, automatically surfacing errors, and tracking slow network requests and load time, [try LogRocket](https://logrocket.com/signup). 

[![LogRocket Dashboard Free Trial Banner](https://blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png)](https://logrocket.com/signup)

[LogRocket](https://logrocket.com/signup) is like a DVR for web apps, recording literally everything that happens on your Rust app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.

Modernize how you debug your Rust apps — [start monitoring for free](https://logrocket.com/signup).