Tauri Tutorial: Build Your First Desktop App with Rust & JavaScript (2026)
Video: Tauri Tutorial: Build Your First Desktop App with Rust & JavaScript (2026) by Taught by Celeste AI - AI Coding Coach
A native cross-platform desktop window driven by HTML and Rust. The 2026 way to ship desktop apps without bundling Chromium.
Tauri lets you build desktop applications using web technologies for the UI (HTML, CSS, JavaScript) and Rust for the backend — without shipping a 200MB Electron bundle. Tauri uses the operating system's native webview (WebKit on macOS, WebView2 on Windows, WebKitGTK on Linux), which means a final binary is typically 5–15MB instead of 100+.
This is Lesson 1 of the Tauri series. By the end you will have a working desktop app with React on the front-end and Rust on the back-end.
Prerequisites
Before you start, you need:
- Rust — install from rustup.rs.
- Node.js — version 18 or newer.
- A C compiler — Xcode CLT on macOS, MSVC build tools on Windows, GCC on Linux.
- Platform-specific WebView — already installed on macOS, Edge WebView2 on Windows 11+, WebKitGTK on Linux.
Check your install:
rustc --version
node --version
Scaffold the project
The create-tauri-app CLI scaffolds a fresh project with sensible defaults:
npm create tauri-app@latest
Follow the prompts. For this lesson:
- Project name —
my-first-tauri-app - Language — TypeScript
- Framework — React
- Bundler — Vite (default)
- UI flavor — Tailwind (optional)
The CLI creates a directory with two halves:
my-first-tauri-app/
├── src/ # frontend (React + TypeScript)
├── src-tauri/ # Rust backend
│ ├── Cargo.toml
│ └── src/
│ ├── main.rs
│ └── lib.rs
└── package.json
The frontend is a standard Vite + React project. The backend is a standard Cargo project that uses the tauri crate as its main dependency. Both halves are wired together by Tauri's build system.
The Rust side
src-tauri/src/lib.rs looks something like:
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![/* commands here */])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
tauri::Builder::default() is the entry point. It configures the app with plugins and command handlers, then runs it.
src-tauri/src/main.rs is a thin wrapper:
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
my_first_tauri_app_lib::run()
}
The cfg_attr line hides the console window on Windows release builds. main calls into the lib so Tauri can also build for mobile (which uses a different entry point macro).
The frontend
src/App.tsx is a normal React component:
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
return (
<main>
<h1>Tauri + React</h1>
<button onClick={() => setCount(count + 1)}>
Count is {count}
</button>
</main>
);
}
export default App;
There's nothing Tauri-specific yet — the React side is just web. Tauri shows up when you start invoking Rust functions from the frontend, which we'll do in Lesson 2.
Run the app
npm run tauri dev
Three things happen:
- Cargo compiles the Rust side (slow first time, ~30 seconds; cached after).
- Vite starts the frontend dev server.
- Tauri opens a native window and points its webview at the Vite server.
The window appears with the React app inside. Edit src/App.tsx and Vite hot-reloads — the change appears in the window without a restart. Edit Rust and Cargo recompiles, then Tauri restarts the app automatically.
Build a release binary
npm run tauri build
Cargo builds in release mode, Vite produces production assets, Tauri bundles everything into a native installer for the host OS:
- macOS:
.dmgand.app - Windows:
.msiand.exe - Linux:
.deb,.AppImage, and.rpm
The output lives in src-tauri/target/release/bundle/. Ship the bundle to users.
The release build is 5–15MB depending on platform — much smaller than Electron's typical 100MB.
What Tauri solves
Two recurring desktop-app problems Tauri addresses:
Bundle size. Electron ships an entire Chromium runtime per app. Tauri uses the system webview, so the runtime is shared.
Native access. Web apps can't read files, talk to system processes, or use most native APIs. Tauri bridges that — Rust functions exposed to the frontend can do anything Rust can do.
The trade-off is that you lose Chromium consistency: WebKit on macOS and WebView2 on Windows differ subtly in CSS support, JS APIs, and rendering. Most apps don't notice; complex web apps occasionally do.
What's next
Lesson 2 introduces commands — Rust functions exposed to the frontend that you can call with invoke(). That's where Tauri stops being "React in a window" and starts being a hybrid app.
Common mistakes
Skipping the Rust toolchain install. Tauri builds need Cargo. If cargo --version doesn't work, install Rust first.
Running npm run dev instead of npm run tauri dev. The first runs only the frontend in a browser; the second wraps it in the Tauri window.
Trying to import Node.js modules in the frontend. The Tauri frontend is a web context — no fs, no path, no child_process. Use Tauri commands (Lesson 2) to call into Rust for those.
Forgetting the tauri-plugin-opener. Tauri 2 uses a plugin model for many features. The default scaffold includes opener for opening URLs and files; other plugins for filesystem, dialog, http, etc., are added with cargo add and npm install.
Recap
npm create tauri-app@latest scaffolds a Tauri 2 project with React + TypeScript + Vite. The project has two halves: src/ (frontend) and src-tauri/ (Rust backend). npm run tauri dev runs both with hot-reload. npm run tauri build produces a native installer. Tauri 2 uses a plugin model — pull in tauri-plugin-* crates for native features.
Next lesson: commands — the bridge between Rust and JavaScript.