Building a Powerful Note App with Tauri, React, and TailwindCSS

Building a Powerful Note App with Tauri, React, and TailwindCSS
Photo by Patrick Perkins / Unsplash

About the Tech Stack!

Tauri is a framework for building cross-platform desktop applications using web technologies such as HTML, CSS, and JavaScript. It provides a bridge between the web frontend and the native backend, allowing developers to create high-performance and native-like desktop applications.

React is a popular JavaScript library for building user interfaces. It is often used in combination with Tauri to create desktop applications. Some benefits of using Tauri and React for desktop apps include:

  • Code Reusability: With React, you can reuse components across different platforms, including web and desktop.
  • Developer Efficiency: React's component-based architecture and declarative syntax make it easier to develop and maintain complex desktop applications.
  • Performance: Tauri leverages web technologies while providing native-like performance, making it suitable for building high-performance desktop apps.
  • Cross-Platform Compatibility: Tauri and React allow you to build applications that run on multiple operating systems, including Windows, macOS, and Linux.
  • Ecosystem and Community: React has a large and active community, providing a wealth of resources, libraries, and tools. Tauri also has an active community and provides comprehensive documentation and support.

Using Tauri and React together provides a powerful and efficient way to develop desktop applications with modern web technologies.

About this Tutorial!

Creating a desktop note app with Tauri, React, Tailwind CSS, and a flat-file database is a multi-step process. I'll provide an outline and code snippets to guide you through building such an application.

Set Up the Project

Create a new directory for your project:

mkdir desktop-note-app
cd desktop-note-app

Initialize a new Tauri project:

npx tauri init

Create a React app inside the Tauri project:

npx create-react-app react-app

Install the required dependencies:

npm install --save react-query tailwindcss

Create the Flat-File Database

In this example, we'll use the fs module to create and manage a flat-file database for storing notes.

Create a directory for your database files:

mkdir data

Create a JavaScript module (database.js) to handle database operations:

// database.js
const fs = require('fs');
const path = require('path');

const dbPath = path.join(__dirname, 'data', 'notes.json');

function readNotes() {
  try {
    const data = fs.readFileSync(dbPath, 'utf-8');
    return JSON.parse(data);
  } catch (error) {
    return [];
  }
}

function writeNotes(notes) {
  try {
    fs.writeFileSync(dbPath, JSON.stringify(notes, null, 2));
  } catch (error) {
    console.error('Error writing to database:', error);
  }
}

module.exports = { readNotes, writeNotes };

Create the React Note App

Update the src/App.js file with your React application code. Here's a simplified example:

import React, { useState } from 'react';
import { useQuery, useMutation } from 'react-query';

// Import other components and styles as needed

function App() {
  const { data: notes, isLoading } = useQuery('notes', fetchNotes);
  const [createNote] = useMutation(saveNote);

  const [newNote, setNewNote] = useState('');

  const handleNoteChange = (event) => {
    setNewNote(event.target.value);
  };

  const handleSaveNote = () => {
    createNote({ content: newNote });
    setNewNote('');
  };

  return (
    <div className="p-4">
      <h1 className="text-2xl font-bold mb-4">Notes</h1>
      <div className="mb-4">
        <textarea
          className="w-full border rounded p-2"
          placeholder="Enter a new note..."
          value={newNote}
          onChange={handleNoteChange}
        ></textarea>
        <button
          className="mt-2 bg-blue-500 text-white px-4 py-2 rounded"
          onClick={handleSaveNote}
        >
          Save Note
        </button>
      </div>
      {isLoading ? (
        <p>Loading...</p>
      ) : (
        <ul>
          {notes.map((note) => (
            <li key={note.id}>{note.content}</li>
          ))}
        </ul>
      )}
    </div>
  );
}

export default App;

Implement functions to fetch and save notes using the flat-file database (database.js) and integrate them into your React component.


Add, Edit, and Delete your Note

To add the functionality to add, edit, and delete notes in your desktop note app built with Tauri, React, Tailwind CSS, and a flat-file database, you can follow these steps:

Update the React Note App

Update your src/App.js file to include functions for adding, editing, and deleting notes:

import React, { useState } from 'react';
import { useQuery, useMutation } from 'react-query';
import { v4 as uuidv4 } from 'uuid'; // Add this import for generating unique IDs

// Import other components and styles as needed

