Developing a desktop note-taking app with Tauri (React + Rust)

Hello friends!

In this tutorial, we will develop a desktop application using Tauri. Tauri is a framework for creating desktop applications, similar to Electronbut allowing to use Rust instead of Node.jsfor example, to interact with the file system.

As a framework for developing a user interface, I will use React and typescriptand to work with dependencies for JavaScriptYarn.

Served as a source of inspiration for me this wonderful article.

note: I will develop an application under Windows x64other operating systems (and architectures) will have slightly different implementation details.

The application will be a kind of one-line terminal for writing notes to a file tasks.txtlocated in the home directory (for Windows – this is C:\Users\[User]). The app will launch with a keyboard shortcut Ctrl + Shift + Q and end when pressed Esc.

Here’s what it will look like:

Repository with project code.

If you are interested, please under cat.

Preparing and setting up a project

note: must be installed on your machine Node.js and Rust. About installation Rust you can read here and here.

If you use as a code editor VSCodeI recommend installing this set of extensions to work with Rust.

Windows

  • After installation Rust execute the command rustup default stable-msvc in terminal to switch to toolbox MSVC.

Create a new project Tauri:

yarn create tauri-app

  • Enter the name of the application, for example, Tauri Focus;
  • choose a tool for creating a frontend template create-vite;
  • add a package @tauri-apps/api;
  • choose a template react-ts.

note: in Windows command prompt must be run as administrator.

Go to the created directory tauri-focus and bring it to the following form:

Ignore the files for now postcss.config.js and tailwind.config.jsyou will have them soon.

Execute the command yarn tauri dev to run the application in development mode. note: at first start Rust you will need to compile the application files, so you will have to wait a bit (on subsequent launches, the cache will be used).

This completes the preparation and configuration of the project. Let’s move on to the development of the user interface.

User interface

To style the application, we will use TailwindCSS. Install the necessary packages, being in the root directory of the project:

yarn add -D tailwindcss postcss autoprefixer

Initialize Tailwind:

yarn tailwindcss init -p

Add to tailwind.config.js next line:

module.exports = {
  // если хотите, можете оставить здесь только `tsx`
  content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
  // ...
}

Import styles to src/index.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

Editing the file src/App.tsx:

import React, { useState } from 'react'

function App() {
  const [text, setText] = useState('')

  const addTask = (e: React.KeyboardEvent) => {
    if (e.key === 'Enter') {
      console.log(text)
    }
  }

  return (
    <input
      type="text"
      className="w-[600px] h-[60px] px-4 bg-gray-800 text-2xl text-green-600 rounded-sm"
      value={text}
      onChange={(e) => setText(e.target.value)}
      onKeyDown={addTask}
    />
  )
}

export default App

We have a (dark) field of size 600x60px to enter (green) note text. When you press Enter the value of this field is output to the console.

Next, we need a function to write a note to a file.

Interaction with the file system

Define a team Tauri/function to write a note in a file src-tauri/src/main.rs:

// импорт зависимостей
use std::fs::OpenOptions;
use std::io::Write;

#[tauri::command]
fn add_task(text: String) {
  let mut file = OpenOptions::new()
    .create(true)
    .append(true)
    .open("../tasks.txt")
    .expect("Ошибка при открытии файла");

  writeln!(file, "{}", text).expect("Ошибка при записи файла");
}

Add this function to the list of commands:

fn main() {
  let context = tauri::generate_context!();
  tauri::Builder::default()
    // !
    .invoke_handler(tauri::generate_handler![add_task])
    .menu(tauri::Menu::os_default(&context.package_info().name))
    .run(context)
    .expect("Ошибка при запуске приложения");
}

Editing the handler addTask in src/App.tsx (вызов Rust from the frontend):

import { invoke, process } from '@tauri-apps/api'

// ...
const addTask = async (e: React.KeyboardEvent) => {
  switch (e.key) {
    // при нажатии `Enter` вызываем `add_task` с текстом заметки
    case 'Enter':
      try {
        await invoke('add_task', { text })
        setText('')
      } catch (e) {
        console.error(e)
      }
      break
    // при нажатии `Esc` завершаем процесс
    case 'Escape':
      return process.exit()
    default:
      return
  }
}
// ...

Application health check and finishing touches

Before running the application, you need to edit the file a little tauri.conf.json:

"tauri": {
  "bundle": {
    "identifier": "app.tauri.focus",
  },
  "windows": [
    {
      "fullscreen": false,
      "resizable": false,
      "center": true,
      "width": 600,
      "height": 60,
      "title": "Tauri Focus App",
      "decorations": false
    }
  ]
}

decorations: false means hiding the header, the rest, I think, is clear.

  • Execute the command yarn tauri dev (notethat the frontend can be debugged separately at http://localhost:3000);
  • enter some test values, for example, test, test2, test3;
  • we see that a file has appeared in the root of the project tasks.txt with our notes.

If you are confused by the error on this line:

let context = tauri::generate_context!();

Just create a frontend build with the command yarn build.

Excellent. The app is working as expected. However, the project root is not a very good place to store the file. tasks.txt. Also, we haven’t generated the setup file yet.

For universal access to the home directory, you need the package home (“crate” in terminology Rust). Add to file src-tauri/src/Cargo.toml this line:

[dependencies]
# ...
home = "0.5.3"

Importing the package into main.rs and define a variable for the path to the home directory:

// ...
use home::home_dir;

fn add_task(text: String) {
  // !
  let mut path = home_dir()
    .expect("Ошибка доступа к домашней директории");
  // добавляем в путь название файла для заметок
  path.push("tasks.txt");

  let mut file = OpenOptions::new()
    .create(true)
    .append(true)
    // !
    .open(path)
    .expect("Ошибка при открытии файла");

  writeln!(file, "{text}").expect("Ошибка при записи файла");
}

Execute the command yarn tauri build:

This will generate the setup file src-tauri/target/release/bundle/msi/tauri-focus_0.1.0_x64_en-US.msi.

Also in the directory src-tauri/target/release executable is generated tauri-focus.exe.

  • Create a shortcut for this file on the desktop;
  • open the properties of the shortcut;
  • in the “Shortcut” field, enter a keyboard shortcut to launch the application, for example, Ctrl + Shift + Q;
  • apply the changes.

Click Ctrl + Shift + Q and add a couple of notes:

pressing Esc terminates the application.

Perhaps this is all I wanted to share with you in this article. I hope you found something interesting for yourself and did not waste your time.

Thank you for your attention and happy coding!


Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *