Quick start
This walkthrough creates an app from the default template, adds a Rust command that the page calls, and builds a package you can hand to someone else. It assumes you have installed the CLI.
-
Create the project.
Terminal window sabine new my-appcd my-appThe name may contain letters, digits, hyphens and underscores. The app’s id becomes
dev.sabine.my-app; change it inSabine.tomlto a reverse domain you own before you ship.The default template is a Vite and TypeScript page with one command. For a fuller starting point,
sabine new my-notes --template notescreates a small notes app instead: a glass window that draws its own titlebar, with a command its page calls through generated bindings. -
Run it.
Terminal window sabine devThe first run prepares Sabine’s runtime, which takes a minute. After that,
sabine devinstalls the page’s packages ifnode_modulesis missing, starts the Vite server on port 5173 (or reuses one already running there), writes the page’s TypeScript bindings, and starts the app withcargo run. The window opens on the development server, so page edits reload as you save them.Development runs use the app id with
.devappended, so they keep their own browser storage and never collide with an installed copy of the app. Right-click the page and choose Open DevTools to inspect it. -
Look around.
src/main.rsbuilds the window and registers the template’s one command:SabineWindow::main(|window| {Ok(window.app().size(960, 640).bridge_typed("app.version", |_request: VersionRequest| {Ok(VersionResponse {version: env!("CARGO_PKG_VERSION"),})}))});ui/src/main.tscalls it throughui/src/bindings.ts, whichsabine devgenerated from that registration. Project structure describes every file. -
Add a command.
Commands take a request type and return a response type. Both derive serde’s traits and
specta::Type, which lets Sabine describe them in TypeScript. Add a greeting command tosrc/main.rs:#[derive(Deserialize, Type)]struct GreetRequest {name: String,}#[derive(Serialize, Type)]struct Greeting {message: String,}fn greet(request: GreetRequest) -> Result<Greeting, BridgeError> {let name = request.name.trim();if name.is_empty() {return Err(BridgeError::new("Tell me your name first"));}Ok(Greeting {message: format!("Hello, {name}!"),})}Register it next to the existing one:
SabineWindow::main(|window| {Ok(window.app().size(960, 640).bridge_typed("app.version", |_request: VersionRequest| {Ok(VersionResponse {version: env!("CARGO_PKG_VERSION"),})}).bridge_typed("app.greet", greet))});Stop
sabine devwith Ctrl+C and start it again. It rebuilds the app and rewritesui/src/bindings.tswith anappGreetfunction before the page loads. -
Call it from the page.
Add a field and a button to
ui/index.html, inside<main>:<input id="name" placeholder="Your name" /><button id="greet" type="button">Greet</button>Then call the command from
ui/src/main.ts:const name = document.querySelector<HTMLInputElement>("#name");document.querySelector("#greet")?.addEventListener("click", async () => {if (!(status instanceof HTMLElement) || !name) return;try {const { message } = await commands.appGreet({ name: name.value });status.textContent = message;} catch (error) {status.textContent = error instanceof Error ? error.message : String(error);}});The function’s parameter and result types come from your Rust structs, so TypeScript catches a misspelled field before the app runs. An
Errfrom the handler rejects the promise with its message. -
Build a package.
Terminal window sabine bundle .This builds the page with
bun run build, compiles the app in release mode, and writes an archive todist/that runs on any machine with the same operating system: a tarball on Linux and macOS, a zip on Windows. On first launch it prepares Sabine like any installed app.Other targets build installers and system packages, such as
deb,rpm,appimage,msi,exeanddmg. See Bundling for each of them.
Next steps
Section titled “Next steps”- Creating windows covers sizes, recipes, glass and the loading screen.
- Commands and Typed bindings cover the bridge in depth, including async handlers, bytes and events.
- Window chrome shows how to draw your own titlebar.
- Releases and updates sets up automatic updates.