Warning
Tauri Specta v2 is still in beta, until Tauri v2 lands as stable.
However, it's safe to use as long as you lock your versions.
cargo add tauri@=2.0.0-beta.17
cargo add specta@=2.0.0-rc.12
cargo add tauri-specta@=2.0.0-rc.5 --features javascript,typescript
use specta::Type;
use serde::{Deserialize, Serialize};
// The `specta::Type` macro allows us to understand your types
// We implement `specta::Type` on primitive types for you.
// If you want to use a type from an external crate you may need to enable the feature on Specta.
#[derive(Serialize, Type)]
pub struct MyCustomReturnType {
pub some_field: String,
}
#[derive(Deserialize, Type)]
pub struct MyCustomArgumentType {
pub foo: String,
pub bar: i32,
}
#[tauri::command]
#[specta::specta] // <-- This bit here
fn greet3() -> MyCustomReturnType {
MyCustomReturnType {
some_field: "Hello World".into(),
}
}
#[tauri::command]
#[specta::specta] // <-- This bit here
fn greet(name: String) -> String {
format!("Hello {name}!")
}
use specta::collect_types;
use tauri_specta::{ts, js};
// this example exports your types on startup when in debug mode. You can do whatever.
fn main() {
let invoke_handler = {
// You can use `tauri_specta::js::builder` for exporting JS Doc instead of Typescript!`
let builder = tauri_specta::ts::builder()
.commands(tauri_specta::collect_commands![greet, greet2, greet3 ]); // <- Each of your commands
#[cfg(debug_assertions)] // <- Only export on non-release builds
let builder = builder.path("../src/bindings.ts");
builder.build().unwrap()
};
tauri::Builder::default()
.invoke_handler(invoke_handler)
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
import * as commands from "./bindings"; // This should point to the file we export from Rust
await commands.greet("Brendan");
You must reconfigure your builder to support events:
fn main() {
- let invoke_handler = {
+ let (invoke_handler, register_events) = {
// You can use `tauri_specta::js::builder` for exporting JS Doc instead of Typescript!`
let builder = tauri_specta::ts::builder()
.commands(tauri_specta::collect_commands![greet, greet2, greet3 ]) // <- Each of your commands
+ .events(tauri_specta::collect_events![]); // This should contain all your events.
#[cfg(debug_assertions)] // <- Only export on non-release builds
let builder = builder.path("../src/bindings.ts");
builder.build().unwrap()
};
tauri::Builder::default()
.invoke_handler(invoke_handler)
+ .setup(|app| {
+ register_events(app);
+ })
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Now you can add your first event. You can add as many of these as your want.
#[derive(Debug, Clone, Serialize, Deserialize, specta::Type, tauri_specta::Event)]
pub struct DemoEvent(String);
and make sure you register the event with Tauri Specta by adding it to the collect_events
macro like the following:
.events(tauri_specta::collect_events![DemoEvent]); // This should contain all your events, comma separated.
Finally, you can setup a listener or emit a message from Rust like the following:
tauri::Builder::default()
.invoke_handler(invoke_handler)
.setup(|app| {
let handle = app.handle();
DemoEvent::listen_global(&handle, |event| {
dbg!(event.payload);
});
DemoEvent("Test".to_string()).emit_all(&handle).unwrap();
})
...
Most methods take a handle
which can be any of the following types:
The Event
trait defines all methods that can be used to emit or listen so refer to it.
and it can be used in TS like the following:
import { commands, events } from "./bindings";
import { appWindow } from "@tauri-apps/api/window";
// For all windows
events.demoEvent.listen((e) => console.log(e));
// For a single window
events.demoEvent(appWindow).listen((e) => console.log(e));
// Emit to the backend and all windows
await events.demoEvent.emit("Test")
// Emit to a window
await events.demoEvent(appWindow).emit("Test")
Sometimes you might want to export a type to Typescript but it doesn't actually show up in any of our commands.
You can do that like the following:
let builder = ts::builder()
// < your commands and events are probally here
.types(TypeCollection::default().register::<Custom>()); // < call `register` as much as you want.
register
only supports named types as otherwise you would run into the following problem:
// vvv - What would this be without a name?
export ... = {};
Any type implemented using the Type
derive macro will meet this requirement.