function App() {
  const { data: notes, isLoading } = useQuery('notes', fetchNotes);
  const [createNote] = useMutation(saveNote);
  const [updateNote] = useMutation(editNote);
  const [deleteNote] = useMutation(removeNote);

  const [newNote, setNewNote] = useState('');
  const [editingNote, setEditingNote] = useState(null);

  const handleNoteChange = (event) => {
    setNewNote(event.target.value);
  };

  const handleEditNote = (note) => {
    setEditingNote(note);
    setNewNote(note.content);
  };

  const handleSaveNote = () => {
    if (editingNote) {
      updateNote({ id: editingNote.id, content: newNote });
      setEditingNote(null);
    } else {
      createNote({ id: uuidv4(), content: newNote }); // Generate a unique ID
    }
    setNewNote('');
  };

  const handleDeleteNote = (id) => {
    deleteNote(id);
  };

  return (
    <div className="p-4">
      <h1 className="text-2xl font-bold mb-4">Notes</h1>
      <div className="mb-4">
        <textarea
          className="w-full border rounded p-2"
          placeholder="Enter a new note..."
          value={newNote}
          onChange={handleNoteChange}
        ></textarea>
        <button
          className="mt-2 bg-blue-500 text-white px-4 py-2 rounded"
          onClick={handleSaveNote}
        >
          {editingNote ? 'Update Note' : 'Save Note'}
        </button>
      </div>
      {isLoading ? (
        <p>Loading...</p>
      ) : (
        <ul>
          {notes.map((note) => (
            <li key={note.id}>
              {note.content}
              <button
                className="ml-2 bg-red-500 text-white px-2 py-1 rounded"
                onClick={() => handleDeleteNote(note.id)}
              >
                Delete
              </button>
              <button
                className="ml-2 bg-yellow-500 text-white px-2 py-1 rounded"
                onClick={() => handleEditNote(note)}
              >
                Edit
              </button>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

export default App;

Update Database Functions

Update the functions in your database.js module to handle editing and deleting notes:

// database.js
// ... (previous code)

function editNoteById(id, newContent) {
  const notes = readNotes();
  const index = notes.findIndex((note) => note.id === id);
  if (index !== -1) {
    notes[index].content = newContent;
    writeNotes(notes);
  }
}

function removeNoteById(id) {
  const notes = readNotes();
  const updatedNotes = notes.filter((note) => note.id !== id);
  writeNotes(updatedNotes);
}

module.exports = { readNotes, writeNotes, editNoteById, removeNoteById };

Build the Tauri Application for macOS

To build your Tauri application for macOS, run the following command:

npx tauri build --target osx --format dmg

This command will generate a macOS application package (.dmg file) in the target/release/bundle directory. You can distribute this package to macOS users.

Now, you have a desktop note app that allows you to add, edit, and delete notes. When you run the app on macOS, it should function as expected, utilizing the Tauri framework for the desktop environment.

Now, lets add a search function that allows you to search all notes in your desktop note app built with Tauri, React, Tailwind CSS, and a flat-file database, you can follow these steps:

Update the React Note App

Update your src/App.js file to include the search functionality:

import React, { useState } from 'react';
import { useQuery, useMutation } from 'react-query';
import { v4 as uuidv4 } from 'uuid';

// Import other components and styles as needed

function App() {
  const { data: notes, isLoading } = useQuery('notes', fetchNotes);
  const [createNote] = useMutation(saveNote);
  const [updateNote] = useMutation(editNote);
  const [deleteNote] = useMutation(removeNote);

  const [newNote, setNewNote] = useState('');
  const [editingNote, setEditingNote] = useState(null);
  const [searchTerm, setSearchTerm] = useState('');

  const filteredNotes = notes.filter((note) =>
    note.content.toLowerCase().includes(searchTerm.toLowerCase())
  );

  const handleNoteChange = (event) => {
    setNewNote(event.target.value);
  };

  const handleEditNote = (note) => {
    setEditingNote(note);
    setNewNote(note.content);
  };

  const handleSaveNote = () => {
    if (editingNote) {
      updateNote({ id: editingNote.id, content: newNote });
      setEditingNote(null);
    } else {
      createNote({ id: uuidv4(), content: newNote });
    }
    setNewNote('');
  };

  const handleDeleteNote = (id) => {
    deleteNote(id);
  };

  const handleSearch = (event) => {
    setSearchTerm(event.target.value);
  };

  return (
    <div className="p-4">
      <h1 className="text-2xl font-bold mb-4">Notes</h1>
      <div className="mb-4">
        <input
          type="text"
          className="w-full border rounded p-2"
          placeholder="Search notes..."
          value={searchTerm}
          onChange={handleSearch}
        />
      </div>
      <div className="mb-4">
        <textarea
          className="w-full border rounded p-2"
          placeholder="Enter a new note..."
          value={newNote}
          onChange={handleNoteChange}
        ></textarea>
        <button
          className="mt-2 bg-blue-500 text-white px-4 py-2 rounded"
          onClick={handleSaveNote}
        >
          {editingNote ? 'Update Note' : 'Save Note'}
        </button>
      </div>
      {isLoading ? (
        <p>Loading...</p>
      ) : (
        <ul>
          {filteredNotes.map((note) => (
            <li key={note.id}>
              {note.content}
              <button
                className="ml-2 bg-red-500 text-white px-2 py-1 rounded"
                onClick={() => handleDeleteNote(note.id)}
              >
                Delete
              </button>
              <button
                className="ml-2 bg-yellow-500 text-white px-2 py-1 rounded"
                onClick={() => handleEditNote(note)}
              >
                Edit
              </button>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

export default App;

Add Search Function to Filter Notes

In your App.js file, you've added a searchTerm state variable to store the user's search query. The filteredNotes variable filters the notes based on the search term, displaying only the matching notes.

Update the Tauri Application

There is no need to make changes to the Tauri application for this specific feature since the search functionality is implemented entirely on the client side.


Build the Tauri Application

  1. Update the Tauri configuration (tauri.conf.js) to include the necessary settings for your app, including window size, title, and menu.
  2. Create a Tauri shell menu (tauri.conf.json) for your app.
  3. Add Tauri APIs to access the file system and interact with the database in your React component.
  4. Build the Tauri application:
npx tauri build

Run the Application

Run your Tauri application:

npx tauri dev

You can customize the app design, by adding more functionalities and options to it.

Read more