C.W.K.
Stream
Lesson 02 of 07 · published

Your First #[tauri::command]

~13 min · tauri, command, rust, ipc

Level 0Web Tourist
0 XP0/56 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"A command is just a Rust function with a sticker on it — and a name on a list. Miss the list and it vanishes."

Two Steps, Always Both

Exposing native power to your frontend is two steps, and forgetting the second is the most common beginner bug:

  1. Mark the function with the #[tauri::command] attribute. This tells Tauri's macro machinery to generate the IPC glue for it.
  2. Register it in tauri::generate_handler![] inside your Builder. This is what actually puts the command in the router the webview talks to.

Do step 1 but not step 2 and you'll get a runtime error like 'command greet not found' — the function exists, but nothing wired it into the bridge. Whenever a command 'isn't found,' check the handler list first.

What a Command Can Take and Return

Command parameters can be plain types (String, numbers, booleans), your serde structs, or special Tauri types you'll meet later (State, AppHandle, Window). The return type is whatever serializes — a value for an infallible command, or Result<T, E> for one that can fail. Tauri injects the special types automatically; you only pass the data arguments from the frontend.

The Classic First Command

Every Tauri project starts with a greet command, and it's worth typing once to feel the whole loop: a function in Rust, a sticker, a name on the list, and an invoke from the web side (next lesson). Once you've felt one command travel the bridge, every other command is the same shape with different cargo.

Code

The full two-step wiring·rust
// src-tauri/src/lib.rs

// Step 1: the sticker. This function is now an IPC command.
#[tauri::command]
fn greet(name: String) -> String {
    format!("Hello, {name}! Welcome to native.")
}

pub fn run() {
    tauri::Builder::default()
        // Step 2: the list. Without this line, `greet` is 'not found'.
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error while running app");
}
Registering several commands·rust
// Multiple commands: list them all in the same macro.
#[tauri::command]
fn add(a: i64, b: i64) -> i64 { a + b }

#[tauri::command]
fn shout(text: String) -> String { text.to_uppercase() }

// ...
.invoke_handler(tauri::generate_handler![greet, add, shout])

External links

Exercise

In your scaffolded app, add a command called add that takes two numbers and returns their sum. Register it in generate_handler![]. Don't call it from the frontend yet — just make tauri dev compile with the command present. You're proving you can wire a brand-new command end to end on the Rust side.
Hint
Signature: #[tauri::command] fn add(a: i64, b: i64) -> i64 { a + b }. Then add add to the generate_handler![] list next to greet. If it compiles, step 1 and step 2 are both done.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.