Elm + Tauri

James Carlson
5 min readOct 6, 2022

Download Scripta Desktop


Want to develop a desktop app with Elm? Use Tauri!

Elm is an amazing language for building web apps: a stable platform with no churn, no worries about runtime errors, and best of all, safe refactoring. Even major refactoring of core program structures is something you can do without fear. You’ve decided that xyz needs to be reworked completely but you are concerned about breaking things. Not to worry! Just go for it — the compiler with its static type system has your back.

What Elm lacks, however, is way to build desktop apps. One solution is to use Electron. But my number one choice is Tauri: it builds small binaries, has a nice workflow, and apps can be compiled for Linux, MacOS (universal binaries), or Windows. Support on the Tauri Discord channel has been excellent.

In this article, the first of a short series, I’ll describe the development process used for Scripta Desktop, a desktop version of the web app scripta.io. (We describe it below). You can find the source code on Github. The latest universal binary for MacOS is available here.

Part 2 will discuss how to use the Tauri API for file system access. Part 3 deals with integration of the app with the Codemirror 6 editor. Part 4 will discuss the dreaded task of codesigning the installer.

What is Scripta Desktop?

If you are interested in what Scripta can do, read this … otherwise skip to the next section.

Scripta Desktop provides a real-time interactive editing environment for MicroLaTeX, a version of LaTeX, as well as a couple of other markup languages. As you type in the editor (left window in image below), the source text is instantly rendered into HTML (right window).

MicroLaTeX document in Scripta Desktop

The Scripta Desktop app requires no internet access, so you can work on a plane, on a mountaintop, or in a cave. Scripta documents are saved automatically and stored in ~/Desktop/scripta; the app will export them to standard LaTeX or PDF if you need these.

Setting things up

Below is an outline of the setup procedure on MacOS for turning an existing Elm web app into a desktop app using tauri.app. As an example, we use the counter app from the Elm guide. The code for the Tauri desktop version is on Github. In Part 2, we use the same procedure to make Scripta Desktop. The new elements in Part 2 will be (a) adding fonts, CSS styles, and Javascript code from KaTeX.org to render mathematical equations; (b) Using the Tauri fsAPI to access the host computer’s file system so as to open and save files.

For more info and for the procedure for other operating systems, see the Tauri docs.

Procedure (MacOS)

  • Make sure xcode tools are installed: run xcode-select --install
  • Install Rust:
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
  • Install the Tauri toolchain: cargo install tauri-cli.
  • Set up the Elm + Tauri project. Make a new directlry, say counter, and cd into it. Then run cargo tauri init. This command will lead you through a series of questions and make a skeleton Tauri project for you.
$ cargo tauri init
✔ What is your app name? · Counter
✔ What should the window title be? · Counter
✔ Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri/tauri.conf.json" file that will be created? · ../src
✔ What is the url of your dev server? · http://localhost:4000
✔ What is your frontend dev command? · sh dev.sh
✔ What is your frontend build command? · sh build.sh

My dev.sh command is a one-liner:

elm make src/Main.elm --output=public/main.js

The build.sh script is the same, but with the --optimize flag. Usenpm install serve to install the dev server.

The directory in which you issued the cargo tauri init command will now look like the below.

└── src-tauri
├── Cargo.toml
├── build.rs
├── icons
│ ├── 128x128.png
│ ...
│ └── icon.png
├── src
│ └── main.rs
└── tauri.conf.json

The tauri.conf.json file is the one that you will mostly work with. For this project, it starts out like this:

{ "build": {
"beforeBuildCommand": "sh build.sh",
"beforeDevCommand": "sh dev.sh",
"devPath": "http://localhost:4000",
"distDir": "../public",
"withGlobalTauri": true

The "withGlobalTauri": true line is needed because I’m not using a bundler (more on this in Part 2, using the Tauri file system API). You will need to change the "identifier" field from its default value, e.g., to "identifer": io.counter-tauri. I also set the"height" and "width" in the "windows" section of tauri.conf.json.

Your files

Make two new directories, src and public. They are siblings of src-tauri. Put your Elm code in src and put your index.html file in public. In this example, the only resident of src is Main.elm. Also put whatever assets your app needs (Javascript, CSS, font folders, etc) in public. Below is the publicfolder. The index.html is the usual one for an Elm app for which Main.elm is compiled tomain.js .

# folder: public
<.js and .css files, font folders, etc>


Once things are set up, run npx server public -l 4000 and then cargo tauri dev. This will (1) bring your Elm app up on localhost:4000, and (2) bring up a native version your app. It should look like this:

Tauri version of the counter app.
Scripta version of the Elm counter app

Notes: (1) the app running on localhost won’t have access to features that require the Tauri API’s, e.g., access to the file system. (2) For live reload of the Elm app, I use npx elm-watch hot. See this link.

To debug the native app, right-click on it and choose inspect. This will bring up your dev tools (console, network, etc.)


Use cargo tauri build. On MacOS, if you want a universal binary, say cargo tauri build --target universal-apple-darwin. You may get an error message telling you to install some Rust code. Go for it!

After you issue the build command, Tauri while work for a little while, after which you will be presented with a window with the new app. Move it into the Applications folder.

For distribution (without codesigning), you need a .zip file. For the moment, I do something like the below for Scripta Desktop.

# zip.sh
cp -r /Applications/scripta-desktop.app .
zip -r scripta-desktop.zip scripta-desktop.app
rm -r scripta-desktop.app

The resulting scripta-desktop.zip can be distributed for users to expand to scripta-desktop.app.


Part 2: Using the Tauri APIs. I’ll discuss how to open and save files using ports (Elm) and the Tauri APIs .

Part 3: Integration with the Codemirror 6 editor.

Part 4: Codesigning the installer


Many thanks to FabianLars on the Tauri dev team for help getting started with the fs API.