I have finally found a way to make a desktop app using ReactJS and Rust that’s relatively painless and without too much extra tooling. The key ingredient is called Tauri.
I wrote this post because I couldn’t find any straightforward explanation for initializing a React and Tauri app. Also I wanted to write this down for me to remember how to do this. Hopefully this helps you too.
If you find anything wrong, needs updating, or a better way of doing things; post a comment.
The following instructions have worked for me running on Mac OS 11.4.0 X64 with Node.js 16.4.0, Rust 1.53 and Tauri 1.0.0-beta.4.
What is Tauri?
Tauri is an Electron replacement for packaging a desktop app using ReactJS frontend as a user interface and an optional Rust backend. You can package an app using only Javascript but also have the option to write more performant functions in Rust. For more information, check out their Github at https://github.com/tauri-apps/tauri.
Init a ReactJS app using npm
First, cd
to parent directory where the root directory of the app will be located, ie: /home/me/src
. Use the following command to generate the react directory structure:
$ npx create-react-app <app-name>
This will create a directory /home/me/src/app-name
with subdirectories node_modules
, public
and src
.
The React app source will be in /home/me/src/app-name/src
.
The package.json
will be in /home/me/src/app-name/package.json
.
Install Tauri CLI as a dev dependency
Next cd
into the app directory /home/me/src/app-name/src
and run the following command:
$ npm install -D @tauri-apps/cli
Edit package.json to add Tauri commands in npm
Open package.json
and look for the scripts
key. From a fresh init using create-react-app it should look like this:
{
...
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
...
}
Add the “tauri” and “bundle” entries so that it looks like this:
{
...
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"tauri": "tauri",
"bundle": "tauri build"
},
...
}
Init a Tauri inside the React app directory structure
While inside the app directory /home/me/src/app-name
, run the following command:
$ npm run tauri init
This will download some things and then will start an interactive prompt with the following questions (you can change these values later):
- “What is your app name?”
The default value will be the value ofname
in yourpackage.json
. Enter a name or press enter for default. - “What should the window title be?”
Default value will be your app name. Enter a name or press enter for default. - “Where are your web assets (HTML/CSS/JS) located, relative to the
<current dir>/src-tauri
folder that will be created?”.
Default value should be../build
.
Enter../build
if this is not the default value. - “What is the url of your dev server?”
Default value will be http://localhost:3000.
Change this to http://0.0.0.0:3000 as recommended in https://github.com/tauri-apps/tauri/issues/1140#issuecomment-848874865.
After answering these questions, it will download a few more things and will install dependencies that are not yet present.
Once finished, a new subdirectory src-tauri
should be present inside the app directory, ie. /home/me/src/app-name/src-tauri
.
[OPTIONAL] How to edit your answers you made above
If you made a mistake or want to change your app name etc. during the previous step, you can change the values by editing the tauri.conf.json
file. It should be located inside the src-tauri
subdirectory at /home/me/src/app-name/src-tauri/tauri.conf.json
.
Edit answers to question number:
- “What is your app name?” value should be in
package > productName
. - “What should the window title be?” value should be in
tauri > windows > title
. - “Where are your web assets (HTML/CSS/JS) located…” should be in
build > distDir
. - “What is the url of your dev server?” should be in
build > devPath
.
Run a development build of your React-Tauri app (Method A)
First, make sure you are not running anything that is using the http://localhost:3000 URL.
Update: I learned there is another (more convenient?) way to run both the create-react-app dev server and the Tauri dev server. Skip to “Method B” for details.
Open a terminal window and cd
to your React app directory, ie. /home/me/src/app-name
.
Run your react app with the command:
$ npm start
This will automatically open a browser window pointing to http://localhost:3000 and should show your app.
If working from a fresh init from create-react-app
, this should show an animated ReactJS logo and the “Edit src/App,js and save to reload” message.
Then, open a different terminal window and cd
to your your React app directory again, ie. /home/me/src/app-name
.
Run a Tauri dev build using the following command:
$ npm run tauri dev
The first time you run npm run tauri dev
, it will take a while as it will download and build all of Tauri’s Rust dependencies. Subsequent builds should run faster as the dependencies are now built and cached.
Once Rust is finished building the dependencies (if running for the first time) and your Rust code, a webview should open displaying your app exactly like what the browser window should previously.
Changes made to the React app will be automatically reflected thanks to the dev server from using create-react-app
. Changes made to the Rust code will trigger a rebuild and will restart the app to reflect the changes.
Run a development build of your React-Tauri app (Method B)
The other way to run a dev build is to let Tauri dev build script run the command to start the create-react-app dev server before making a dev build. In short, you just need a single command to run both React and Rust development builds.
To do this, edit the tauri.conf.json
file located at /home/me/src/app-name/src-tauri/tauri.conf.json
based on my sample directory structure. Look for the build > beforeDevCommand
and add npm start
such that it looks like this:
{
...
"build": {
"distDir": "../build",
"devPath": "http://0.0.0.0:3000",
"beforeDevCommand": "npm start",
"beforeBuildCommand": ""
},
...
}
Then just like above, run a Tauri dev build using the following command:
$ npm run tauri dev
With this change, this should be equivalent to calling npm start
and npm run tauri dev
consecutively. All the features and caveats of running them separately applies to running them through the beforeDevCommand
method.
While it is convenient to start using a single command, the downside is that the React dev server continues on running even after you terminate the Tauri live development build. This means you will have to hunt down the node process running it and manually killing it. Not so convenient after all.
From my experience, it is better to use Method A for making a dev build. This means running the React dev server and the Tauri live build in separate terminal sessions.
Make a release build of your React-Tauri app (Method A)
Update: Just like the dev build shortcut, there is another more convenient way to make a release build for both your React app and the Tauri app with a single command. Read “Method B” below for details.
Open a terminal window and cd
to your React app directory, ie. /home/me/src/app-name
.
Make a release build your ReactJS app using the following command:
$ npm run build
Then, make a release build of your Tauri app. This will include the release build of the React app you just made.
Run the following command:
$ npm run bundle
or
$ npm run tauri build
Make a release build of your React-Tauri app (Method B)
Similar to Method B in making a development build, there is a way that creates a release build of your React app, Tauri and your Rust code with just a single command.
Open the tauri.conf.json
file located at /home/me/src/app-name/src-tauri/tauri.conf.json
. Edit build > beforeBuildCmmmand
and add npm run bundle
such that it looks like this:
{
...
"build": {
"distDir": "../build",
"devPath": "http://0.0.0.0:3000",
"beforeDevCommand": "npm start",
"beforeBuildCommand": "npm run build"
},
...
}
Then, use the following command to create a release build:
$ npm run tauri dev
or
$ npm run tauri build
Just as in the dev build shortcut, this is equivalent to calling npm run build
and then subsequently calling npm run tauri build
. However, unlike the alternate dev build method, there is no downside to this as npm run build
will terminate after it is done building your React app. This one is actually convenient.
Release build files
Making a release build will create a subdirectory /home/me/src/app-name/src-tauri/target/release
, if it wasn’t present before. Otherwise, it will overwrite files that needs to be recompiled.
/home/me/src/app-name/src-tauri/target/release/productName
where productName was your answer in question 1 when initializing Tauri. This can be launched from the command line./home/me/src/app-name/src-tauri/target/release/bundle/macos/productName.app
will be a Mac OS.app
file that you can double-click to launch./home/me/src/app-name/src-tauri/target/release/bundle/macos/productName_…_.dmg
will be a Mac OS.dmg
file that you can double-click to install.
And there you have it.
Play around with the GUI by modifying the React Javascript code in /home/me/src/app-name/src/App.js
or the make your own Javascript callable Rust functions by modifying /home/me/src/app-name/src-tauri/src/main.rs
. Visit https://tauri.studio/en/docs/usage/guides/command for more details on how to make your own Rust commands.
To learn more about the Tauri project, ReactJS and programming in Rust:
- Tauri project website: https://tauri.studio/en/docs/getting-started/intro
- ReactJS: https://reactjs.org/docs/getting-started.html
- Rust: https://www.rust-lang.org/learn
I’m quite excited to play around with this and I hope to share more of what I learn.