Building a notebook using Tauri and ReactJS
Tags: react, tauri, typescript, desktopapp
Published: 2022-09-08 15:12:13

Hi friends! I'm hudy.
A web developer from Hanoi, Vietnam

In this post, I'm gonna show you how to build a Notebook app - desktop application - using Tauri and ReactJS. 

For those who don't know Tauri, it is a toolkit that helps developers make applications for the major desktop platforms - using virtually any frontend framework in existence

A tutorial video:

{% embed https://www.youtube.com/embed/2E5edr4_LXw %}

The source code for this tutorial is available here: [https://github.com/hudy9x/tauri-notebook](https://github.com/hudy9x/tauri-notebook.git)

## Prerequisite

1. [Tauri](https://tauri.app/v1/guides/getting-started/prerequisites) - a toolkit to build desktop app
2. [Nodejs](https://nodejs.org/en/)
3. npm or yarn - package manager

## Table of contents

1. Install Tauri
2. Scaffolding code structure using `create-tauri-app`
3. Using Tiptap library in `<MainEditor/>` and create `<Menubar/>`
4. Time to style
5. Change app's icon
6. Build app

## #1. Install Tauri
The first step is to intall Rust and it's dependencies. Visit Tauri's home page to get it done

- For window users: check this [link](https://tauri.app/v1/guides/getting-
started/prerequisites#setting-up-windows)
- For macOs users: check this [link](https://tauri.app/v1/guides/getting-started/prerequisites#setting-up-macos)
- For Linux users: check this [link](https://tauri.app/v1/guides/getting-started/prerequisites#setting-up-linux)


## #2. Scaffolding code structure using `create-tauri-app`

Make sure that you've installed `npm` already. Then, run the following command to generate code structure

```shell
$ npm create tauri-app
```
Follow the instructions. Just remember to select `react-ts` as main framework

![Select framework](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/63t4kwsu4vdphoyf4roh.png)

Now, time to install `tiptap` and `remixicon`
```shell
$ npm install @tiptap/react @tiptap/starter-kit remixicon
```
Next, create a `components` folder with 2 files inside it as `Editor.tsx` and `Menubar.tsx`. And add a simple component definition to them

![Create Editor and Menubar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mt7dsgrs5aqwt2zd7rgv.png)

## #3. Using Tiptap library in `<MainEditor/>` and create `<Menubar/>`

Open `src/components/MainEditor.tsx` and add the following code:

```javascript
// src/components/MainEditor.tsx
import { EditorContent, useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Menubar from "./Menubar";

export default function MainEditor() {
  const editor = useEditor({
    extensions: [StarterKit],
    content: `<h2>Hello world</h2>`
  })
  return <>
    {editor ? <Menubar editor={editor} /> : null}
    <EditorContent editor={editor} />
  </>
}
```
Next, open `src/components/Menubar.tsx`
```javascript
// src/components/Menubar.tsx
import {Editor} from '@tiptap/react'

interface IMenubarProp {
  editor: Editor
}

export default function Menubar({editor}: IMenubarProp) {
  const getFocus = () => editor.chain().focus()
  const isActive = (type: string, options?: any) => {
    return editor.isActive(type, options ?? {}) ? 'is-active' : ''
  }

  const menus = [
    [
      {icon: 'bold', onClick: () => getFocus().toggleBold().run(), isActive: isActive('bold')},
      {icon: 'italic', onClick: () => getFocus().toggleItalic().run(), isActive: isActive('italic')},
      {icon: 'strikethrough', onClick: () => getFocus().toggleStrike().run(), isActive: isActive('strike')},
      {icon: 'code-line', onClick: () => getFocus().toggleCode().run(), isActive: isActive('code')},
    ],
    // ...
  ]
    
  return <div className="menu">
    {menus.map(group => {
      return <div className="group-item">
        {group.map(item => {
          return  <button className="menu-item" onClick={item.onClick}>
        <i className={`ri-${item.icon} ${item.isActive}`}></i>
      </button>

        })}
      </div>
          })}
  </div>
}
```

After that, just import `<MainEditor/>` to `App.tsx`
```javascript
// src/App.tsx
import MainEditor from "./components/MainEditor";
import './App.css'
export default function App() {
  return <MainEditor />
}
``` 

Run app to check the result

```shell
$ npm run tauri dev
```

![Run first time](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d8bubaylb79bo31r96uk.png)

## #4. Time to style

Alright, everything work well so far. But we still have no style for the app.

Let's add style to `src/App.css` file

```css
.menu {
  display: flex;
  gap: 0.5rem;
  padding: 0 1rem;
  position: fixed;
  z-index: 10;
}

.group-item {
  display: flex;
  gap: 0.5rem;
}

.menu-item {
  padding: 0.5rem 0.75rem
}

.menu-item .is-active {
  color: #55db55;
}

// Editor css

.ProseMirror {
  padding: 1rem;
  outline: none;
  padding-top: 2.5rem;
}

.ProseMirror >
 * + * {
	 margin-top: 0.75em;
}
 .ProseMirror ul, .ProseMirror ol {
	 padding: 0 1rem;
}
 .ProseMirror h1, .ProseMirror h2, .ProseMirror h3, .ProseMirror h4, .ProseMirror h5, .ProseMirror h6 {
	 line-height: 1.1;
}
 .ProseMirror code {
	 background-color: rgba(97, 97, 97, 0.1);
	 color: #616161;
}
 .ProseMirror pre {
	 background: #0d0d0d;
	 color: #fff;
	 font-family: 'JetBrainsMono', monospace;
	 padding: 0.75rem 1rem;
	 border-radius: 0.5rem;
}
 .ProseMirror pre code {
	 color: inherit;
	 padding: 0;
	 background: none;
	 font-size: 0.8rem;
}
 .ProseMirror img {
	 max-width: 100%;
	 height: auto;
}
 .ProseMirror blockquote {
	 padding-left: 1rem;
	 border-left: 2px solid rgba(13, 13, 13, 0.1);
}
 .ProseMirror hr {
	 border: none;
	 border-top: 2px solid rgba(13, 13, 13, 0.1);
	 margin: 2rem 0;
}

// Scrollbar css
::-webkit-scrollbar {
  width: 14px;
  height: 14px;
}

::-webkit-scrollbar-button {
  display: none;
  width: 0;
  height: 0;
}

::-webkit-scrollbar-corner {
  background-color: transparent;
}

::-webkit-scrollbar-thumb {
  border: 4px solid rgba(0, 0, 0, 0);
  background-clip: padding-box;
  border-radius: 8px;
  background-color: rgb(182, 182, 182);
}

::-webkit-scrollbar-track {
  border: 4px solid transparent;
  background-clip: padding-box;
  border-radius: 8px;
  background-color: transparent;
}
```

Looks good hah ? 😁

![After add style](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zwnec2prvikrcz0cec4e.png)

## #5. Change app's icon (optional)
By default, Tauri has a default icon. If you want to change it to another using my tool [here](https://github.com/hudy9x/tauri-icon-converter)

Prepare an icon in .PNG format that has size of 1024px x 1024px. And it's name must be 1024x1024. Now clone the tool and install packages

```shell
$ git clone https://github.com/hudy9x/tauri-icon-converter
$ cd tauri-icon-converter && npm install
```
And then, copy the icon to `/tauri-icon-converter` folder and run the below command

```shell
$ node .
```

After genereting process finished, you guys can see a list of icon in `/tauri-icon-converter/outputs`

![List of icon](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xko1dvwj0s0t4g8whxll.png)

Just replace all of them, but `.keep` file to `/src-tauri/icons` folder

You could also verify by running `npm run tauri dev`. If the icon showed as below then everything work well

![App icon after replacing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x91qjmn26oq11w5f07dz.png)

## #6. Build app 
Time to package our app to .msi file. Open terminal and run the following

```shell
$ npm run tauri build
```
If you guys got an error like below

![Error when building app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/suo249h0twwygeu32eip.png)

That's because your `indentifier` not unique. Just open `src-tauri/tauri.conf.json` and change value to a new one. I changed mine to `com.notebook.dev`

![Update indentifier value](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uwx8x5jkyvoda5mszz55.png)

Ok, run the command `npm run tauri dev` again. And see the result

![Build success](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c1e5x281ymbhzfn4vlt9.png)

After building process finished you can see the output path like above

## Conclusion

So far, you've learnt how to build a desktop application using Tauri and Reactjs. 

You can also improve this app by providing some personal feature like auto update, sign-in, theme, ...etc

Thank you for reading!