What we're building: a Markdown notes app
Step 1 of 13, and it runs nothing. This is the map: what the notes app does once the track is finished, then the twelve steps between here and there. Read it once so every later article has a place to land — each one assumes the state the previous step left behind and adds one piece to the same application.
The app is called rustnote. It is a real desktop product, not a demo: notes live in a local
SQLite database, the interface is a React app rendered by the webview your operating system already
ships, and everything that touches files, the database or the menu bar is Rust. That split is what
Tauri is for, and it is the best way to learn how Rust and JavaScript actually talk to each other —
you write both halves of the conversation.
The finished window
Section titled “The finished window”By step 13 the app looks like this — drawn rather than photographed, because the window is the thing the last step builds:
┌─ rustnote ──────────────────────────────────────────────────────┐│ 3 notes │ # Shopping list │ PREVIEW ││ │ │ ││ Shopping list │ │ Shopping list ││ Talk ideas │ Milk, eggs, coffee. │ Milk, eggs, coffee. ││ rustnote todo │ │ ││ │ - [x] milk │ ☒ milk ││ │ - [ ] coffee │ ☐ coffee ││ │ │ ││ + new note │ │ │└────────────────┴──────────────────────────┴─────────────────────┘A sidebar with every note, a Markdown editor on the left pane of the main area, the rendered preview beside it. Nothing in that window is decoration — every part of it is a step in the track:
- The sidebar and the editor are two processes talking. React owns the window; the notes come from Rust across the IPC boundary. Step 4 builds the first command, steps 5–7 fill it with real data.
- Autosave with no save button. Not every keystroke hits SQLite: the front end debounces, Rust writes. Step 7.
- Search that answers as you type. SQLite’s FTS5 virtual table runs
MATCHqueries and hands back highlighted hits. Step 8 builds the query; step 13 puts the box in the sidebar and makes it live. - Importing a folder of
.mdfiles goes through Tauri’s file-system and dialog plugins — and through a permissions file you will learn to read. Step 9. - Closing to the tray, global shortcuts, native menus. The chrome around the window is written in Rust. Step 10.
- Markdown rendered in Rust, not JavaScript.
pulldown-cmarkproduces the preview and the HTML export from the same source. Step 11. - Signed installers for macOS, Windows and Linux. Step 12, along with the platform traps that come with them.
- Three panes instead of one column. The window above is the last step: a grid, a preview command, and the test harness that finally goes away. Step 13.
That is the whole surface. No cloud sync, no accounts — a fast local app that owns its data and ships as a file you can double-click.
The thirteen steps
Section titled “The thirteen steps”Published in this order. Each step ends at a state the next one picks up from: an app that builds, runs, and does one more thing than the step before.
| # | Article | What lands |
|---|---|---|
| 2 | Creating a Tauri project | create-tauri-app, and the three directories |
| 3 | Understanding the Tauri architecture | front end, IPC, Rust core |
| 4 | Calling Rust from React with commands | #[tauri::command], invoke, and the registration you forget |
| 5 | Persisting data with SQLite | rusqlite, the connection, and migrations |
| 6 | Listing and reading notes | a Vec<Note> across the IPC boundary |
| 7 | Editing notes and autosave | debounce on the front end, update on the back |
| 8 | Full-text search with FTS5 | a virtual table, MATCH, and highlighting |
| 9 | File system and drag-and-drop | the plugins, and the permissions file |
| 10 | System tray, menus, and keyboard shortcuts | native chrome from Rust |
| 11 | Exporting to HTML and PDF | rendering Markdown in Rust, then printing |
| 12 | Building release bundles for macOS, Windows, and Linux | signing, updates, and the platform traps |
| 13 | Turning the panel into the window | three panes, a preview command, and the harness that goes away |
The shape of it: steps 2–3 put a window on screen and explain the three layers you will be working
in. Step 4 wires the two languages together — the first command, the first invoke(). Steps 5–7
are the spine: SQLite, notes as typed data, autosave. Step 8 makes search work. Steps 9–10 open
the native side: files and drag-and-drop through plugins, then tray and menus. Steps 11–12 turn the
app into a product people can install. Step 13 arranges all of it into the window drawn above —
until then each step is exercised through a column of controls, which is all a command needs while
you are still writing commands.
The Rust side of every step is written and tested against Rust 1.99.0 with edition 2024 — what a
current cargo new produces. Steps 2–4 need nothing but Rust and Node on your machine; from step 5
the app carries SQLite with it, and no external database ever appears in this track.
What you need first
Section titled “What you need first”- Rust, installed and current. rustup.rs installs it or brings an old install up to date.
- Node and npm. The front end is a React app, so its toolchain runs on Node. The React side stays deliberately small, but this track does not teach React or TypeScript.
- Structs, enums, and pattern matching — note structs cross the IPC boundary, and serde carries them.
- Option, Result, and error handling — errors have to cross that boundary too, and step 4 shows what that takes.
- Modules, packages, and crates — a Tauri project is two codebases in one directory, and knowing which file you are editing matters from step 2 on.
No Tauri experience assumed — that is what the track is for.
Where the code lives
Section titled “Where the code lives”Steps that change the project commit the result to tauri-desktop-app/ in
the examples repository and tag it — tauri-ch02
after the setup step, and so on. If your build misbehaves partway through, checking out that tag in
your clone puts your tree exactly where the article left it, and a diff against your own files
shows which line drifted. This article commits nothing; the first commit lands with article 2.
Step 2 creates the project for real: create-tauri-app scaffolds the React app, the Rust crate
and the configuration between them, and cargo tauri dev opens a window before you have written a
line of your own code. The track overview keeps the full list
current as articles land.